diff --git a/DEPS b/DEPS
index f0c37a1..e065460e 100644
--- a/DEPS
+++ b/DEPS
@@ -253,23 +253,23 @@
   # 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': '32b1e5d861b7516104baa315d12a934dea340295',
+  'skia_revision': 'a83b8c6a78bd9ce39b621fa557e52e770269864f',
   # 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': '581e471e91a732d9c105251655c3618c2c7d170e',
+  'v8_revision': '8ff9ed3e60e742ff412cf8dd5f16f6ed343fe809',
   # 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': 'e2d5eb729537663730b6091d2539c9710ccd07a9',
+  'angle_revision': 'c874943b952e4aa24ec1c37a5542e3bcb8c20fad',
   # 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': '94de879bfdd1b0a13222d625f0f5580109aadf7a',
+  'swiftshader_revision': '732515a031faeecfee06c821d9039ebabd6bb71e',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
-  'pdfium_revision': '8f5a0068093ca141dcff03a13ba7b12fdd915648',
+  'pdfium_revision': 'b94a462ff2cb708a2e99f9c994dfef41326ce4e2',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling BoringSSL
   # and whatever else without interference from each other.
@@ -320,7 +320,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': '08a8050c47dc376c85ba2bda2ec4db2e1f7ea961',
+  'catapult_revision': '62a74cf243b4a63011f561ee10e57635a914df08',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -704,7 +704,7 @@
     Var('chromium_git') + '/external/github.com/toji/webvr.info.git' + '@' + 'c58ae99b9ff9e2aa4c524633519570bf33536248',
 
   'src/docs/website': {
-    'url': Var('chromium_git') + '/website.git' + '@' + '7d2fec37624555097407b96b6f19251f62bac923',
+    'url': Var('chromium_git') + '/website.git' + '@' + 'c5fc33d1a41991a0f64b42169220a57ffdaf7b54',
   },
 
   'src/ios/third_party/earl_grey2/src': {
@@ -1131,7 +1131,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '2c668c866d8678edbff7d10f99c03e82140242ec',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'c1ab734908f09df3d4c6672acd66c0cfcd584114',
 
   'src/third_party/devtools-frontend/src':
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
@@ -1514,7 +1514,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + 'cf4922dea8779ff37f641b2e5b652f71509320dc',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + 'dfc6cffca9b6a34068386915d50040c6bc142717',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1592,7 +1592,7 @@
       'packages': [
           {
               'package': 'fuchsia/third_party/android/aemu/release/linux-amd64',
-              'version': 'OjqkN58Kl1OnwGfKJcxwUDz-LD-d1d2qJYKU7X_peaIC'
+              'version': 'Lq0crmit6SHAoX9JV6lIzxREjsTFHYpEtIpiuUDOTCEC'
           },
       ],
       'condition': 'host_os == "linux" and checkout_fuchsia',
@@ -1805,7 +1805,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@39abbc9096080591d4b14a445c2cdfca1e525212',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@b0e85070320af039efe9a4dd3be4f41375159ad2',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index f4a2ee4..74d22535 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -495,8 +495,8 @@
     "highlighter/highlighter_view.h",
     "host/ash_window_tree_host.cc",
     "host/ash_window_tree_host.h",
+    "host/ash_window_tree_host_delegate.h",
     "host/ash_window_tree_host_init_params.h",
-    "host/ash_window_tree_host_mirroring_delegate.h",
     "host/ash_window_tree_host_mirroring_unified.cc",
     "host/ash_window_tree_host_mirroring_unified.h",
     "host/ash_window_tree_host_platform.cc",
@@ -1327,8 +1327,6 @@
     "system/phonehub/bluetooth_disabled_view.h",
     "system/phonehub/camera_roll_menu_model.cc",
     "system/phonehub/camera_roll_menu_model.h",
-    "system/phonehub/camera_roll_opt_in_view.cc",
-    "system/phonehub/camera_roll_opt_in_view.h",
     "system/phonehub/camera_roll_thumbnail.cc",
     "system/phonehub/camera_roll_thumbnail.h",
     "system/phonehub/camera_roll_view.cc",
@@ -1339,8 +1337,8 @@
     "system/phonehub/enable_hotspot_quick_action_controller.h",
     "system/phonehub/locate_phone_quick_action_controller.cc",
     "system/phonehub/locate_phone_quick_action_controller.h",
-    "system/phonehub/notification_opt_in_view.cc",
-    "system/phonehub/notification_opt_in_view.h",
+    "system/phonehub/multidevice_feature_opt_in_view.cc",
+    "system/phonehub/multidevice_feature_opt_in_view.h",
     "system/phonehub/onboarding_view.cc",
     "system/phonehub/onboarding_view.h",
     "system/phonehub/phone_connected_view.cc",
@@ -2419,6 +2417,7 @@
     "highlighter/highlighter_controller_unittest.cc",
     "highlighter/highlighter_gesture_util_unittest.cc",
     "host/ash_window_tree_host_platform_unittest.cc",
+    "host/ash_window_tree_host_unified_unittest.cc",
     "ime/ime_controller_impl_unittest.cc",
     "in_session_auth/authentication_dialog_unittest.cc",
     "in_session_auth/in_session_auth_dialog_controller_impl_unittest.cc",
@@ -2923,6 +2922,7 @@
     "//ui/events:test_support",
     "//ui/events/devices",
     "//ui/events/devices:test_support",
+    "//ui/events/ozone:ozone",
     "//ui/gfx",
     "//ui/gfx:test_support",
     "//ui/gfx/geometry",
diff --git a/ash/app_list/views/search_result_view.cc b/ash/app_list/views/search_result_view.cc
index 78fd2cba..b46a3de 100644
--- a/ash/app_list/views/search_result_view.cc
+++ b/ash/app_list/views/search_result_view.cc
@@ -77,8 +77,12 @@
 
 constexpr int kSearchRatingStarPadding = 4;
 constexpr int kSearchRatingStarSize = 16;
-constexpr gfx::Insets kAnswerCardBorder(12, 12, 12, 12);
-constexpr gfx::Insets kBigTitleBorder(0, 14, 0, 12);
+constexpr int kAnswerCardBorderMargin = 12;
+constexpr gfx::Insets kAnswerCardBorder(kAnswerCardBorderMargin,
+                                        kAnswerCardBorderMargin,
+                                        kAnswerCardBorderMargin,
+                                        kAnswerCardBorderMargin);
+constexpr gfx::Insets kBigTitleBorder(0, 0, 0, kAnswerCardBorderMargin);
 
 views::ImageView* SetupChildImageView(views::FlexLayoutView* parent) {
   views::ImageView* image_view =
@@ -730,7 +734,12 @@
   actions_view()->SetBoundsRect(actions_bounds);
 
   gfx::Rect text_bounds(rect);
-  text_bounds.set_x(kPreferredIconViewWidth);
+  // Text bounds need to be shifted over by kAnswerCardBorderMargin for answer
+  // card views to make room for the kAnswerCardBorder.
+  text_bounds.set_x(kPreferredIconViewWidth +
+                    (view_type_ == SearchResultViewType::kAnswerCard
+                         ? kAnswerCardBorderMargin
+                         : 0));
   if (actions_view()->GetVisible()) {
     text_bounds.set_width(
         rect.width() - kPreferredIconViewWidth - kTextTrailPadding -
diff --git a/ash/ash_strings.grd b/ash/ash_strings.grd
index 86dae97..dc763bb 100644
--- a/ash/ash_strings.grd
+++ b/ash/ash_strings.grd
@@ -1589,12 +1589,6 @@
       <message name="IDS_ASH_PHONE_HUB_SUB_FEATURE_OPT_IN_DISMISS_BUTTON" desc="Label for the dismiss the dialog that allows the user to opt in to a sub feature of Phone Hub.">
         Dismiss
       </message>
-      <message name="IDS_ASH_PHONE_HUB_CAMERA_ROLL_OPT_IN_DESCRIPTION" desc="Description for the camera roll opt in view.">
-        View recent photos from your phone‘s camera roll on <ph name="DEVICE_TYPE">$1<ex>Chromebook</ex></ph>
-      </message>
-      <message name="IDS_ASH_PHONE_HUB_CAMERA_ROLL_OPT_IN_TURN_ON_BUTTON" desc="Label for button to turn on the setting to show recent photo from the phone.">
-        Turn on
-      </message>
       <message name="IDS_ASH_PHONE_HUB_NOTIFICATION_INLINE_REPLY_BUTTON" desc="Label for the inline reply button inside a PhoneHub notification.">
         Reply
       </message>
diff --git a/ash/ash_strings_grd/IDS_ASH_PHONE_HUB_CAMERA_ROLL_OPT_IN_DESCRIPTION.png.sha1 b/ash/ash_strings_grd/IDS_ASH_PHONE_HUB_CAMERA_ROLL_OPT_IN_DESCRIPTION.png.sha1
deleted file mode 100644
index 6cd2fde5..0000000
--- a/ash/ash_strings_grd/IDS_ASH_PHONE_HUB_CAMERA_ROLL_OPT_IN_DESCRIPTION.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-fc117b6ae46d7533cd00d28a8f676132ad599bf7
\ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_PHONE_HUB_CAMERA_ROLL_OPT_IN_TURN_ON_BUTTON.png.sha1 b/ash/ash_strings_grd/IDS_ASH_PHONE_HUB_CAMERA_ROLL_OPT_IN_TURN_ON_BUTTON.png.sha1
deleted file mode 100644
index 6cd2fde5..0000000
--- a/ash/ash_strings_grd/IDS_ASH_PHONE_HUB_CAMERA_ROLL_OPT_IN_TURN_ON_BUTTON.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-fc117b6ae46d7533cd00d28a8f676132ad599bf7
\ No newline at end of file
diff --git a/ash/components/arc/session/BUILD.gn b/ash/components/arc/session/BUILD.gn
index 14adae93..add6a9e 100644
--- a/ash/components/arc/session/BUILD.gn
+++ b/ash/components/arc/session/BUILD.gn
@@ -53,6 +53,7 @@
     "//ash/public/cpp/external_arc:external_arc",
     "//chromeos/components/sensors:buildflags",
     "//chromeos/dbus/dlcservice:dlcservice",
+    "//chromeos/dbus/dlcservice:dlcservice_proto",
     "//chromeos/dbus/session_manager",
     "//chromeos/dbus/upstart",
     "//chromeos/memory:memory",
diff --git a/ash/components/arc/session/arc_dlc_installer.cc b/ash/components/arc/session/arc_dlc_installer.cc
index be81a20..d92564b 100644
--- a/ash/components/arc/session/arc_dlc_installer.cc
+++ b/ash/components/arc/session/arc_dlc_installer.cc
@@ -6,6 +6,7 @@
 
 #include "base/callback_helpers.h"
 #include "base/logging.h"
+#include "chromeos/dbus/dlcservice/dlcservice.pb.h"
 
 namespace arc {
 
@@ -61,8 +62,10 @@
 
   state_ = InstallerState::kInstalling;
   VLOG(2) << "Installing ARC DLC: " << kHoudiniRvcDlc;
+  dlcservice::InstallRequest install_request;
+  install_request.set_id(kHoudiniRvcDlc);
   chromeos::DlcserviceClient::Get()->Install(
-      kHoudiniRvcDlc,
+      install_request,
       base::BindOnce(&ArcDlcInstaller::OnDlcInstalled,
                      weak_ptr_factory_.GetWeakPtr(), kHoudiniRvcDlc),
       base::DoNothing());
diff --git a/ash/components/phonehub/BUILD.gn b/ash/components/phonehub/BUILD.gn
index 7f54eafb..5b1fb0c8 100644
--- a/ash/components/phonehub/BUILD.gn
+++ b/ash/components/phonehub/BUILD.gn
@@ -54,16 +54,16 @@
     "message_sender.h",
     "message_sender_impl.cc",
     "message_sender_impl.h",
+    "multidevice_feature_access_manager.cc",
+    "multidevice_feature_access_manager.h",
+    "multidevice_feature_access_manager_impl.cc",
+    "multidevice_feature_access_manager_impl.h",
     "multidevice_setup_state_updater.cc",
     "multidevice_setup_state_updater.h",
     "mutable_phone_model.cc",
     "mutable_phone_model.h",
     "notification.cc",
     "notification.h",
-    "notification_access_manager.cc",
-    "notification_access_manager.h",
-    "notification_access_manager_impl.cc",
-    "notification_access_manager_impl.h",
     "notification_access_setup_operation.cc",
     "notification_access_setup_operation.h",
     "notification_click_handler.h",
@@ -154,8 +154,8 @@
     "fake_feature_status_provider.h",
     "fake_find_my_device_controller.cc",
     "fake_find_my_device_controller.h",
-    "fake_notification_access_manager.cc",
-    "fake_notification_access_manager.h",
+    "fake_multidevice_feature_access_manager.cc",
+    "fake_multidevice_feature_access_manager.h",
     "fake_notification_interaction_handler.cc",
     "fake_notification_interaction_handler.h",
     "fake_notification_manager.cc",
@@ -231,9 +231,9 @@
     "invalid_connection_disconnector_unittest.cc",
     "message_receiver_unittest.cc",
     "message_sender_unittest.cc",
+    "multidevice_feature_access_manager_impl_unittest.cc",
     "multidevice_setup_state_updater_unittest.cc",
     "mutable_phone_model_unittest.cc",
-    "notification_access_manager_impl_unittest.cc",
     "notification_interaction_handler_impl_unittest.cc",
     "notification_manager_impl_unittest.cc",
     "notification_processor_unittest.cc",
diff --git a/ash/components/phonehub/fake_multidevice_feature_access_manager.cc b/ash/components/phonehub/fake_multidevice_feature_access_manager.cc
new file mode 100644
index 0000000..3f3c9464
--- /dev/null
+++ b/ash/components/phonehub/fake_multidevice_feature_access_manager.cc
@@ -0,0 +1,90 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/components/phonehub/fake_multidevice_feature_access_manager.h"
+
+namespace ash {
+namespace phonehub {
+
+FakeMultideviceFeatureAccessManager::FakeMultideviceFeatureAccessManager(
+    AccessStatus notification_access_status,
+    AccessStatus camera_roll_access_status,
+    AccessProhibitedReason reason)
+    : notification_access_status_(notification_access_status),
+      camera_roll_access_status_(camera_roll_access_status),
+      access_prohibited_reason_(reason) {}
+
+FakeMultideviceFeatureAccessManager::~FakeMultideviceFeatureAccessManager() =
+    default;
+
+void FakeMultideviceFeatureAccessManager::SetNotificationAccessStatusInternal(
+    AccessStatus notification_access_status,
+    AccessProhibitedReason reason) {
+  if (notification_access_status_ == notification_access_status)
+    return;
+
+  notification_access_status_ = notification_access_status;
+  access_prohibited_reason_ = reason;
+  NotifyNotificationAccessChanged();
+}
+
+MultideviceFeatureAccessManager::AccessProhibitedReason
+FakeMultideviceFeatureAccessManager::GetNotificationAccessProhibitedReason()
+    const {
+  return access_prohibited_reason_;
+}
+
+void FakeMultideviceFeatureAccessManager::SetCameraRollAccessStatusInternal(
+    AccessStatus camera_roll_access_status) {
+  if (camera_roll_access_status_ == camera_roll_access_status)
+    return;
+
+  camera_roll_access_status_ = camera_roll_access_status;
+  NotifyCameraRollAccessChanged();
+}
+
+MultideviceFeatureAccessManager::AccessStatus
+FakeMultideviceFeatureAccessManager::GetNotificationAccessStatus() const {
+  return notification_access_status_;
+}
+MultideviceFeatureAccessManager::AccessStatus
+FakeMultideviceFeatureAccessManager::GetCameraRollAccessStatus() const {
+  return camera_roll_access_status_;
+}
+
+bool FakeMultideviceFeatureAccessManager::
+    HasMultideviceFeatureSetupUiBeenDismissed() const {
+  return has_notification_setup_ui_been_dismissed_;
+}
+
+void FakeMultideviceFeatureAccessManager::DismissSetupRequiredUi() {
+  has_notification_setup_ui_been_dismissed_ = true;
+}
+
+void FakeMultideviceFeatureAccessManager::
+    ResetHasMultideviceFeatureSetupUiBeenDismissed() {
+  has_notification_setup_ui_been_dismissed_ = false;
+}
+
+void FakeMultideviceFeatureAccessManager::SetNotificationSetupOperationStatus(
+    NotificationAccessSetupOperation::Status new_status) {
+  switch (new_status) {
+    case NotificationAccessSetupOperation::Status::kCompletedSuccessfully:
+      SetNotificationAccessStatusInternal(AccessStatus::kAccessGranted);
+      break;
+    case NotificationAccessSetupOperation::Status::
+        kProhibitedFromProvidingAccess:
+      SetNotificationAccessStatusInternal(AccessStatus::kProhibited);
+      break;
+    default:
+      // Do not update access status based on other operation status values.
+      break;
+  }
+
+  MultideviceFeatureAccessManager::SetNotificationSetupOperationStatus(
+      new_status);
+}
+
+}  // namespace phonehub
+}  // namespace ash
diff --git a/ash/components/phonehub/fake_multidevice_feature_access_manager.h b/ash/components/phonehub/fake_multidevice_feature_access_manager.h
new file mode 100644
index 0000000..d33e68f
--- /dev/null
+++ b/ash/components/phonehub/fake_multidevice_feature_access_manager.h
@@ -0,0 +1,60 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_COMPONENTS_PHONEHUB_FAKE_MULTIDEVICE_FEATURE_ACCESS_MANAGER_H_
+#define ASH_COMPONENTS_PHONEHUB_FAKE_MULTIDEVICE_FEATURE_ACCESS_MANAGER_H_
+
+#include "ash/components/phonehub/multidevice_feature_access_manager.h"
+
+namespace ash {
+namespace phonehub {
+
+class FakeMultideviceFeatureAccessManager
+    : public MultideviceFeatureAccessManager {
+ public:
+  explicit FakeMultideviceFeatureAccessManager(
+      AccessStatus notification_access_status =
+          AccessStatus::kAvailableButNotGranted,
+      AccessStatus camera_roll_access_status =
+          AccessStatus::kAvailableButNotGranted,
+      AccessProhibitedReason reason = AccessProhibitedReason::kWorkProfile);
+  ~FakeMultideviceFeatureAccessManager() override;
+
+  using MultideviceFeatureAccessManager::IsSetupOperationInProgress;
+
+  void SetNotificationAccessStatusInternal(
+      AccessStatus notification_access_status,
+      AccessProhibitedReason reason =
+          AccessProhibitedReason::kUnknown) override;
+  AccessStatus GetNotificationAccessStatus() const override;
+  void SetNotificationSetupOperationStatus(
+      NotificationAccessSetupOperation::Status new_status);
+  AccessProhibitedReason GetNotificationAccessProhibitedReason() const override;
+
+  bool HasMultideviceFeatureSetupUiBeenDismissed() const override;
+  void DismissSetupRequiredUi() override;
+  void ResetHasMultideviceFeatureSetupUiBeenDismissed();
+
+  void SetCameraRollAccessStatusInternal(
+      AccessStatus camera_roll_access_status) override;
+  AccessStatus GetCameraRollAccessStatus() const override;
+
+ private:
+  AccessStatus notification_access_status_;
+  AccessStatus camera_roll_access_status_;
+  AccessProhibitedReason access_prohibited_reason_;
+  bool has_notification_setup_ui_been_dismissed_ = false;
+};
+
+}  // namespace phonehub
+}  // namespace ash
+
+// TODO(https://crbug.com/1164001): remove after the migration is finished.
+namespace chromeos {
+namespace phonehub {
+using ::ash::phonehub::FakeMultideviceFeatureAccessManager;
+}
+}  // namespace chromeos
+
+#endif  // ASH_COMPONENTS_PHONEHUB_FAKE_MULTIDEVICE_FEATURE_ACCESS_MANAGER_H_
diff --git a/ash/components/phonehub/fake_notification_access_manager.cc b/ash/components/phonehub/fake_notification_access_manager.cc
deleted file mode 100644
index e0ba7ba..0000000
--- a/ash/components/phonehub/fake_notification_access_manager.cc
+++ /dev/null
@@ -1,70 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ash/components/phonehub/fake_notification_access_manager.h"
-
-namespace ash {
-namespace phonehub {
-
-FakeNotificationAccessManager::FakeNotificationAccessManager(
-    AccessStatus access_status,
-    AccessProhibitedReason reason)
-    : access_status_(access_status), access_prohibited_reason_(reason) {}
-
-FakeNotificationAccessManager::~FakeNotificationAccessManager() = default;
-
-void FakeNotificationAccessManager::SetAccessStatusInternal(
-    AccessStatus access_status,
-    AccessProhibitedReason reason) {
-  if (access_status_ == access_status)
-    return;
-
-  access_status_ = access_status;
-  access_prohibited_reason_ = reason;
-  NotifyNotificationAccessChanged();
-}
-
-NotificationAccessManager::AccessStatus
-FakeNotificationAccessManager::GetAccessStatus() const {
-  return access_status_;
-}
-
-NotificationAccessManager::AccessProhibitedReason
-FakeNotificationAccessManager::GetAccessProhibitedReason() const {
-  return access_prohibited_reason_;
-}
-
-bool FakeNotificationAccessManager::HasNotificationSetupUiBeenDismissed()
-    const {
-  return has_notification_setup_ui_been_dismissed_;
-}
-
-void FakeNotificationAccessManager::DismissSetupRequiredUi() {
-  has_notification_setup_ui_been_dismissed_ = true;
-}
-
-void FakeNotificationAccessManager::ResetHasNotificationSetupUiBeenDismissed() {
-  has_notification_setup_ui_been_dismissed_ = false;
-}
-
-void FakeNotificationAccessManager::SetNotificationSetupOperationStatus(
-    NotificationAccessSetupOperation::Status new_status) {
-  switch (new_status) {
-    case NotificationAccessSetupOperation::Status::kCompletedSuccessfully:
-      SetAccessStatusInternal(AccessStatus::kAccessGranted);
-      break;
-    case NotificationAccessSetupOperation::Status::
-        kProhibitedFromProvidingAccess:
-      SetAccessStatusInternal(AccessStatus::kProhibited);
-      break;
-    default:
-      // Do not update access status based on other operation status values.
-      break;
-  }
-
-  NotificationAccessManager::SetNotificationSetupOperationStatus(new_status);
-}
-
-}  // namespace phonehub
-}  // namespace ash
diff --git a/ash/components/phonehub/fake_notification_access_manager.h b/ash/components/phonehub/fake_notification_access_manager.h
deleted file mode 100644
index 79e6ea4..0000000
--- a/ash/components/phonehub/fake_notification_access_manager.h
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef ASH_COMPONENTS_PHONEHUB_FAKE_NOTIFICATION_ACCESS_MANAGER_H_
-#define ASH_COMPONENTS_PHONEHUB_FAKE_NOTIFICATION_ACCESS_MANAGER_H_
-
-#include "ash/components/phonehub/notification_access_manager.h"
-
-namespace ash {
-namespace phonehub {
-
-class FakeNotificationAccessManager : public NotificationAccessManager {
- public:
-  explicit FakeNotificationAccessManager(
-      AccessStatus access_status = AccessStatus::kAvailableButNotGranted,
-      AccessProhibitedReason reason = AccessProhibitedReason::kWorkProfile);
-  ~FakeNotificationAccessManager() override;
-
-  using NotificationAccessManager::IsSetupOperationInProgress;
-
-  void SetAccessStatusInternal(AccessStatus access_status,
-                               AccessProhibitedReason reason =
-                                   AccessProhibitedReason::kUnknown) override;
-  void SetNotificationSetupOperationStatus(
-      NotificationAccessSetupOperation::Status new_status);
-
-  // NotificationAccessManager:
-  AccessStatus GetAccessStatus() const override;
-  AccessProhibitedReason GetAccessProhibitedReason() const override;
-  bool HasNotificationSetupUiBeenDismissed() const override;
-  void DismissSetupRequiredUi() override;
-
-  void ResetHasNotificationSetupUiBeenDismissed();
-
- private:
-  AccessStatus access_status_;
-  AccessProhibitedReason access_prohibited_reason_;
-  bool has_notification_setup_ui_been_dismissed_ = false;
-};
-
-}  // namespace phonehub
-}  // namespace ash
-
-// TODO(https://crbug.com/1164001): remove after the migration is finished.
-namespace chromeos {
-namespace phonehub {
-using ::ash::phonehub::FakeNotificationAccessManager;
-}
-}  // namespace chromeos
-
-#endif  // ASH_COMPONENTS_PHONEHUB_FAKE_NOTIFICATION_ACCESS_MANAGER_H_
diff --git a/ash/components/phonehub/fake_phone_hub_manager.cc b/ash/components/phonehub/fake_phone_hub_manager.cc
index baded97..e841fea 100644
--- a/ash/components/phonehub/fake_phone_hub_manager.cc
+++ b/ash/components/phonehub/fake_phone_hub_manager.cc
@@ -34,8 +34,9 @@
   return &fake_find_my_device_controller_;
 }
 
-NotificationAccessManager* FakePhoneHubManager::GetNotificationAccessManager() {
-  return &fake_notification_access_manager_;
+MultideviceFeatureAccessManager*
+FakePhoneHubManager::GetMultideviceFeatureAccessManager() {
+  return &fake_multidevice_feature_access_manager_;
 }
 
 NotificationInteractionHandler*
diff --git a/ash/components/phonehub/fake_phone_hub_manager.h b/ash/components/phonehub/fake_phone_hub_manager.h
index fd0b0c7..6b507517 100644
--- a/ash/components/phonehub/fake_phone_hub_manager.h
+++ b/ash/components/phonehub/fake_phone_hub_manager.h
@@ -11,7 +11,7 @@
 #include "ash/components/phonehub/fake_do_not_disturb_controller.h"
 #include "ash/components/phonehub/fake_feature_status_provider.h"
 #include "ash/components/phonehub/fake_find_my_device_controller.h"
-#include "ash/components/phonehub/fake_notification_access_manager.h"
+#include "ash/components/phonehub/fake_multidevice_feature_access_manager.h"
 #include "ash/components/phonehub/fake_notification_interaction_handler.h"
 #include "ash/components/phonehub/fake_notification_manager.h"
 #include "ash/components/phonehub/fake_onboarding_ui_tracker.h"
@@ -43,8 +43,9 @@
     return &fake_find_my_device_controller_;
   }
 
-  FakeNotificationAccessManager* fake_notification_access_manager() {
-    return &fake_notification_access_manager_;
+  FakeMultideviceFeatureAccessManager*
+  fake_multidevice_feature_access_manager() {
+    return &fake_multidevice_feature_access_manager_;
   }
 
   FakeNotificationInteractionHandler* fake_notification_interaction_handler() {
@@ -96,7 +97,8 @@
   DoNotDisturbController* GetDoNotDisturbController() override;
   FeatureStatusProvider* GetFeatureStatusProvider() override;
   FindMyDeviceController* GetFindMyDeviceController() override;
-  NotificationAccessManager* GetNotificationAccessManager() override;
+  MultideviceFeatureAccessManager* GetMultideviceFeatureAccessManager()
+      override;
   NotificationInteractionHandler* GetNotificationInteractionHandler() override;
   NotificationManager* GetNotificationManager() override;
   OnboardingUiTracker* GetOnboardingUiTracker() override;
@@ -110,7 +112,7 @@
   FakeDoNotDisturbController fake_do_not_disturb_controller_;
   FakeFeatureStatusProvider fake_feature_status_provider_;
   FakeFindMyDeviceController fake_find_my_device_controller_;
-  FakeNotificationAccessManager fake_notification_access_manager_;
+  FakeMultideviceFeatureAccessManager fake_multidevice_feature_access_manager_;
   FakeNotificationInteractionHandler fake_notification_interaction_handler_;
   FakeNotificationManager fake_notification_manager_;
   FakeOnboardingUiTracker fake_onboarding_ui_tracker_;
diff --git a/ash/components/phonehub/multidevice_feature_access_manager.cc b/ash/components/phonehub/multidevice_feature_access_manager.cc
new file mode 100644
index 0000000..706d7af
--- /dev/null
+++ b/ash/components/phonehub/multidevice_feature_access_manager.cc
@@ -0,0 +1,143 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/components/phonehub/multidevice_feature_access_manager.h"
+
+#include "base/bind.h"
+#include "base/memory/ptr_util.h"
+#include "chromeos/components/multidevice/logging/logging.h"
+
+namespace ash {
+namespace phonehub {
+
+MultideviceFeatureAccessManager::MultideviceFeatureAccessManager() = default;
+
+MultideviceFeatureAccessManager::~MultideviceFeatureAccessManager() = default;
+
+std::unique_ptr<NotificationAccessSetupOperation>
+MultideviceFeatureAccessManager::AttemptNotificationSetup(
+    NotificationAccessSetupOperation::Delegate* delegate) {
+  // Should only be able to start the setup process if notification access is
+  // available but not yet granted.
+  // TODO: check camra roll access status once setup flow is wired up
+  if (GetNotificationAccessStatus() != AccessStatus::kAvailableButNotGranted)
+    return nullptr;
+
+  int operation_id = next_operation_id_;
+  ++next_operation_id_;
+
+  auto operation = base::WrapUnique(new NotificationAccessSetupOperation(
+      delegate,
+      base::BindOnce(&MultideviceFeatureAccessManager::OnSetupOperationDeleted,
+                     weak_ptr_factory_.GetWeakPtr(), operation_id)));
+  id_to_operation_map_.emplace(operation_id, operation.get());
+
+  OnSetupRequested();
+  return operation;
+}
+
+void MultideviceFeatureAccessManager::AddObserver(Observer* observer) {
+  observer_list_.AddObserver(observer);
+}
+
+void MultideviceFeatureAccessManager::RemoveObserver(Observer* observer) {
+  observer_list_.RemoveObserver(observer);
+}
+
+void MultideviceFeatureAccessManager::NotifyNotificationAccessChanged() {
+  for (auto& observer : observer_list_)
+    observer.OnNotificationAccessChanged();
+}
+
+void MultideviceFeatureAccessManager::NotifyCameraRollAccessChanged() {
+  for (auto& observer : observer_list_)
+    observer.OnCameraRollAccessChanged();
+}
+
+void MultideviceFeatureAccessManager::SetNotificationSetupOperationStatus(
+    NotificationAccessSetupOperation::Status new_status) {
+  DCHECK(IsSetupOperationInProgress());
+
+  PA_LOG(INFO) << "Notification access setup flow - new status: " << new_status;
+
+  for (auto& it : id_to_operation_map_)
+    it.second->NotifyStatusChanged(new_status);
+
+  if (NotificationAccessSetupOperation::IsFinalStatus(new_status))
+    id_to_operation_map_.clear();
+}
+
+bool MultideviceFeatureAccessManager::IsSetupOperationInProgress() const {
+  return !id_to_operation_map_.empty();
+}
+
+void MultideviceFeatureAccessManager::OnSetupOperationDeleted(
+    int operation_id) {
+  auto it = id_to_operation_map_.find(operation_id);
+  if (it == id_to_operation_map_.end())
+    return;
+
+  id_to_operation_map_.erase(it);
+
+  if (id_to_operation_map_.empty())
+    PA_LOG(INFO) << "Notification access setup operation has ended.";
+}
+
+void MultideviceFeatureAccessManager::Observer::OnNotificationAccessChanged() {
+  // Optional method, inherit class doesn't have to implement this
+}
+
+void MultideviceFeatureAccessManager::Observer::OnCameraRollAccessChanged() {
+  // Optional method, inherit class doesn't have to implement this
+}
+
+std::ostream& operator<<(std::ostream& stream,
+                         MultideviceFeatureAccessManager::AccessStatus status) {
+  switch (status) {
+    case MultideviceFeatureAccessManager::AccessStatus::kProhibited:
+      stream << "[Access prohibited]";
+      break;
+    case MultideviceFeatureAccessManager::AccessStatus::kAvailableButNotGranted:
+      stream << "[Access available but not granted]";
+      break;
+    case MultideviceFeatureAccessManager::AccessStatus::kAccessGranted:
+      stream << "[Access granted]";
+      break;
+  }
+  return stream;
+}
+
+std::ostream& operator<<(
+    std::ostream& stream,
+    MultideviceFeatureAccessManager::AccessProhibitedReason reason) {
+  switch (reason) {
+    case MultideviceFeatureAccessManager::AccessProhibitedReason::kUnknown:
+      stream << "[Unknown]";
+      break;
+    case MultideviceFeatureAccessManager::AccessProhibitedReason::kWorkProfile:
+      stream << "[Work Profile]";
+      break;
+    case MultideviceFeatureAccessManager::AccessProhibitedReason::
+        kDisabledByPhonePolicy:
+      stream << "[Admin Policy]";
+      break;
+  }
+  return stream;
+}
+
+std::ostream& operator<<(
+    std::ostream& stream,
+    std::pair<MultideviceFeatureAccessManager::AccessStatus,
+              MultideviceFeatureAccessManager::AccessProhibitedReason>
+        status_reason) {
+  stream << status_reason.first;
+  if (status_reason.first ==
+      MultideviceFeatureAccessManager::AccessStatus::kProhibited) {
+    stream << "," << status_reason.second;
+  }
+  return stream;
+}
+
+}  // namespace phonehub
+}  // namespace ash
diff --git a/ash/components/phonehub/multidevice_feature_access_manager.h b/ash/components/phonehub/multidevice_feature_access_manager.h
new file mode 100644
index 0000000..4cff2c4a
--- /dev/null
+++ b/ash/components/phonehub/multidevice_feature_access_manager.h
@@ -0,0 +1,169 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_COMPONENTS_PHONEHUB_MULTIDEVICE_FEATURE_ACCESS_MANAGER_H_
+#define ASH_COMPONENTS_PHONEHUB_MULTIDEVICE_FEATURE_ACCESS_MANAGER_H_
+
+#include <ostream>
+
+#include "ash/components/phonehub/notification_access_setup_operation.h"
+#include "base/containers/flat_map.h"
+#include "base/memory/weak_ptr.h"
+#include "base/observer_list.h"
+#include "base/observer_list_types.h"
+
+namespace ash {
+namespace phonehub {
+
+// Tracks the status of whether the user has granted permissions for the
+// following features to be enabled on the host device:
+// 1. Notification
+// 2. Camera roll
+//
+// While Phone Hub can be enabled via Chrome OS, access to
+// notifications requires that the user grant access via Android settings. If a
+// Phone Hub connection to the phone has never succeeded, we assume that access
+// has not yet been granted. If there is no active Phone Hub connection, we
+// assume that the last access value seen is the current value.
+//
+// Additionally, this class provides an API for requesting the notification
+// access setup flow via AttemptNotificationSetup().
+//
+// In order for user to use camera roll feature, users need to explicit give
+// consent for the feature to be enabled on the phone via the feature setup
+// dialog.
+class MultideviceFeatureAccessManager {
+ public:
+  // Status of a feature's access. Numerical values are stored in prefs and
+  // should not be changed or reused.
+  enum class AccessStatus {
+    // Access has not been granted and is prohibited from being granted (e.g.,
+    // if the phone is using a Work Profile when trying to use notification
+    // fearture).
+    kProhibited = 0,
+
+    // Access has not been granted, but the user is free to grant access.
+    kAvailableButNotGranted = 1,
+
+    // Access has been granted by the user.
+    kAccessGranted = 2
+  };
+
+  enum class AccessProhibitedReason {
+    // Access is either not prohibited or is unset. Use as a safe default value.
+    kUnknown = 0,
+    // Access is prohibited because the phone is using a Work Profile and on
+    // Android version <N.
+    kWorkProfile = 1,
+    // Access is prohibited because the phone is using a Work Profile, and the
+    // policy managing the phone disables access.
+    kDisabledByPhonePolicy = 2
+  };
+
+  class Observer : public base::CheckedObserver {
+   public:
+    ~Observer() override = default;
+
+    // Called when notification access has changed; use
+    // GetNotificationAccessStatus() for the new status.
+    virtual void OnNotificationAccessChanged();
+
+    // Called when camera roll access has changed; use
+    // GetCameraRollAccessStatus() for the new status.
+    virtual void OnCameraRollAccessChanged();
+  };
+
+  MultideviceFeatureAccessManager(MultideviceFeatureAccessManager&) = delete;
+  MultideviceFeatureAccessManager& operator=(MultideviceFeatureAccessManager&) =
+      delete;
+  virtual ~MultideviceFeatureAccessManager();
+
+  virtual AccessStatus GetNotificationAccessStatus() const = 0;
+
+  virtual AccessStatus GetCameraRollAccessStatus() const = 0;
+
+  // Returns the reason notification access status is prohibited. The return
+  // result is valid if the current access status (from GetAccessStatus())
+  // is AccessStatus::kProhibited. Otherwise, the result is undefined and should
+  // not be used.
+  virtual AccessProhibitedReason GetNotificationAccessProhibitedReason()
+      const = 0;
+
+  virtual bool HasMultideviceFeatureSetupUiBeenDismissed() const = 0;
+
+  // Disables the ability to show the banner within the PhoneHub UI.
+  virtual void DismissSetupRequiredUi() = 0;
+
+  // Starts an attempt to enable the notification access. |delegate| will be
+  // updated with the status of the flow as long as the operation object
+  // returned by this function remains instantiated.
+  //
+  // To cancel an ongoing setup attempt, delete the operation. If a setup
+  // attempt fails, clients can retry by calling AttemptNotificationSetup()
+  // again to start a new attempt.
+  //
+  // If notification access has already been granted, this function returns null
+  // since there is nothing to set up.
+  std::unique_ptr<NotificationAccessSetupOperation> AttemptNotificationSetup(
+      NotificationAccessSetupOperation::Delegate* delegate);
+
+  void AddObserver(Observer* observer);
+  void RemoveObserver(Observer* observer);
+
+ protected:
+  MultideviceFeatureAccessManager();
+
+  void NotifyNotificationAccessChanged();
+  void NotifyCameraRollAccessChanged();
+  void SetNotificationSetupOperationStatus(
+      NotificationAccessSetupOperation::Status new_status);
+
+  bool IsSetupOperationInProgress() const;
+
+  virtual void OnSetupRequested() {}
+
+ private:
+  friend class MultideviceFeatureAccessManagerImplTest;
+  friend class PhoneStatusProcessor;
+
+  // Sets the internal AccessStatus but does not send a request for
+  // a new status to the remote phone device.
+  virtual void SetNotificationAccessStatusInternal(
+      AccessStatus access_status,
+      AccessProhibitedReason reason) = 0;
+  // Sets the internal AccessStatus but does not send a request for
+  // a new status to the remote phone device.
+  virtual void SetCameraRollAccessStatusInternal(
+      AccessStatus camera_roll_access_status) = 0;
+
+  void OnSetupOperationDeleted(int operation_id);
+
+  int next_operation_id_ = 0;
+  base::flat_map<int, NotificationAccessSetupOperation*> id_to_operation_map_;
+  base::ObserverList<Observer> observer_list_;
+  base::WeakPtrFactory<MultideviceFeatureAccessManager> weak_ptr_factory_{this};
+};
+
+std::ostream& operator<<(std::ostream& stream,
+                         MultideviceFeatureAccessManager::AccessStatus status);
+std::ostream& operator<<(
+    std::ostream& stream,
+    MultideviceFeatureAccessManager::AccessProhibitedReason reason);
+std::ostream& operator<<(
+    std::ostream& stream,
+    std::pair<MultideviceFeatureAccessManager::AccessStatus,
+              MultideviceFeatureAccessManager::AccessProhibitedReason>
+        status_reason);
+
+}  // namespace phonehub
+}  // namespace ash
+
+// TODO(https://crbug.com/1164001): remove after the migration is finished.
+namespace chromeos {
+namespace phonehub {
+using ::ash::phonehub::MultideviceFeatureAccessManager;
+}
+}  // namespace chromeos
+
+#endif  // ASH_COMPONENTS_PHONEHUB_MULTIDEVICE_FEATURE_ACCESS_MANAGER_H_
diff --git a/ash/components/phonehub/notification_access_manager_impl.cc b/ash/components/phonehub/multidevice_feature_access_manager_impl.cc
similarity index 76%
rename from ash/components/phonehub/notification_access_manager_impl.cc
rename to ash/components/phonehub/multidevice_feature_access_manager_impl.cc
index 67739e0..cf81a26 100644
--- a/ash/components/phonehub/notification_access_manager_impl.cc
+++ b/ash/components/phonehub/multidevice_feature_access_manager_impl.cc
@@ -2,22 +2,27 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ash/components/phonehub/notification_access_manager_impl.h"
+#include "ash/components/phonehub/multidevice_feature_access_manager_impl.h"
 
 #include "ash/components/phonehub/connection_scheduler.h"
 #include "ash/components/phonehub/message_sender.h"
 #include "ash/components/phonehub/pref_names.h"
+#include "ash/components/phonehub/util/histogram_util.h"
 #include "chromeos/components/multidevice/logging/logging.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
+#include "pref_names.h"
 
 namespace ash {
 namespace phonehub {
 
 // static
-void NotificationAccessManagerImpl::RegisterPrefs(
+void MultideviceFeatureAccessManagerImpl::RegisterPrefs(
     PrefRegistrySimple* registry) {
   registry->RegisterIntegerPref(
+      prefs::kCameraRollAccessStatus,
+      static_cast<int>(AccessStatus::kAvailableButNotGranted));
+  registry->RegisterIntegerPref(
       prefs::kNotificationAccessStatus,
       static_cast<int>(AccessStatus::kAvailableButNotGranted));
   registry->RegisterIntegerPref(
@@ -28,7 +33,7 @@
                                 true);
 }
 
-NotificationAccessManagerImpl::NotificationAccessManagerImpl(
+MultideviceFeatureAccessManagerImpl::MultideviceFeatureAccessManagerImpl(
     PrefService* pref_service,
     FeatureStatusProvider* feature_status_provider,
     MessageSender* message_sender,
@@ -44,33 +49,40 @@
   feature_status_provider_->AddObserver(this);
 }
 
-NotificationAccessManagerImpl::~NotificationAccessManagerImpl() {
+MultideviceFeatureAccessManagerImpl::~MultideviceFeatureAccessManagerImpl() {
   feature_status_provider_->RemoveObserver(this);
 }
 
-bool NotificationAccessManagerImpl::HasNotificationSetupUiBeenDismissed()
-    const {
+bool MultideviceFeatureAccessManagerImpl::
+    HasMultideviceFeatureSetupUiBeenDismissed() const {
   return pref_service_->GetBoolean(prefs::kHasDismissedSetupRequiredUi);
 }
 
-void NotificationAccessManagerImpl::DismissSetupRequiredUi() {
+void MultideviceFeatureAccessManagerImpl::DismissSetupRequiredUi() {
   pref_service_->SetBoolean(prefs::kHasDismissedSetupRequiredUi, true);
 }
 
-NotificationAccessManagerImpl::AccessStatus
-NotificationAccessManagerImpl::GetAccessStatus() const {
+MultideviceFeatureAccessManagerImpl::AccessStatus
+MultideviceFeatureAccessManagerImpl::GetNotificationAccessStatus() const {
   int status = pref_service_->GetInteger(prefs::kNotificationAccessStatus);
   return static_cast<AccessStatus>(status);
 }
 
-NotificationAccessManagerImpl::AccessProhibitedReason
-NotificationAccessManagerImpl::GetAccessProhibitedReason() const {
+MultideviceFeatureAccessManagerImpl::AccessStatus
+MultideviceFeatureAccessManagerImpl::GetCameraRollAccessStatus() const {
+  int status = pref_service_->GetInteger(prefs::kCameraRollAccessStatus);
+  return static_cast<AccessStatus>(status);
+}
+
+MultideviceFeatureAccessManagerImpl::AccessProhibitedReason
+MultideviceFeatureAccessManagerImpl::GetNotificationAccessProhibitedReason()
+    const {
   int reason =
       pref_service_->GetInteger(prefs::kNotificationAccessProhibitedReason);
   return static_cast<AccessProhibitedReason>(reason);
 }
 
-void NotificationAccessManagerImpl::SetAccessStatusInternal(
+void MultideviceFeatureAccessManagerImpl::SetNotificationAccessStatusInternal(
     AccessStatus access_status,
     AccessProhibitedReason reason) {
   // TODO(http://crbug.com/1215559): Deprecate when there are no more active
@@ -95,7 +107,8 @@
                             false);
 
   PA_LOG(INFO) << "Notification access: "
-               << std::make_pair(GetAccessStatus(), GetAccessProhibitedReason())
+               << std::make_pair(GetNotificationAccessStatus(),
+                                 GetNotificationAccessProhibitedReason())
                << " => " << std::make_pair(access_status, reason);
 
   pref_service_->SetInteger(prefs::kNotificationAccessStatus,
@@ -123,7 +136,14 @@
   }
 }
 
-void NotificationAccessManagerImpl::OnSetupRequested() {
+void MultideviceFeatureAccessManagerImpl::SetCameraRollAccessStatusInternal(
+    AccessStatus camera_roll_access_status) {
+  pref_service_->SetInteger(prefs::kCameraRollAccessStatus,
+                            static_cast<int>(camera_roll_access_status));
+  NotifyCameraRollAccessChanged();
+}
+
+void MultideviceFeatureAccessManagerImpl::OnSetupRequested() {
   PA_LOG(INFO) << "Notification access setup flow started.";
 
   switch (feature_status_provider_->GetStatus()) {
@@ -150,7 +170,7 @@
   }
 }
 
-void NotificationAccessManagerImpl::OnFeatureStatusChanged() {
+void MultideviceFeatureAccessManagerImpl::OnFeatureStatusChanged() {
   if (!IsSetupOperationInProgress())
     return;
 
@@ -184,20 +204,21 @@
   }
 }
 
-void NotificationAccessManagerImpl::SendShowNotificationAccessSetupRequest() {
+void MultideviceFeatureAccessManagerImpl::
+    SendShowNotificationAccessSetupRequest() {
   message_sender_->SendShowNotificationAccessSetupRequest();
   SetNotificationSetupOperationStatus(
       NotificationAccessSetupOperation::Status::
           kSentMessageToPhoneAndWaitingForResponse);
 }
 
-bool NotificationAccessManagerImpl::HasAccessStatusChanged(
+bool MultideviceFeatureAccessManagerImpl::HasAccessStatusChanged(
     AccessStatus access_status,
     AccessProhibitedReason reason) {
-  if (access_status != GetAccessStatus())
+  if (access_status != GetNotificationAccessStatus())
     return true;
   if (access_status == AccessStatus::kProhibited &&
-      reason != GetAccessProhibitedReason()) {
+      reason != GetNotificationAccessProhibitedReason()) {
     return true;
   }
   return false;
diff --git a/ash/components/phonehub/multidevice_feature_access_manager_impl.h b/ash/components/phonehub/multidevice_feature_access_manager_impl.h
new file mode 100644
index 0000000..fe27814
--- /dev/null
+++ b/ash/components/phonehub/multidevice_feature_access_manager_impl.h
@@ -0,0 +1,72 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_COMPONENTS_PHONEHUB_MULTIDEVICE_FEATURE_ACCESS_MANAGER_IMPL_H_
+#define ASH_COMPONENTS_PHONEHUB_MULTIDEVICE_FEATURE_ACCESS_MANAGER_IMPL_H_
+
+#include "ash/components/phonehub/multidevice_feature_access_manager.h"
+
+#include "ash/components/phonehub/feature_status_provider.h"
+#include "ash/components/phonehub/message_receiver.h"
+
+class PrefRegistrySimple;
+class PrefService;
+
+namespace ash {
+namespace phonehub {
+
+class ConnectionScheduler;
+class MessageSender;
+
+// Implements MultideviceFeatureAccessManager by persisting the last-known
+// notification access status and camera roll access status to user prefs.
+class MultideviceFeatureAccessManagerImpl
+    : public MultideviceFeatureAccessManager,
+      public FeatureStatusProvider::Observer {
+ public:
+  static void RegisterPrefs(PrefRegistrySimple* registry);
+
+  explicit MultideviceFeatureAccessManagerImpl(
+      PrefService* pref_service,
+      FeatureStatusProvider* feature_status_provider,
+      MessageSender* message_sender,
+      ConnectionScheduler* connection_scheduler);
+  ~MultideviceFeatureAccessManagerImpl() override;
+
+ private:
+  friend class MultideviceFeatureAccessManagerImplTest;
+
+  // MultideviceFeatureAccessManager:
+  AccessStatus GetNotificationAccessStatus() const override;
+  AccessProhibitedReason GetNotificationAccessProhibitedReason() const override;
+  void SetNotificationAccessStatusInternal(
+      AccessStatus notification_access_status,
+      AccessProhibitedReason reason) override;
+  void SetCameraRollAccessStatusInternal(
+      AccessStatus camera_roll_access_status) override;
+  AccessStatus GetCameraRollAccessStatus() const override;
+  void OnSetupRequested() override;
+
+  bool HasMultideviceFeatureSetupUiBeenDismissed() const override;
+  void DismissSetupRequiredUi() override;
+
+  // FeatureStatusProvider::Observer:
+  void OnFeatureStatusChanged() override;
+
+  void SendShowNotificationAccessSetupRequest();
+
+  bool HasAccessStatusChanged(AccessStatus access_status,
+                              AccessProhibitedReason reason);
+
+  FeatureStatus current_feature_status_;
+  PrefService* pref_service_;
+  FeatureStatusProvider* feature_status_provider_;
+  MessageSender* message_sender_;
+  ConnectionScheduler* connection_scheduler_;
+};
+
+}  // namespace phonehub
+}  // namespace ash
+
+#endif  // ASH_COMPONENTS_PHONEHUB_MULTIDEVICE_FEATURE_ACCESS_MANAGER_IMPL_H_
diff --git a/ash/components/phonehub/multidevice_feature_access_manager_impl_unittest.cc b/ash/components/phonehub/multidevice_feature_access_manager_impl_unittest.cc
new file mode 100644
index 0000000..1b61c865
--- /dev/null
+++ b/ash/components/phonehub/multidevice_feature_access_manager_impl_unittest.cc
@@ -0,0 +1,660 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/components/phonehub/multidevice_feature_access_manager_impl.h"
+
+#include <memory>
+
+#include "ash/components/phonehub/fake_connection_scheduler.h"
+#include "ash/components/phonehub/fake_feature_status_provider.h"
+#include "ash/components/phonehub/fake_message_sender.h"
+#include "ash/components/phonehub/notification_access_setup_operation.h"
+#include "ash/components/phonehub/pref_names.h"
+#include "components/prefs/testing_pref_service.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace ash {
+namespace phonehub {
+namespace {
+
+class FakeObserver : public MultideviceFeatureAccessManager::Observer {
+ public:
+  FakeObserver() = default;
+  ~FakeObserver() override = default;
+
+  size_t num_calls() const { return num_calls_; }
+
+  // MultideviceFeatureAccessManager::Observer:
+  void OnNotificationAccessChanged() override { ++num_calls_; }
+
+  // MultideviceFeatureAccessManager::Observer:
+  void OnCameraRollAccessChanged() override { ++num_calls_; }
+
+ private:
+  size_t num_calls_ = 0;
+};
+
+class FakeOperationDelegate
+    : public NotificationAccessSetupOperation::Delegate {
+ public:
+  FakeOperationDelegate() = default;
+  ~FakeOperationDelegate() override = default;
+
+  NotificationAccessSetupOperation::Status status() const { return status_; }
+
+  // NotificationAccessSetupOperation::Delegate:
+  void OnStatusChange(
+      NotificationAccessSetupOperation::Status new_status) override {
+    status_ = new_status;
+  }
+
+ private:
+  NotificationAccessSetupOperation::Status status_ =
+      NotificationAccessSetupOperation::Status::kConnecting;
+};
+
+}  // namespace
+
+class MultideviceFeatureAccessManagerImplTest : public testing::Test {
+ protected:
+  MultideviceFeatureAccessManagerImplTest() = default;
+  MultideviceFeatureAccessManagerImplTest(
+      const MultideviceFeatureAccessManagerImplTest&) = delete;
+  MultideviceFeatureAccessManagerImplTest& operator=(
+      const MultideviceFeatureAccessManagerImplTest&) = delete;
+  ~MultideviceFeatureAccessManagerImplTest() override = default;
+
+  // testing::Test:
+  void SetUp() override {
+    MultideviceFeatureAccessManagerImpl::RegisterPrefs(
+        pref_service_.registry());
+    fake_feature_status_provider_ =
+        std::make_unique<FakeFeatureStatusProvider>();
+    fake_message_sender_ = std::make_unique<FakeMessageSender>();
+    fake_connection_scheduler_ = std::make_unique<FakeConnectionScheduler>();
+  }
+
+  void TearDown() override { manager_->RemoveObserver(&fake_observer_); }
+
+  void InitializeAccessStatus(
+      MultideviceFeatureAccessManager::AccessStatus
+          notification_expected_status,
+      MultideviceFeatureAccessManager::AccessStatus camera_roll_expected_status,
+      MultideviceFeatureAccessManager::AccessProhibitedReason reason =
+          MultideviceFeatureAccessManager::AccessProhibitedReason::kUnknown) {
+    pref_service_.SetInteger(prefs::kNotificationAccessStatus,
+                             static_cast<int>(notification_expected_status));
+    pref_service_.SetInteger(prefs::kCameraRollAccessStatus,
+                             static_cast<int>(camera_roll_expected_status));
+    pref_service_.SetInteger(prefs::kNotificationAccessProhibitedReason,
+                             static_cast<int>(reason));
+    SetNeedsOneTimeNotificationAccessUpdate(/*needs_update=*/false);
+    manager_ = std::make_unique<MultideviceFeatureAccessManagerImpl>(
+        &pref_service_, fake_feature_status_provider_.get(),
+        fake_message_sender_.get(), fake_connection_scheduler_.get());
+    manager_->AddObserver(&fake_observer_);
+  }
+
+  NotificationAccessSetupOperation::Status
+  GetNotificationAccessSetupOperationStatus() {
+    return fake_delegate_.status();
+  }
+
+  void VerifyNotificationAccessGrantedState(
+      MultideviceFeatureAccessManager::AccessStatus expected_status,
+      MultideviceFeatureAccessManager::AccessProhibitedReason expected_reason =
+          MultideviceFeatureAccessManager::AccessProhibitedReason::kUnknown) {
+    EXPECT_EQ(static_cast<int>(expected_status),
+              pref_service_.GetInteger(prefs::kNotificationAccessStatus));
+    EXPECT_EQ(expected_status, manager_->GetNotificationAccessStatus());
+    EXPECT_EQ(
+        static_cast<int>(expected_reason),
+        pref_service_.GetInteger(prefs::kNotificationAccessProhibitedReason));
+    EXPECT_EQ(expected_reason,
+              manager_->GetNotificationAccessProhibitedReason());
+  }
+
+  void VerifyCameraRollAccessGrantedState(
+      MultideviceFeatureAccessManager::AccessStatus expected_status) {
+    EXPECT_EQ(static_cast<int>(expected_status),
+              pref_service_.GetInteger(prefs::kCameraRollAccessStatus));
+    EXPECT_EQ(expected_status, manager_->GetCameraRollAccessStatus());
+  }
+
+  bool HasMultideviceFeatureSetupUiBeenDismissed() {
+    return manager_->HasMultideviceFeatureSetupUiBeenDismissed();
+  }
+
+  void DismissSetupRequiredUi() { manager_->DismissSetupRequiredUi(); }
+
+  std::unique_ptr<NotificationAccessSetupOperation> StartSetupOperation() {
+    return manager_->AttemptNotificationSetup(&fake_delegate_);
+  }
+
+  bool IsSetupOperationInProgress() {
+    return manager_->IsSetupOperationInProgress();
+  }
+
+  void SetNotificationAccessStatusInternal(
+      MultideviceFeatureAccessManager::AccessStatus status,
+      MultideviceFeatureAccessManager::AccessProhibitedReason reason =
+          MultideviceFeatureAccessManager::AccessProhibitedReason::kUnknown) {
+    manager_->SetNotificationAccessStatusInternal(status, reason);
+  }
+
+  void SetCameraRollAccessStatusInternal(
+      MultideviceFeatureAccessManager::AccessStatus status) {
+    manager_->SetCameraRollAccessStatusInternal(status);
+  }
+
+  void SetFeatureStatus(FeatureStatus status) {
+    fake_feature_status_provider_->SetStatus(status);
+  }
+
+  FeatureStatus GetFeatureStatus() {
+    return fake_feature_status_provider_->GetStatus();
+  }
+
+  size_t GetNumScheduleConnectionNowCalls() const {
+    return fake_connection_scheduler_->num_schedule_connection_now_calls();
+  }
+
+  size_t GetNumShowNotificationAccessSetupRequestCount() const {
+    return fake_message_sender_->show_notification_access_setup_request_count();
+  }
+
+  size_t GetNumObserverCalls() const { return fake_observer_.num_calls(); }
+
+  void SetNeedsOneTimeNotificationAccessUpdate(bool needs_update) {
+    pref_service_.SetBoolean(prefs::kNeedsOneTimeNotificationAccessUpdate,
+                             needs_update);
+  }
+
+ private:
+  TestingPrefServiceSimple pref_service_;
+
+  FakeObserver fake_observer_;
+  FakeOperationDelegate fake_delegate_;
+  std::unique_ptr<FakeFeatureStatusProvider> fake_feature_status_provider_;
+  std::unique_ptr<FakeMessageSender> fake_message_sender_;
+  std::unique_ptr<FakeConnectionScheduler> fake_connection_scheduler_;
+  std::unique_ptr<MultideviceFeatureAccessManager> manager_;
+};
+
+TEST_F(MultideviceFeatureAccessManagerImplTest, ShouldShowSetupRequiredUi) {
+  // Notification setup is not dismissed initially even when access has been
+  // granted.
+  InitializeAccessStatus(
+      MultideviceFeatureAccessManager::AccessStatus::kAccessGranted,
+      MultideviceFeatureAccessManager::AccessStatus::kAccessGranted);
+  EXPECT_FALSE(HasMultideviceFeatureSetupUiBeenDismissed());
+
+  // Notification setup is not dismissed initially when access has not been
+  // granted.
+  InitializeAccessStatus(
+      MultideviceFeatureAccessManager::AccessStatus::kAvailableButNotGranted,
+      MultideviceFeatureAccessManager::AccessStatus::kAccessGranted);
+  EXPECT_FALSE(HasMultideviceFeatureSetupUiBeenDismissed());
+
+  // Simlulate dismissal of UI.
+  DismissSetupRequiredUi();
+  EXPECT_TRUE(HasMultideviceFeatureSetupUiBeenDismissed());
+
+  // Dismissal value is persisted on initialization with access not granted.
+  InitializeAccessStatus(
+      MultideviceFeatureAccessManager::AccessStatus::kAvailableButNotGranted,
+      MultideviceFeatureAccessManager::AccessStatus::kAccessGranted);
+  EXPECT_TRUE(HasMultideviceFeatureSetupUiBeenDismissed());
+
+  // Dismissal value is persisted on initialization with access granted.
+  InitializeAccessStatus(
+      MultideviceFeatureAccessManager::AccessStatus::kAccessGranted,
+      MultideviceFeatureAccessManager::AccessStatus::kAccessGranted);
+  EXPECT_TRUE(HasMultideviceFeatureSetupUiBeenDismissed());
+}
+
+TEST_F(MultideviceFeatureAccessManagerImplTest, AllAccessInitiallyGranted) {
+  InitializeAccessStatus(
+      MultideviceFeatureAccessManager::AccessStatus::kAccessGranted,
+      MultideviceFeatureAccessManager::AccessStatus::kAccessGranted);
+  VerifyNotificationAccessGrantedState(
+      MultideviceFeatureAccessManager::AccessStatus::kAccessGranted);
+  VerifyCameraRollAccessGrantedState(
+      MultideviceFeatureAccessManager::AccessStatus::kAccessGranted);
+
+  // Cannot start the notification access setup flow if notification and camera
+  // roll access have already been granted.
+  auto operation = StartSetupOperation();
+  EXPECT_FALSE(operation);
+}
+
+TEST_F(MultideviceFeatureAccessManagerImplTest, OnFeatureStatusChanged) {
+  InitializeAccessStatus(
+      MultideviceFeatureAccessManager::AccessStatus::kAvailableButNotGranted,
+      MultideviceFeatureAccessManager::AccessStatus::kAvailableButNotGranted);
+  VerifyNotificationAccessGrantedState(
+      MultideviceFeatureAccessManager::AccessStatus::kAvailableButNotGranted);
+  VerifyCameraRollAccessGrantedState(
+      MultideviceFeatureAccessManager::AccessStatus::kAvailableButNotGranted);
+
+  // Set initial state to disconnected.
+  SetFeatureStatus(FeatureStatus::kEnabledButDisconnected);
+  EXPECT_EQ(0u, GetNumShowNotificationAccessSetupRequestCount());
+  EXPECT_EQ(NotificationAccessSetupOperation::Status::kConnecting,
+            GetNotificationAccessSetupOperationStatus());
+  // Simulate feature status to be enabled and connected. SetupOperation is
+  // also not in progress, so expect no new requests to be sent.
+  SetFeatureStatus(FeatureStatus::kEnabledAndConnected);
+  EXPECT_EQ(0u, GetNumShowNotificationAccessSetupRequestCount());
+  EXPECT_EQ(NotificationAccessSetupOperation::Status::kConnecting,
+            GetNotificationAccessSetupOperationStatus());
+  // Simulate setup operation is in progress. This will trigger a sent
+  // request.
+  auto operation = StartSetupOperation();
+  EXPECT_TRUE(operation);
+  EXPECT_EQ(1u, GetNumShowNotificationAccessSetupRequestCount());
+  EXPECT_EQ(NotificationAccessSetupOperation::Status::
+                kSentMessageToPhoneAndWaitingForResponse,
+            GetNotificationAccessSetupOperationStatus());
+
+  // Set another feature status, expect status to be updated.
+  SetFeatureStatus(FeatureStatus::kEnabledButDisconnected);
+  EXPECT_EQ(1u, GetNumShowNotificationAccessSetupRequestCount());
+  EXPECT_EQ(NotificationAccessSetupOperation::Status::kConnectionDisconnected,
+            GetNotificationAccessSetupOperationStatus());
+}
+
+TEST_F(MultideviceFeatureAccessManagerImplTest, StartDisconnectedAndNoAccess) {
+  // Set initial state to disconnected.
+  SetFeatureStatus(FeatureStatus::kEnabledButDisconnected);
+
+  InitializeAccessStatus(
+      MultideviceFeatureAccessManager::AccessStatus::kAvailableButNotGranted,
+      MultideviceFeatureAccessManager::AccessStatus::kAccessGranted);
+  VerifyNotificationAccessGrantedState(
+      MultideviceFeatureAccessManager::AccessStatus::kAvailableButNotGranted);
+  VerifyCameraRollAccessGrantedState(
+      MultideviceFeatureAccessManager::AccessStatus::kAccessGranted);
+
+  // Start a setup operation with enabled but disconnected status and access
+  // not granted.
+  auto operation = StartSetupOperation();
+  EXPECT_TRUE(operation);
+  EXPECT_EQ(1u, GetNumScheduleConnectionNowCalls());
+
+  // Simulate changing states from connecting to connected.
+  SetFeatureStatus(FeatureStatus::kEnabledAndConnecting);
+  SetFeatureStatus(FeatureStatus::kEnabledAndConnected);
+
+  // Verify that the request message has been sent and our operation status
+  // is updated.
+  EXPECT_EQ(1u, GetNumShowNotificationAccessSetupRequestCount());
+  EXPECT_EQ(NotificationAccessSetupOperation::Status::
+                kSentMessageToPhoneAndWaitingForResponse,
+            GetNotificationAccessSetupOperationStatus());
+
+  // Simulate getting a response back from the phone.
+  SetNotificationAccessStatusInternal(
+      MultideviceFeatureAccessManager::AccessStatus::kAccessGranted);
+  VerifyNotificationAccessGrantedState(
+      MultideviceFeatureAccessManager::AccessStatus::kAccessGranted);
+  EXPECT_EQ(NotificationAccessSetupOperation::Status::kCompletedSuccessfully,
+            GetNotificationAccessSetupOperationStatus());
+}
+
+TEST_F(MultideviceFeatureAccessManagerImplTest,
+       StartDisconnectedAndNoAccess_NotificationAccessIsProhibited) {
+  // Set initial state to disconnected.
+  SetFeatureStatus(FeatureStatus::kEnabledButDisconnected);
+
+  InitializeAccessStatus(
+      MultideviceFeatureAccessManager::AccessStatus::kAvailableButNotGranted,
+      MultideviceFeatureAccessManager::AccessStatus::kAvailableButNotGranted);
+  VerifyNotificationAccessGrantedState(
+      MultideviceFeatureAccessManager::AccessStatus::kAvailableButNotGranted);
+  VerifyCameraRollAccessGrantedState(
+      MultideviceFeatureAccessManager::AccessStatus::kAvailableButNotGranted);
+
+  // Start a setup operation with enabled but disconnected status and access
+  // not granted.
+  auto operation = StartSetupOperation();
+  EXPECT_TRUE(operation);
+  EXPECT_EQ(1u, GetNumScheduleConnectionNowCalls());
+
+  // Simulate changing states from connecting to connected.
+  SetFeatureStatus(FeatureStatus::kEnabledAndConnecting);
+  SetFeatureStatus(FeatureStatus::kEnabledAndConnected);
+
+  // Verify that the request message has been sent and our operation status
+  // is updated.
+  EXPECT_EQ(1u, GetNumShowNotificationAccessSetupRequestCount());
+  EXPECT_EQ(NotificationAccessSetupOperation::Status::
+                kSentMessageToPhoneAndWaitingForResponse,
+            GetNotificationAccessSetupOperationStatus());
+
+  // Simulate getting a response back from the phone.
+  SetNotificationAccessStatusInternal(
+      MultideviceFeatureAccessManager::AccessStatus::kProhibited);
+  VerifyNotificationAccessGrantedState(
+      MultideviceFeatureAccessManager::AccessStatus::kProhibited);
+  EXPECT_EQ(
+      NotificationAccessSetupOperation::Status::kProhibitedFromProvidingAccess,
+      GetNotificationAccessSetupOperationStatus());
+}
+
+TEST_F(MultideviceFeatureAccessManagerImplTest, StartConnectingAndNoAccess) {
+  // Set initial state to connecting.
+  SetFeatureStatus(FeatureStatus::kEnabledAndConnecting);
+
+  InitializeAccessStatus(
+      MultideviceFeatureAccessManager::AccessStatus::kAvailableButNotGranted,
+      MultideviceFeatureAccessManager::AccessStatus::kAvailableButNotGranted);
+  VerifyNotificationAccessGrantedState(
+      MultideviceFeatureAccessManager::AccessStatus::kAvailableButNotGranted);
+  VerifyCameraRollAccessGrantedState(
+      MultideviceFeatureAccessManager::AccessStatus::kAvailableButNotGranted);
+
+  // Start a setup operation with enabled and connecting status and access
+  // not granted.
+  auto operation = StartSetupOperation();
+  EXPECT_TRUE(operation);
+
+  // Simulate changing states from connecting to connected.
+  SetFeatureStatus(FeatureStatus::kEnabledAndConnected);
+
+  // Verify that the request message has been sent and our operation status
+  // is updated.
+  EXPECT_EQ(1u, GetNumShowNotificationAccessSetupRequestCount());
+  EXPECT_EQ(NotificationAccessSetupOperation::Status::
+                kSentMessageToPhoneAndWaitingForResponse,
+            GetNotificationAccessSetupOperationStatus());
+
+  // Simulate getting a response back from the phone.
+  SetNotificationAccessStatusInternal(
+      MultideviceFeatureAccessManager::AccessStatus::kAccessGranted);
+  VerifyNotificationAccessGrantedState(
+      MultideviceFeatureAccessManager::AccessStatus::kAccessGranted);
+  EXPECT_EQ(NotificationAccessSetupOperation::Status::kCompletedSuccessfully,
+            GetNotificationAccessSetupOperationStatus());
+}
+
+TEST_F(MultideviceFeatureAccessManagerImplTest, StartConnectedAndNoAccess) {
+  // Set initial state to connected.
+  SetFeatureStatus(FeatureStatus::kEnabledAndConnected);
+
+  InitializeAccessStatus(
+      MultideviceFeatureAccessManager::AccessStatus::kAvailableButNotGranted,
+      MultideviceFeatureAccessManager::AccessStatus::kAvailableButNotGranted);
+  VerifyNotificationAccessGrantedState(
+      MultideviceFeatureAccessManager::AccessStatus::kAvailableButNotGranted);
+  VerifyCameraRollAccessGrantedState(
+      MultideviceFeatureAccessManager::AccessStatus::kAvailableButNotGranted);
+
+  // Start a setup operation with enabled and connected status and access
+  // not granted.
+  auto operation = StartSetupOperation();
+  EXPECT_TRUE(operation);
+
+  // Verify that the request message has been sent and our operation status
+  // is updated.
+  EXPECT_EQ(1u, GetNumShowNotificationAccessSetupRequestCount());
+  EXPECT_EQ(NotificationAccessSetupOperation::Status::
+                kSentMessageToPhoneAndWaitingForResponse,
+            GetNotificationAccessSetupOperationStatus());
+
+  // Simulate getting a response back from the phone.
+  SetNotificationAccessStatusInternal(
+      MultideviceFeatureAccessManager::AccessStatus::kAccessGranted);
+  VerifyNotificationAccessGrantedState(
+      MultideviceFeatureAccessManager::AccessStatus::kAccessGranted);
+  EXPECT_EQ(NotificationAccessSetupOperation::Status::kCompletedSuccessfully,
+            GetNotificationAccessSetupOperationStatus());
+}
+
+TEST_F(MultideviceFeatureAccessManagerImplTest,
+       SimulateConnectingToDisconnected) {
+  // Set initial state to connecting.
+  SetFeatureStatus(FeatureStatus::kEnabledAndConnecting);
+
+  InitializeAccessStatus(
+      MultideviceFeatureAccessManager::AccessStatus::kAvailableButNotGranted,
+      MultideviceFeatureAccessManager::AccessStatus::kAvailableButNotGranted);
+  VerifyNotificationAccessGrantedState(
+      MultideviceFeatureAccessManager::AccessStatus::kAvailableButNotGranted);
+  VerifyCameraRollAccessGrantedState(
+      MultideviceFeatureAccessManager::AccessStatus::kAvailableButNotGranted);
+
+  auto operation = StartSetupOperation();
+  EXPECT_TRUE(operation);
+
+  // Simulate a disconnection and expect that status has been updated.
+  SetFeatureStatus(FeatureStatus::kEnabledButDisconnected);
+  EXPECT_EQ(NotificationAccessSetupOperation::Status::kTimedOutConnecting,
+            GetNotificationAccessSetupOperationStatus());
+}
+
+TEST_F(MultideviceFeatureAccessManagerImplTest,
+       SimulateConnectedToDisconnected) {
+  // Simulate connected state.
+  SetFeatureStatus(FeatureStatus::kEnabledAndConnected);
+
+  InitializeAccessStatus(
+      MultideviceFeatureAccessManager::AccessStatus::kAvailableButNotGranted,
+      MultideviceFeatureAccessManager::AccessStatus::kAvailableButNotGranted);
+  VerifyNotificationAccessGrantedState(
+      MultideviceFeatureAccessManager::AccessStatus::kAvailableButNotGranted);
+  VerifyCameraRollAccessGrantedState(
+      MultideviceFeatureAccessManager::AccessStatus::kAvailableButNotGranted);
+
+  auto operation = StartSetupOperation();
+  EXPECT_TRUE(operation);
+
+  EXPECT_EQ(1u, GetNumShowNotificationAccessSetupRequestCount());
+
+  // Simulate a disconnection, expect status update.
+  SetFeatureStatus(FeatureStatus::kEnabledButDisconnected);
+  EXPECT_EQ(NotificationAccessSetupOperation::Status::kConnectionDisconnected,
+            GetNotificationAccessSetupOperationStatus());
+}
+
+TEST_F(MultideviceFeatureAccessManagerImplTest, SimulateConnectedToDisabled) {
+  // Simulate connected state.
+  SetFeatureStatus(FeatureStatus::kEnabledAndConnected);
+
+  InitializeAccessStatus(
+      MultideviceFeatureAccessManager::AccessStatus::kAvailableButNotGranted,
+      MultideviceFeatureAccessManager::AccessStatus::kAvailableButNotGranted);
+  VerifyNotificationAccessGrantedState(
+      MultideviceFeatureAccessManager::AccessStatus::kAvailableButNotGranted);
+  VerifyCameraRollAccessGrantedState(
+      MultideviceFeatureAccessManager::AccessStatus::kAvailableButNotGranted);
+
+  auto operation = StartSetupOperation();
+  EXPECT_TRUE(operation);
+
+  EXPECT_EQ(1u, GetNumShowNotificationAccessSetupRequestCount());
+
+  // Simulate disabling the feature, expect status update.
+  SetFeatureStatus(FeatureStatus::kDisabled);
+  EXPECT_EQ(NotificationAccessSetupOperation::Status::kConnectionDisconnected,
+            GetNotificationAccessSetupOperationStatus());
+}
+
+TEST_F(MultideviceFeatureAccessManagerImplTest,
+       FlipNotificationAccessGrantedToNotGranted) {
+  InitializeAccessStatus(
+      MultideviceFeatureAccessManager::AccessStatus::kAccessGranted,
+      MultideviceFeatureAccessManager::AccessStatus::kAccessGranted);
+  VerifyNotificationAccessGrantedState(
+      MultideviceFeatureAccessManager::AccessStatus::kAccessGranted);
+  VerifyCameraRollAccessGrantedState(
+      MultideviceFeatureAccessManager::AccessStatus::kAccessGranted);
+
+  // Simulate flipping notification access state to no granted.
+  SetNotificationAccessStatusInternal(
+      MultideviceFeatureAccessManager::AccessStatus::kAvailableButNotGranted);
+  VerifyNotificationAccessGrantedState(
+      MultideviceFeatureAccessManager::AccessStatus::kAvailableButNotGranted);
+  EXPECT_EQ(1u, GetNumObserverCalls());
+}
+
+TEST_F(MultideviceFeatureAccessManagerImplTest,
+       FlipNotificationAccessGrantedToProhibited) {
+  InitializeAccessStatus(
+      MultideviceFeatureAccessManager::AccessStatus::kAccessGranted,
+      MultideviceFeatureAccessManager::AccessStatus::kAccessGranted);
+  VerifyNotificationAccessGrantedState(
+      MultideviceFeatureAccessManager::AccessStatus::kAccessGranted);
+  VerifyCameraRollAccessGrantedState(
+      MultideviceFeatureAccessManager::AccessStatus::kAccessGranted);
+
+  // Simulate flipping notification access state to prohibited.
+  SetNotificationAccessStatusInternal(
+      MultideviceFeatureAccessManager::AccessStatus::kProhibited);
+  VerifyNotificationAccessGrantedState(
+      MultideviceFeatureAccessManager::AccessStatus::kProhibited);
+  EXPECT_EQ(1u, GetNumObserverCalls());
+}
+
+TEST_F(MultideviceFeatureAccessManagerImplTest,
+       FlipCameraRollAccessGrantedToNotGranted) {
+  InitializeAccessStatus(
+      MultideviceFeatureAccessManager::AccessStatus::kAccessGranted,
+      MultideviceFeatureAccessManager::AccessStatus::kAccessGranted);
+  VerifyNotificationAccessGrantedState(
+      MultideviceFeatureAccessManager::AccessStatus::kAccessGranted);
+  VerifyCameraRollAccessGrantedState(
+      MultideviceFeatureAccessManager::AccessStatus::kAccessGranted);
+
+  // Simulate flipping camera roll access state to no granted.
+  SetCameraRollAccessStatusInternal(
+      MultideviceFeatureAccessManager::AccessStatus::kAvailableButNotGranted);
+  VerifyCameraRollAccessGrantedState(
+      MultideviceFeatureAccessManager::AccessStatus::kAvailableButNotGranted);
+  EXPECT_EQ(1u, GetNumObserverCalls());
+}
+
+TEST_F(MultideviceFeatureAccessManagerImplTest, AccessNotChanged) {
+  InitializeAccessStatus(
+      MultideviceFeatureAccessManager::AccessStatus::kAccessGranted,
+      MultideviceFeatureAccessManager::AccessStatus::kAccessGranted);
+  VerifyNotificationAccessGrantedState(
+      MultideviceFeatureAccessManager::AccessStatus::kAccessGranted);
+  VerifyCameraRollAccessGrantedState(
+      MultideviceFeatureAccessManager::AccessStatus::kAccessGranted);
+
+  // If the access state is unchanged, we do not expect any notifications.
+  SetNotificationAccessStatusInternal(
+      MultideviceFeatureAccessManager::AccessStatus::kAccessGranted);
+  VerifyNotificationAccessGrantedState(
+      MultideviceFeatureAccessManager::AccessStatus::kAccessGranted);
+  EXPECT_EQ(0u, GetNumObserverCalls());
+}
+
+TEST_F(MultideviceFeatureAccessManagerImplTest,
+       NeedsOneTimeNotificationAccessUpdate_AccessGranted) {
+  InitializeAccessStatus(
+      MultideviceFeatureAccessManager::AccessStatus::kAccessGranted,
+      MultideviceFeatureAccessManager::AccessStatus::kAccessGranted);
+  VerifyNotificationAccessGrantedState(
+      MultideviceFeatureAccessManager::AccessStatus::kAccessGranted);
+  VerifyCameraRollAccessGrantedState(
+      MultideviceFeatureAccessManager::AccessStatus::kAccessGranted);
+
+  // Send a one-time signal to observers if access is granted. See
+  // http://crbug.com/1215559.
+  SetNeedsOneTimeNotificationAccessUpdate(/*needs_update=*/true);
+  SetNotificationAccessStatusInternal(
+      MultideviceFeatureAccessManager::AccessStatus::kAccessGranted);
+  VerifyNotificationAccessGrantedState(
+      MultideviceFeatureAccessManager::AccessStatus::kAccessGranted);
+  EXPECT_EQ(1u, GetNumObserverCalls());
+
+  // Observers should be notified only once ever.
+  SetNotificationAccessStatusInternal(
+      MultideviceFeatureAccessManager::AccessStatus::kAccessGranted);
+  VerifyNotificationAccessGrantedState(
+      MultideviceFeatureAccessManager::AccessStatus::kAccessGranted);
+  EXPECT_EQ(1u, GetNumObserverCalls());
+}
+
+TEST_F(MultideviceFeatureAccessManagerImplTest,
+       NeedsOneTimeNotificationAccessUpdate_Prohibited) {
+  InitializeAccessStatus(
+      MultideviceFeatureAccessManager::AccessStatus::kProhibited,
+      MultideviceFeatureAccessManager::AccessStatus::kAccessGranted);
+  VerifyNotificationAccessGrantedState(
+      MultideviceFeatureAccessManager::AccessStatus::kProhibited);
+  VerifyCameraRollAccessGrantedState(
+      MultideviceFeatureAccessManager::AccessStatus::kAccessGranted);
+
+  // Only send the one-time signal to observers if access is granted. See
+  // http://crbug.com/1215559.
+  SetNeedsOneTimeNotificationAccessUpdate(/*needs_update=*/true);
+  SetNotificationAccessStatusInternal(
+      MultideviceFeatureAccessManager::AccessStatus::kProhibited);
+  VerifyNotificationAccessGrantedState(
+      MultideviceFeatureAccessManager::AccessStatus::kProhibited);
+  EXPECT_EQ(0u, GetNumObserverCalls());
+}
+
+TEST_F(MultideviceFeatureAccessManagerImplTest,
+       NotificationAccessProhibitedReason_FromProhibited) {
+  InitializeAccessStatus(
+      MultideviceFeatureAccessManager::AccessStatus::kProhibited,
+      MultideviceFeatureAccessManager::AccessStatus::kAccessGranted);
+  VerifyNotificationAccessGrantedState(
+      MultideviceFeatureAccessManager::AccessStatus::kProhibited);
+
+  // Simulates an initial update after the pref is first added
+  SetNotificationAccessStatusInternal(
+      MultideviceFeatureAccessManager::AccessStatus::kProhibited,
+      MultideviceFeatureAccessManager::AccessProhibitedReason::kWorkProfile);
+  VerifyNotificationAccessGrantedState(
+      MultideviceFeatureAccessManager::AccessStatus::kProhibited,
+      MultideviceFeatureAccessManager::AccessProhibitedReason::kWorkProfile);
+  EXPECT_EQ(1u, GetNumObserverCalls());
+
+  // No update or observer notification should occur with no change
+  SetNotificationAccessStatusInternal(
+      MultideviceFeatureAccessManager::AccessStatus::kProhibited,
+      MultideviceFeatureAccessManager::AccessProhibitedReason::kWorkProfile);
+  VerifyNotificationAccessGrantedState(
+      MultideviceFeatureAccessManager::AccessStatus::kProhibited,
+      MultideviceFeatureAccessManager::AccessProhibitedReason::kWorkProfile);
+  EXPECT_EQ(1u, GetNumObserverCalls());
+
+  // This can happen if a user updates from Android <N to >=N
+  SetNotificationAccessStatusInternal(
+      MultideviceFeatureAccessManager::AccessStatus::kProhibited,
+      MultideviceFeatureAccessManager::AccessProhibitedReason::
+          kDisabledByPhonePolicy);
+  VerifyNotificationAccessGrantedState(
+      MultideviceFeatureAccessManager::AccessStatus::kProhibited,
+      MultideviceFeatureAccessManager::AccessProhibitedReason::
+          kDisabledByPhonePolicy);
+  EXPECT_EQ(2u, GetNumObserverCalls());
+}
+
+TEST_F(MultideviceFeatureAccessManagerImplTest,
+       NotificationAccessProhibitedReason_FromGranted) {
+  InitializeAccessStatus(
+      MultideviceFeatureAccessManager::AccessStatus::kAccessGranted,
+      MultideviceFeatureAccessManager::AccessStatus::kAccessGranted);
+  VerifyNotificationAccessGrantedState(
+      MultideviceFeatureAccessManager::AccessStatus::kAccessGranted);
+
+  SetNotificationAccessStatusInternal(
+      MultideviceFeatureAccessManager::AccessStatus::kProhibited,
+      MultideviceFeatureAccessManager::AccessProhibitedReason::
+          kDisabledByPhonePolicy);
+  VerifyNotificationAccessGrantedState(
+      MultideviceFeatureAccessManager::AccessStatus::kProhibited,
+      MultideviceFeatureAccessManager::AccessProhibitedReason::
+          kDisabledByPhonePolicy);
+  EXPECT_EQ(1u, GetNumObserverCalls());
+}
+
+}  // namespace phonehub
+}  // namespace ash
diff --git a/ash/components/phonehub/multidevice_setup_state_updater.cc b/ash/components/phonehub/multidevice_setup_state_updater.cc
index cba5d3a1d..d999dc8 100644
--- a/ash/components/phonehub/multidevice_setup_state_updater.cc
+++ b/ash/components/phonehub/multidevice_setup_state_updater.cc
@@ -31,22 +31,22 @@
 MultideviceSetupStateUpdater::MultideviceSetupStateUpdater(
     PrefService* pref_service,
     multidevice_setup::MultiDeviceSetupClient* multidevice_setup_client,
-    NotificationAccessManager* notification_access_manager)
+    MultideviceFeatureAccessManager* multidevice_feature_access_manager)
     : pref_service_(pref_service),
       multidevice_setup_client_(multidevice_setup_client),
-      notification_access_manager_(notification_access_manager) {
+      multidevice_feature_access_manager_(multidevice_feature_access_manager) {
   multidevice_setup_client_->AddObserver(this);
-  notification_access_manager_->AddObserver(this);
+  multidevice_feature_access_manager_->AddObserver(this);
 }
 
 MultideviceSetupStateUpdater::~MultideviceSetupStateUpdater() {
   multidevice_setup_client_->RemoveObserver(this);
-  notification_access_manager_->RemoveObserver(this);
+  multidevice_feature_access_manager_->RemoveObserver(this);
 }
 
 void MultideviceSetupStateUpdater::OnNotificationAccessChanged() {
-  switch (notification_access_manager_->GetAccessStatus()) {
-    case NotificationAccessManager::AccessStatus::kAccessGranted:
+  switch (multidevice_feature_access_manager_->GetNotificationAccessStatus()) {
+    case MultideviceFeatureAccessManager::AccessStatus::kAccessGranted:
       if (IsWaitingForAccessToInitiallyEnableNotifications()) {
         PA_LOG(INFO) << "Enabling PhoneHubNotifications for the first time now "
                      << "that access has been granted by the phone.";
@@ -56,9 +56,9 @@
       }
       break;
 
-    case NotificationAccessManager::AccessStatus::kAvailableButNotGranted:
+    case MultideviceFeatureAccessManager::AccessStatus::kAvailableButNotGranted:
       [[fallthrough]];
-    case NotificationAccessManager::AccessStatus::kProhibited:
+    case MultideviceFeatureAccessManager::AccessStatus::kProhibited:
       // Disable kPhoneHubNotifications if notification access has been revoked
       // by the phone.
       PA_LOG(INFO) << "Disabling PhoneHubNotifications feature.";
@@ -69,6 +69,26 @@
   }
 }
 
+void MultideviceSetupStateUpdater::OnCameraRollAccessChanged() {
+  switch (multidevice_feature_access_manager_->GetCameraRollAccessStatus()) {
+    case MultideviceFeatureAccessManager::AccessStatus::kAccessGranted:
+      if (IsWaitingForAccessToInitiallyEnableCameraRoll()) {
+        multidevice_setup_client_->SetFeatureEnabledState(
+            Feature::kPhoneHubCameraRoll, /*enabled=*/true,
+            /*auth_token=*/absl::nullopt, base::DoNothing());
+      }
+      break;
+
+    case MultideviceFeatureAccessManager::AccessStatus::kAvailableButNotGranted:
+      [[fallthrough]];
+    case MultideviceFeatureAccessManager::AccessStatus::kProhibited:
+      multidevice_setup_client_->SetFeatureEnabledState(
+          Feature::kPhoneHubCameraRoll, /*enabled=*/false,
+          /*auth_token=*/absl::nullopt, base::DoNothing());
+      break;
+  }
+}
+
 void MultideviceSetupStateUpdater::OnHostStatusChanged(
     const multidevice_setup::MultiDeviceSetupClient::HostStatusWithDevice&
         host_device_with_status) {
@@ -87,14 +107,28 @@
   // should enable it after
   //   1. the top-level Phone Hub feature is enabled, and
   //   2. the phone has granted access.
-  // We do *not* want disrupt the feature state if it was already explicitly set
-  // by the user.
+  // We do *not* want to automatically enable the feature unless the opt-in flow
+  // was triggered from this device
   return chromeos::multidevice_setup::IsDefaultFeatureEnabledValue(
              Feature::kPhoneHubNotifications, pref_service_) &&
          multidevice_setup_client_->GetFeatureState(Feature::kPhoneHub) ==
              FeatureState::kEnabledByUser;
 }
 
+bool MultideviceSetupStateUpdater::
+    IsWaitingForAccessToInitiallyEnableCameraRoll() const {
+  // If the Phone Hub camera roll feature has never been explicitly set, we
+  // should enable it after
+  //   1. the top-level Phone Hub feature is enabled, and
+  //   2. the phone has granted access.
+  // We do *not* want to automatically enable the feature unless the opt-in flow
+  // was triggered from this device
+  return chromeos::multidevice_setup::IsDefaultFeatureEnabledValue(
+             Feature::kPhoneHubCameraRoll, pref_service_) &&
+         multidevice_setup_client_->GetFeatureState(Feature::kPhoneHub) ==
+             FeatureState::kEnabledByUser;
+}
+
 void MultideviceSetupStateUpdater::EnablePhoneHubIfAwaitingVerifiedHost() {
   bool is_awaiting_verified_host =
       pref_service_->GetBoolean(prefs::kIsAwaitingVerifiedHost);
diff --git a/ash/components/phonehub/multidevice_setup_state_updater.h b/ash/components/phonehub/multidevice_setup_state_updater.h
index 658ae9b..43163677 100644
--- a/ash/components/phonehub/multidevice_setup_state_updater.h
+++ b/ash/components/phonehub/multidevice_setup_state_updater.h
@@ -5,7 +5,7 @@
 #ifndef ASH_COMPONENTS_PHONEHUB_MULTIDEVICE_SETUP_STATE_UPDATER_H_
 #define ASH_COMPONENTS_PHONEHUB_MULTIDEVICE_SETUP_STATE_UPDATER_H_
 
-#include "ash/components/phonehub/notification_access_manager.h"
+#include "ash/components/phonehub/multidevice_feature_access_manager.h"
 #include "ash/services/multidevice_setup/public/cpp/multidevice_setup_client.h"
 
 class PrefRegistrySimple;
@@ -18,17 +18,17 @@
 // the Phone Hub feature. This intent to enable the feature is persisted across
 // restarts. This class also disables the PhoneHubNotification Multidevice
 // feature state when Notification access has been revoked by the phone,
-// provided via NotificationAccessManager.
+// provided via MultideviceFeatureAccessManager.
 class MultideviceSetupStateUpdater
     : public multidevice_setup::MultiDeviceSetupClient::Observer,
-      public NotificationAccessManager::Observer {
+      public MultideviceFeatureAccessManager::Observer {
  public:
   static void RegisterPrefs(PrefRegistrySimple* registry);
 
   MultideviceSetupStateUpdater(
       PrefService* pref_service,
       multidevice_setup::MultiDeviceSetupClient* multidevice_setup_client,
-      NotificationAccessManager* notification_access_manager);
+      MultideviceFeatureAccessManager* multidevice_feature_access_manager);
   ~MultideviceSetupStateUpdater() override;
 
  private:
@@ -40,16 +40,18 @@
       const multidevice_setup::MultiDeviceSetupClient::FeatureStatesMap&
           feature_state_map) override;
 
-  // NotificationAccessManager::Observer:
+  // MultideviceFeatureAccessManager::Observer:
   void OnNotificationAccessChanged() override;
+  void OnCameraRollAccessChanged() override;
 
   bool IsWaitingForAccessToInitiallyEnableNotifications() const;
+  bool IsWaitingForAccessToInitiallyEnableCameraRoll() const;
   void EnablePhoneHubIfAwaitingVerifiedHost();
   void UpdateIsAwaitingVerifiedHost();
 
   PrefService* pref_service_;
   multidevice_setup::MultiDeviceSetupClient* multidevice_setup_client_;
-  NotificationAccessManager* notification_access_manager_;
+  MultideviceFeatureAccessManager* multidevice_feature_access_manager_;
 };
 
 }  // namespace phonehub
diff --git a/ash/components/phonehub/multidevice_setup_state_updater_unittest.cc b/ash/components/phonehub/multidevice_setup_state_updater_unittest.cc
index 99e492e..6b00325 100644
--- a/ash/components/phonehub/multidevice_setup_state_updater_unittest.cc
+++ b/ash/components/phonehub/multidevice_setup_state_updater_unittest.cc
@@ -4,7 +4,7 @@
 
 #include "ash/components/phonehub/multidevice_setup_state_updater.h"
 
-#include "ash/components/phonehub/fake_notification_access_manager.h"
+#include "ash/components/phonehub/fake_multidevice_feature_access_manager.h"
 #include "ash/services/multidevice_setup/public/cpp/fake_multidevice_setup_client.h"
 #include "ash/services/multidevice_setup/public/cpp/prefs.h"
 #include "components/prefs/testing_pref_service.h"
@@ -45,16 +45,25 @@
   void CreateUpdater() {
     updater_ = std::make_unique<MultideviceSetupStateUpdater>(
         &pref_service_, &fake_multidevice_setup_client_,
-        &fake_notification_access_manager_);
+        &fake_multidevice_feature_access_manager_);
   }
 
   void DestroyUpdater() { updater_.reset(); }
 
   void SetNotificationAccess(bool enabled) {
-    fake_notification_access_manager_.SetAccessStatusInternal(
-        enabled
-            ? NotificationAccessManager::AccessStatus::kAccessGranted
-            : NotificationAccessManager::AccessStatus::kAvailableButNotGranted);
+    fake_multidevice_feature_access_manager_
+        .SetNotificationAccessStatusInternal(
+            enabled
+                ? MultideviceFeatureAccessManager::AccessStatus::kAccessGranted
+                : MultideviceFeatureAccessManager::AccessStatus::
+                      kAvailableButNotGranted);
+  }
+
+  void SetCameraRollAccess(bool enabled) {
+    fake_multidevice_feature_access_manager_.SetCameraRollAccessStatusInternal(
+        enabled ? MultideviceFeatureAccessManager::AccessStatus::kAccessGranted
+                : MultideviceFeatureAccessManager::AccessStatus::
+                      kAvailableButNotGranted);
   }
 
   void SetFeatureState(Feature feature, FeatureState feature_state) {
@@ -76,7 +85,7 @@
 
   TestingPrefServiceSimple pref_service_;
   multidevice_setup::FakeMultiDeviceSetupClient fake_multidevice_setup_client_;
-  FakeNotificationAccessManager fake_notification_access_manager_;
+  FakeMultideviceFeatureAccessManager fake_multidevice_feature_access_manager_;
 };
 
 TEST_F(MultideviceSetupStateUpdaterTest, EnablePhoneHub) {
@@ -267,5 +276,49 @@
       fake_multidevice_setup_client()->NumPendingSetFeatureEnabledStateCalls());
 }
 
+TEST_F(MultideviceSetupStateUpdaterTest, InitiallyEnableCameraRoll) {
+  SetCameraRollAccess(false);
+  SetFeatureState(Feature::kPhoneHub, FeatureState::kEnabledByUser);
+  CreateUpdater();
+
+  // If the camera roll feature has not been explicitly set yet, enable it
+  // when Phone Hub is enabled and access has been granted.
+  SetCameraRollAccess(true);
+  fake_multidevice_setup_client()->InvokePendingSetFeatureEnabledStateCallback(
+      /*expected_feature=*/Feature::kPhoneHubCameraRoll,
+      /*expected_enabled=*/true, /*expected_auth_token=*/absl::nullopt,
+      /*success=*/true);
+}
+
+TEST_F(MultideviceSetupStateUpdaterTest,
+       InitiallyEnableCameraRoll_DisablePhoneHub) {
+  SetCameraRollAccess(false);
+  SetFeatureState(Feature::kPhoneHub, FeatureState::kEnabledByUser);
+
+  // Explicitly disable Phone Hub, all sub feature should be disabled
+  SetFeatureState(Feature::kPhoneHub, FeatureState::kDisabledByUser);
+
+  CreateUpdater();
+
+  // No action after access is granted
+  SetCameraRollAccess(true);
+  EXPECT_EQ(
+      0u,
+      fake_multidevice_setup_client()->NumPendingSetFeatureEnabledStateCalls());
+}
+
+TEST_F(MultideviceSetupStateUpdaterTest, RevokePhoneHubCameraRollAccess) {
+  SetCameraRollAccess(true);
+  CreateUpdater();
+
+  // Test that there is a call to disable kPhoneHubCameraRoll when camera roll
+  // access has been revoked.
+  SetCameraRollAccess(false);
+  fake_multidevice_setup_client()->InvokePendingSetFeatureEnabledStateCallback(
+      /*expected_feature=*/Feature::kPhoneHubCameraRoll,
+      /*expected_enabled=*/false, /*expected_auth_token=*/absl::nullopt,
+      /*success=*/true);
+}
+
 }  // namespace phonehub
 }  // namespace ash
diff --git a/ash/components/phonehub/notification_access_manager.cc b/ash/components/phonehub/notification_access_manager.cc
deleted file mode 100644
index 953a0ef..0000000
--- a/ash/components/phonehub/notification_access_manager.cc
+++ /dev/null
@@ -1,128 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ash/components/phonehub/notification_access_manager.h"
-
-#include "base/bind.h"
-#include "base/memory/ptr_util.h"
-#include "chromeos/components/multidevice/logging/logging.h"
-
-namespace ash {
-namespace phonehub {
-
-NotificationAccessManager::NotificationAccessManager() = default;
-
-NotificationAccessManager::~NotificationAccessManager() = default;
-
-std::unique_ptr<NotificationAccessSetupOperation>
-NotificationAccessManager::AttemptNotificationSetup(
-    NotificationAccessSetupOperation::Delegate* delegate) {
-  // Should only be able to start the setup process if notification access is
-  // available but not yet granted.
-  if (GetAccessStatus() != AccessStatus::kAvailableButNotGranted)
-    return nullptr;
-
-  int operation_id = next_operation_id_;
-  ++next_operation_id_;
-
-  auto operation = base::WrapUnique(new NotificationAccessSetupOperation(
-      delegate,
-      base::BindOnce(&NotificationAccessManager::OnSetupOperationDeleted,
-                     weak_ptr_factory_.GetWeakPtr(), operation_id)));
-  id_to_operation_map_.emplace(operation_id, operation.get());
-
-  OnSetupRequested();
-  return operation;
-}
-
-void NotificationAccessManager::AddObserver(Observer* observer) {
-  observer_list_.AddObserver(observer);
-}
-
-void NotificationAccessManager::RemoveObserver(Observer* observer) {
-  observer_list_.RemoveObserver(observer);
-}
-
-void NotificationAccessManager::NotifyNotificationAccessChanged() {
-  for (auto& observer : observer_list_)
-    observer.OnNotificationAccessChanged();
-}
-
-void NotificationAccessManager::SetNotificationSetupOperationStatus(
-    NotificationAccessSetupOperation::Status new_status) {
-  DCHECK(IsSetupOperationInProgress());
-
-  PA_LOG(INFO) << "Notification access setup flow - new status: " << new_status;
-
-  for (auto& it : id_to_operation_map_)
-    it.second->NotifyStatusChanged(new_status);
-
-  if (NotificationAccessSetupOperation::IsFinalStatus(new_status))
-    id_to_operation_map_.clear();
-}
-
-bool NotificationAccessManager::IsSetupOperationInProgress() const {
-  return !id_to_operation_map_.empty();
-}
-
-void NotificationAccessManager::OnSetupOperationDeleted(int operation_id) {
-  auto it = id_to_operation_map_.find(operation_id);
-  if (it == id_to_operation_map_.end())
-    return;
-
-  id_to_operation_map_.erase(it);
-
-  if (id_to_operation_map_.empty())
-    PA_LOG(INFO) << "Notification access setup operation has ended.";
-}
-
-std::ostream& operator<<(std::ostream& stream,
-                         NotificationAccessManager::AccessStatus status) {
-  switch (status) {
-    case NotificationAccessManager::AccessStatus::kProhibited:
-      stream << "[Access prohibited]";
-      break;
-    case NotificationAccessManager::AccessStatus::kAvailableButNotGranted:
-      stream << "[Access available but not granted]";
-      break;
-    case NotificationAccessManager::AccessStatus::kAccessGranted:
-      stream << "[Access granted]";
-      break;
-  }
-  return stream;
-}
-
-std::ostream& operator<<(
-    std::ostream& stream,
-    NotificationAccessManager::AccessProhibitedReason reason) {
-  switch (reason) {
-    case NotificationAccessManager::AccessProhibitedReason::kUnknown:
-      stream << "[Unknown]";
-      break;
-    case NotificationAccessManager::AccessProhibitedReason::kWorkProfile:
-      stream << "[Work Profile]";
-      break;
-    case NotificationAccessManager::AccessProhibitedReason::
-        kDisabledByPhonePolicy:
-      stream << "[Admin Policy]";
-      break;
-  }
-  return stream;
-}
-
-std::ostream& operator<<(
-    std::ostream& stream,
-    std::pair<NotificationAccessManager::AccessStatus,
-              NotificationAccessManager::AccessProhibitedReason>
-        status_reason) {
-  stream << status_reason.first;
-  if (status_reason.first ==
-      NotificationAccessManager::AccessStatus::kProhibited) {
-    stream << "," << status_reason.second;
-  }
-  return stream;
-}
-
-}  // namespace phonehub
-}  // namespace ash
diff --git a/ash/components/phonehub/notification_access_manager.h b/ash/components/phonehub/notification_access_manager.h
deleted file mode 100644
index 2a6433b6..0000000
--- a/ash/components/phonehub/notification_access_manager.h
+++ /dev/null
@@ -1,146 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef ASH_COMPONENTS_PHONEHUB_NOTIFICATION_ACCESS_MANAGER_H_
-#define ASH_COMPONENTS_PHONEHUB_NOTIFICATION_ACCESS_MANAGER_H_
-
-#include <ostream>
-
-#include "ash/components/phonehub/notification_access_setup_operation.h"
-#include "base/containers/flat_map.h"
-#include "base/memory/weak_ptr.h"
-#include "base/observer_list.h"
-#include "base/observer_list_types.h"
-
-namespace ash {
-namespace phonehub {
-
-// Tracks the status of whether the user has enabled notification access on
-// their phone. While Phone Hub can be enabled via Chrome OS, access to
-// notifications requires that the user grant access via Android settings. If a
-// Phone Hub connection to the phone has never succeeded, we assume that access
-// has not yet been granted. If there is no active Phone Hub connection, we
-// assume that the last access value seen is the current value.
-//
-// Additionally, this class provides an API for requesting the notification
-// access setup flow via AttemptNotificationSetup().
-class NotificationAccessManager {
- public:
-  // Status of notification access. Numerical values are stored in prefs and
-  // should not be changed or reused.
-  enum class AccessStatus {
-    // Access has not been granted and is prohibited from being granted (e.g.,
-    // if the phone is using a Work Profile).
-    kProhibited = 0,
-
-    // Access has not been granted, but the user is free to grant access.
-    kAvailableButNotGranted = 1,
-
-    // Access has been granted by the user.
-    kAccessGranted = 2
-  };
-
-  enum class AccessProhibitedReason {
-    // Access is either not prohibited or is unset. Use as a safe default value.
-    kUnknown = 0,
-    // Access is prohibited because the phone is using a Work Profile and on
-    // Android version <N.
-    kWorkProfile = 1,
-    // Access is prohibited because the phone is using a Work Profile, and the
-    // policy managing the phone disables access.
-    kDisabledByPhonePolicy = 2
-  };
-
-  class Observer : public base::CheckedObserver {
-   public:
-    ~Observer() override = default;
-
-    // Called when notification access has changed; use GetAccessStatus()
-    // for the new status.
-    virtual void OnNotificationAccessChanged() = 0;
-  };
-
-  NotificationAccessManager(const NotificationAccessManager&) = delete;
-  NotificationAccessManager& operator=(const NotificationAccessManager&) =
-      delete;
-  virtual ~NotificationAccessManager();
-
-  virtual AccessStatus GetAccessStatus() const = 0;
-
-  // Returns the reason notification access status is prohibited. The return
-  // result is valid if the current access status (from GetAccessStatus())
-  // is AccessStatus::kProhibited. Otherwise, the result is undefined and should
-  // not be used.
-  virtual AccessProhibitedReason GetAccessProhibitedReason() const = 0;
-
-  virtual bool HasNotificationSetupUiBeenDismissed() const = 0;
-
-  // Disables the ability to show the banner within the PhoneHub UI.
-  virtual void DismissSetupRequiredUi() = 0;
-
-  // Starts an attempt to enable the notification access. |delegate| will be
-  // updated with the status of the flow as long as the operation object
-  // returned by this function remains instantiated.
-  //
-  // To cancel an ongoing setup attempt, delete the operation. If a setup
-  // attempt fails, clients can retry by calling AttemptNotificationSetup()
-  // again to start a new attempt.
-  //
-  // If notification access has already been granted, this function returns null
-  // since there is nothing to set up.
-  std::unique_ptr<NotificationAccessSetupOperation> AttemptNotificationSetup(
-      NotificationAccessSetupOperation::Delegate* delegate);
-
-  void AddObserver(Observer* observer);
-  void RemoveObserver(Observer* observer);
-
- protected:
-  NotificationAccessManager();
-
-  void NotifyNotificationAccessChanged();
-  void SetNotificationSetupOperationStatus(
-      NotificationAccessSetupOperation::Status new_status);
-
-  bool IsSetupOperationInProgress() const;
-
-  virtual void OnSetupRequested() {}
-
- private:
-  friend class NotificationAccessManagerImplTest;
-  friend class PhoneStatusProcessor;
-
-  // Sets the internal AccessStatus but does not send a request for a new
-  // status to the remote phone device.
-  virtual void SetAccessStatusInternal(AccessStatus access_status,
-                                       AccessProhibitedReason reason) = 0;
-
-  void OnSetupOperationDeleted(int operation_id);
-
-  int next_operation_id_ = 0;
-  base::flat_map<int, NotificationAccessSetupOperation*> id_to_operation_map_;
-  base::ObserverList<Observer> observer_list_;
-  base::WeakPtrFactory<NotificationAccessManager> weak_ptr_factory_{this};
-};
-
-std::ostream& operator<<(std::ostream& stream,
-                         NotificationAccessManager::AccessStatus status);
-std::ostream& operator<<(
-    std::ostream& stream,
-    NotificationAccessManager::AccessProhibitedReason reason);
-std::ostream& operator<<(
-    std::ostream& stream,
-    std::pair<NotificationAccessManager::AccessStatus,
-              NotificationAccessManager::AccessProhibitedReason> status_reason);
-
-}  // namespace phonehub
-}  // namespace ash
-
-// TODO(https://crbug.com/1164001): remove after the migration is finished.
-namespace chromeos {
-namespace phonehub {
-using ::ash::phonehub::NotificationAccessManager;
-}
-}  // namespace chromeos
-
-#endif  // ASH_COMPONENTS_PHONEHUB_NOTIFICATION_ACCESS_MANAGER_H_
diff --git a/ash/components/phonehub/notification_access_manager_impl.h b/ash/components/phonehub/notification_access_manager_impl.h
deleted file mode 100644
index bec38bd2..0000000
--- a/ash/components/phonehub/notification_access_manager_impl.h
+++ /dev/null
@@ -1,66 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef ASH_COMPONENTS_PHONEHUB_NOTIFICATION_ACCESS_MANAGER_IMPL_H_
-#define ASH_COMPONENTS_PHONEHUB_NOTIFICATION_ACCESS_MANAGER_IMPL_H_
-
-#include "ash/components/phonehub/notification_access_manager.h"
-
-#include "ash/components/phonehub/feature_status_provider.h"
-
-class PrefRegistrySimple;
-class PrefService;
-
-namespace ash {
-namespace phonehub {
-
-class ConnectionScheduler;
-class MessageSender;
-
-// Implements NotificationAccessManager by persisting the last-known
-// notification access value to user prefs.
-class NotificationAccessManagerImpl : public NotificationAccessManager,
-                                      public FeatureStatusProvider::Observer {
- public:
-  static void RegisterPrefs(PrefRegistrySimple* registry);
-
-  explicit NotificationAccessManagerImpl(
-      PrefService* pref_service,
-      FeatureStatusProvider* feature_status_provider,
-      MessageSender* message_sender,
-      ConnectionScheduler* connection_scheduler);
-  ~NotificationAccessManagerImpl() override;
-
- private:
-  friend class NotificationAccessManagerImplTest;
-
-  // NotificationAccessManager:
-  AccessStatus GetAccessStatus() const override;
-  AccessProhibitedReason GetAccessProhibitedReason() const override;
-  void SetAccessStatusInternal(AccessStatus access_status,
-                               AccessProhibitedReason reason) override;
-  void OnSetupRequested() override;
-
-  bool HasNotificationSetupUiBeenDismissed() const override;
-  void DismissSetupRequiredUi() override;
-
-  // FeatureStatusProvider::Observer:
-  void OnFeatureStatusChanged() override;
-
-  void SendShowNotificationAccessSetupRequest();
-
-  bool HasAccessStatusChanged(AccessStatus access_status,
-                              AccessProhibitedReason reason);
-
-  FeatureStatus current_feature_status_;
-  PrefService* pref_service_;
-  FeatureStatusProvider* feature_status_provider_;
-  MessageSender* message_sender_;
-  ConnectionScheduler* connection_scheduler_;
-};
-
-}  // namespace phonehub
-}  // namespace ash
-
-#endif  // ASH_COMPONENTS_PHONEHUB_NOTIFICATION_ACCESS_MANAGER_IMPL_H_
diff --git a/ash/components/phonehub/notification_access_manager_impl_unittest.cc b/ash/components/phonehub/notification_access_manager_impl_unittest.cc
deleted file mode 100644
index df22c33..0000000
--- a/ash/components/phonehub/notification_access_manager_impl_unittest.cc
+++ /dev/null
@@ -1,546 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ash/components/phonehub/notification_access_manager_impl.h"
-
-#include <memory>
-
-#include "ash/components/phonehub/fake_connection_scheduler.h"
-#include "ash/components/phonehub/fake_feature_status_provider.h"
-#include "ash/components/phonehub/fake_message_sender.h"
-#include "ash/components/phonehub/notification_access_setup_operation.h"
-#include "ash/components/phonehub/pref_names.h"
-#include "components/prefs/testing_pref_service.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace ash {
-namespace phonehub {
-namespace {
-
-class FakeObserver : public NotificationAccessManager::Observer {
- public:
-  FakeObserver() = default;
-  ~FakeObserver() override = default;
-
-  size_t num_calls() const { return num_calls_; }
-
-  // NotificationAccessManager::Observer:
-  void OnNotificationAccessChanged() override { ++num_calls_; }
-
- private:
-  size_t num_calls_ = 0;
-};
-
-class FakeOperationDelegate
-    : public NotificationAccessSetupOperation::Delegate {
- public:
-  FakeOperationDelegate() = default;
-  ~FakeOperationDelegate() override = default;
-
-  NotificationAccessSetupOperation::Status status() const { return status_; }
-
-  // NotificationAccessSetupOperation::Delegate:
-  void OnStatusChange(
-      NotificationAccessSetupOperation::Status new_status) override {
-    status_ = new_status;
-  }
-
- private:
-  NotificationAccessSetupOperation::Status status_ =
-      NotificationAccessSetupOperation::Status::kConnecting;
-};
-
-}  // namespace
-
-class NotificationAccessManagerImplTest : public testing::Test {
- protected:
-  NotificationAccessManagerImplTest() = default;
-  NotificationAccessManagerImplTest(const NotificationAccessManagerImplTest&) =
-      delete;
-  NotificationAccessManagerImplTest& operator=(
-      const NotificationAccessManagerImplTest&) = delete;
-  ~NotificationAccessManagerImplTest() override = default;
-
-  // testing::Test:
-  void SetUp() override {
-    NotificationAccessManagerImpl::RegisterPrefs(pref_service_.registry());
-    fake_feature_status_provider_ =
-        std::make_unique<FakeFeatureStatusProvider>();
-    fake_message_sender_ = std::make_unique<FakeMessageSender>();
-    fake_connection_scheduler_ = std::make_unique<FakeConnectionScheduler>();
-  }
-
-  void TearDown() override { manager_->RemoveObserver(&fake_observer_); }
-
-  void Initialize(
-      NotificationAccessManager::AccessStatus status,
-      NotificationAccessManager::AccessProhibitedReason reason =
-          NotificationAccessManager::AccessProhibitedReason::kUnknown) {
-    pref_service_.SetInteger(prefs::kNotificationAccessStatus,
-                             static_cast<int>(status));
-    pref_service_.SetInteger(prefs::kNotificationAccessProhibitedReason,
-                             static_cast<int>(reason));
-    SetNeedsOneTimeNotificationAccessUpdate(/*needs_update=*/false);
-    manager_ = std::make_unique<NotificationAccessManagerImpl>(
-        &pref_service_, fake_feature_status_provider_.get(),
-        fake_message_sender_.get(), fake_connection_scheduler_.get());
-    manager_->AddObserver(&fake_observer_);
-  }
-
-  NotificationAccessSetupOperation::Status
-  GetNotificationAccessSetupOperationStatus() {
-    return fake_delegate_.status();
-  }
-
-  void VerifyNotificationAccessGrantedState(
-      NotificationAccessManager::AccessStatus expected_status,
-      NotificationAccessManager::AccessProhibitedReason expected_reason =
-          NotificationAccessManager::AccessProhibitedReason::kUnknown) {
-    EXPECT_EQ(static_cast<int>(expected_status),
-              pref_service_.GetInteger(prefs::kNotificationAccessStatus));
-    EXPECT_EQ(expected_status, manager_->GetAccessStatus());
-
-    EXPECT_EQ(
-        static_cast<int>(expected_reason),
-        pref_service_.GetInteger(prefs::kNotificationAccessProhibitedReason));
-    EXPECT_EQ(expected_reason, manager_->GetAccessProhibitedReason());
-  }
-
-  bool HasNotificationSetupUiBeenDismissed() {
-    return manager_->HasNotificationSetupUiBeenDismissed();
-  }
-
-  void DismissSetupRequiredUi() { manager_->DismissSetupRequiredUi(); }
-
-  std::unique_ptr<NotificationAccessSetupOperation> StartSetupOperation() {
-    return manager_->AttemptNotificationSetup(&fake_delegate_);
-  }
-
-  bool IsSetupOperationInProgress() {
-    return manager_->IsSetupOperationInProgress();
-  }
-
-  void SetAccessStatusInternal(
-      NotificationAccessManager::AccessStatus status,
-      NotificationAccessManager::AccessProhibitedReason reason =
-          NotificationAccessManager::AccessProhibitedReason::kUnknown) {
-    manager_->SetAccessStatusInternal(status, reason);
-  }
-
-  void SetFeatureStatus(FeatureStatus status) {
-    fake_feature_status_provider_->SetStatus(status);
-  }
-
-  FeatureStatus GetFeatureStatus() {
-    return fake_feature_status_provider_->GetStatus();
-  }
-
-  size_t GetNumScheduleConnectionNowCalls() const {
-    return fake_connection_scheduler_->num_schedule_connection_now_calls();
-  }
-
-  size_t GetNumShowNotificationAccessSetupRequestCount() const {
-    return fake_message_sender_->show_notification_access_setup_request_count();
-  }
-
-  size_t GetNumObserverCalls() const { return fake_observer_.num_calls(); }
-
-  void SetNeedsOneTimeNotificationAccessUpdate(bool needs_update) {
-    pref_service_.SetBoolean(prefs::kNeedsOneTimeNotificationAccessUpdate,
-                             needs_update);
-  }
-
- private:
-  TestingPrefServiceSimple pref_service_;
-
-  FakeObserver fake_observer_;
-  FakeOperationDelegate fake_delegate_;
-  std::unique_ptr<FakeFeatureStatusProvider> fake_feature_status_provider_;
-  std::unique_ptr<FakeMessageSender> fake_message_sender_;
-  std::unique_ptr<FakeConnectionScheduler> fake_connection_scheduler_;
-  std::unique_ptr<NotificationAccessManager> manager_;
-};
-
-TEST_F(NotificationAccessManagerImplTest, ShouldShowSetupRequiredUi) {
-  // Notification setup is not dismissed initially even when access has been
-  // granted.
-  Initialize(NotificationAccessManager::AccessStatus::kAccessGranted);
-  EXPECT_FALSE(HasNotificationSetupUiBeenDismissed());
-
-  // Notification setup is not dismissed initially when access has not been
-  // granted.
-  Initialize(NotificationAccessManager::AccessStatus::kAvailableButNotGranted);
-  EXPECT_FALSE(HasNotificationSetupUiBeenDismissed());
-
-  // Simlulate dismissal of UI.
-  DismissSetupRequiredUi();
-  EXPECT_TRUE(HasNotificationSetupUiBeenDismissed());
-
-  // Dismissal value is persisted on initialization with access not granted.
-  Initialize(NotificationAccessManager::AccessStatus::kAvailableButNotGranted);
-  EXPECT_TRUE(HasNotificationSetupUiBeenDismissed());
-
-  // Dismissal value is persisted on initialization with access granted.
-  Initialize(NotificationAccessManager::AccessStatus::kAccessGranted);
-  EXPECT_TRUE(HasNotificationSetupUiBeenDismissed());
-}
-
-TEST_F(NotificationAccessManagerImplTest, InitiallyGranted) {
-  Initialize(NotificationAccessManager::AccessStatus::kAccessGranted);
-  VerifyNotificationAccessGrantedState(
-      NotificationAccessManager::AccessStatus::kAccessGranted);
-
-  // Cannot start the notification access setup flow if access has already been
-  // granted.
-  auto operation = StartSetupOperation();
-  EXPECT_FALSE(operation);
-}
-
-TEST_F(NotificationAccessManagerImplTest, OnFeatureStatusChanged) {
-  Initialize(NotificationAccessManager::AccessStatus::kAvailableButNotGranted);
-  VerifyNotificationAccessGrantedState(
-      NotificationAccessManager::AccessStatus::kAvailableButNotGranted);
-
-  // Set initial state to disconnected.
-  SetFeatureStatus(FeatureStatus::kEnabledButDisconnected);
-  EXPECT_EQ(0u, GetNumShowNotificationAccessSetupRequestCount());
-  EXPECT_EQ(NotificationAccessSetupOperation::Status::kConnecting,
-            GetNotificationAccessSetupOperationStatus());
-
-  // Simulate feature status to be enabled and connected. SetupOperation is also
-  // not in progress, so expect no new requests to be sent.
-  SetFeatureStatus(FeatureStatus::kEnabledAndConnected);
-  EXPECT_EQ(0u, GetNumShowNotificationAccessSetupRequestCount());
-  EXPECT_EQ(NotificationAccessSetupOperation::Status::kConnecting,
-            GetNotificationAccessSetupOperationStatus());
-
-  // Simulate setup operation is in progress. This will trigger a sent request.
-  auto operation = StartSetupOperation();
-  EXPECT_TRUE(operation);
-  EXPECT_EQ(1u, GetNumShowNotificationAccessSetupRequestCount());
-  EXPECT_EQ(NotificationAccessSetupOperation::Status::
-                kSentMessageToPhoneAndWaitingForResponse,
-            GetNotificationAccessSetupOperationStatus());
-
-  // Set another feature status, expect status to be updated.
-  SetFeatureStatus(FeatureStatus::kEnabledButDisconnected);
-  EXPECT_EQ(1u, GetNumShowNotificationAccessSetupRequestCount());
-  EXPECT_EQ(NotificationAccessSetupOperation::Status::kConnectionDisconnected,
-            GetNotificationAccessSetupOperationStatus());
-}
-
-TEST_F(NotificationAccessManagerImplTest, StartDisconnectedAndNoAccess) {
-  // Set initial state to disconnected.
-  SetFeatureStatus(FeatureStatus::kEnabledButDisconnected);
-
-  Initialize(NotificationAccessManager::AccessStatus::kAvailableButNotGranted);
-  VerifyNotificationAccessGrantedState(
-      NotificationAccessManager::AccessStatus::kAvailableButNotGranted);
-
-  // Start a setup operation with enabled but disconnected status and access
-  // not granted.
-  auto operation = StartSetupOperation();
-  EXPECT_TRUE(operation);
-  EXPECT_EQ(1u, GetNumScheduleConnectionNowCalls());
-
-  // Simulate changing states from connecting to connected.
-  SetFeatureStatus(FeatureStatus::kEnabledAndConnecting);
-  SetFeatureStatus(FeatureStatus::kEnabledAndConnected);
-
-  // Verify that the request message has been sent and our operation status
-  // is updated.
-  EXPECT_EQ(1u, GetNumShowNotificationAccessSetupRequestCount());
-  EXPECT_EQ(NotificationAccessSetupOperation::Status::
-                kSentMessageToPhoneAndWaitingForResponse,
-            GetNotificationAccessSetupOperationStatus());
-
-  // Simulate getting a response back from the phone.
-  SetAccessStatusInternal(
-      NotificationAccessManager::AccessStatus::kAccessGranted);
-  VerifyNotificationAccessGrantedState(
-      NotificationAccessManager::AccessStatus::kAccessGranted);
-  EXPECT_EQ(NotificationAccessSetupOperation::Status::kCompletedSuccessfully,
-            GetNotificationAccessSetupOperationStatus());
-}
-
-TEST_F(NotificationAccessManagerImplTest,
-       StartDisconnectedAndNoAccess_Prohibited) {
-  // Set initial state to disconnected.
-  SetFeatureStatus(FeatureStatus::kEnabledButDisconnected);
-
-  Initialize(NotificationAccessManager::AccessStatus::kAvailableButNotGranted);
-  VerifyNotificationAccessGrantedState(
-      NotificationAccessManager::AccessStatus::kAvailableButNotGranted);
-
-  // Start a setup operation with enabled but disconnected status and access
-  // not granted.
-  auto operation = StartSetupOperation();
-  EXPECT_TRUE(operation);
-  EXPECT_EQ(1u, GetNumScheduleConnectionNowCalls());
-
-  // Simulate changing states from connecting to connected.
-  SetFeatureStatus(FeatureStatus::kEnabledAndConnecting);
-  SetFeatureStatus(FeatureStatus::kEnabledAndConnected);
-
-  // Verify that the request message has been sent and our operation status
-  // is updated.
-  EXPECT_EQ(1u, GetNumShowNotificationAccessSetupRequestCount());
-  EXPECT_EQ(NotificationAccessSetupOperation::Status::
-                kSentMessageToPhoneAndWaitingForResponse,
-            GetNotificationAccessSetupOperationStatus());
-
-  // Simulate getting a response back from the phone.
-  SetAccessStatusInternal(NotificationAccessManager::AccessStatus::kProhibited);
-  VerifyNotificationAccessGrantedState(
-      NotificationAccessManager::AccessStatus::kProhibited);
-  EXPECT_EQ(
-      NotificationAccessSetupOperation::Status::kProhibitedFromProvidingAccess,
-      GetNotificationAccessSetupOperationStatus());
-}
-
-TEST_F(NotificationAccessManagerImplTest, StartConnectingAndNoAccess) {
-  // Set initial state to connecting.
-  SetFeatureStatus(FeatureStatus::kEnabledAndConnecting);
-
-  Initialize(NotificationAccessManager::AccessStatus::kAvailableButNotGranted);
-  VerifyNotificationAccessGrantedState(
-      NotificationAccessManager::AccessStatus::kAvailableButNotGranted);
-
-  // Start a setup operation with enabled and connecting status and access
-  // not granted.
-  auto operation = StartSetupOperation();
-  EXPECT_TRUE(operation);
-
-  // Simulate changing states from connecting to connected.
-  SetFeatureStatus(FeatureStatus::kEnabledAndConnected);
-
-  // Verify that the request message has been sent and our operation status
-  // is updated.
-  EXPECT_EQ(1u, GetNumShowNotificationAccessSetupRequestCount());
-  EXPECT_EQ(NotificationAccessSetupOperation::Status::
-                kSentMessageToPhoneAndWaitingForResponse,
-            GetNotificationAccessSetupOperationStatus());
-
-  // Simulate getting a response back from the phone.
-  SetAccessStatusInternal(
-      NotificationAccessManager::AccessStatus::kAccessGranted);
-  VerifyNotificationAccessGrantedState(
-      NotificationAccessManager::AccessStatus::kAccessGranted);
-  EXPECT_EQ(NotificationAccessSetupOperation::Status::kCompletedSuccessfully,
-            GetNotificationAccessSetupOperationStatus());
-}
-
-TEST_F(NotificationAccessManagerImplTest, StartConnectedAndNoAccess) {
-  // Set initial state to connected.
-  SetFeatureStatus(FeatureStatus::kEnabledAndConnected);
-
-  Initialize(NotificationAccessManager::AccessStatus::kAvailableButNotGranted);
-  VerifyNotificationAccessGrantedState(
-      NotificationAccessManager::AccessStatus::kAvailableButNotGranted);
-
-  // Start a setup operation with enabled and connected status and access
-  // not granted.
-  auto operation = StartSetupOperation();
-  EXPECT_TRUE(operation);
-
-  // Verify that the request message has been sent and our operation status
-  // is updated.
-  EXPECT_EQ(1u, GetNumShowNotificationAccessSetupRequestCount());
-  EXPECT_EQ(NotificationAccessSetupOperation::Status::
-                kSentMessageToPhoneAndWaitingForResponse,
-            GetNotificationAccessSetupOperationStatus());
-
-  // Simulate getting a response back from the phone.
-  SetAccessStatusInternal(
-      NotificationAccessManager::AccessStatus::kAccessGranted);
-  VerifyNotificationAccessGrantedState(
-      NotificationAccessManager::AccessStatus::kAccessGranted);
-  EXPECT_EQ(NotificationAccessSetupOperation::Status::kCompletedSuccessfully,
-            GetNotificationAccessSetupOperationStatus());
-}
-
-TEST_F(NotificationAccessManagerImplTest, SimulateConnectingToDisconnected) {
-  // Set initial state to connecting.
-  SetFeatureStatus(FeatureStatus::kEnabledAndConnecting);
-
-  Initialize(NotificationAccessManager::AccessStatus::kAvailableButNotGranted);
-  VerifyNotificationAccessGrantedState(
-      NotificationAccessManager::AccessStatus::kAvailableButNotGranted);
-
-  auto operation = StartSetupOperation();
-  EXPECT_TRUE(operation);
-
-  // Simulate a disconnection and expect that status has been updated.
-  SetFeatureStatus(FeatureStatus::kEnabledButDisconnected);
-  EXPECT_EQ(NotificationAccessSetupOperation::Status::kTimedOutConnecting,
-            GetNotificationAccessSetupOperationStatus());
-}
-
-TEST_F(NotificationAccessManagerImplTest, SimulateConnectedToDisconnected) {
-  // Simulate connected state.
-  SetFeatureStatus(FeatureStatus::kEnabledAndConnected);
-
-  Initialize(NotificationAccessManager::AccessStatus::kAvailableButNotGranted);
-  VerifyNotificationAccessGrantedState(
-      NotificationAccessManager::AccessStatus::kAvailableButNotGranted);
-
-  auto operation = StartSetupOperation();
-  EXPECT_TRUE(operation);
-
-  EXPECT_EQ(1u, GetNumShowNotificationAccessSetupRequestCount());
-
-  // Simulate a disconnection, expect status update.
-  SetFeatureStatus(FeatureStatus::kEnabledButDisconnected);
-  EXPECT_EQ(NotificationAccessSetupOperation::Status::kConnectionDisconnected,
-            GetNotificationAccessSetupOperationStatus());
-}
-
-TEST_F(NotificationAccessManagerImplTest, SimulateConnectedToDisabled) {
-  // Simulate connected state.
-  SetFeatureStatus(FeatureStatus::kEnabledAndConnected);
-
-  Initialize(NotificationAccessManager::AccessStatus::kAvailableButNotGranted);
-  VerifyNotificationAccessGrantedState(
-      NotificationAccessManager::AccessStatus::kAvailableButNotGranted);
-
-  auto operation = StartSetupOperation();
-  EXPECT_TRUE(operation);
-
-  EXPECT_EQ(1u, GetNumShowNotificationAccessSetupRequestCount());
-
-  // Simulate disabling the feature, expect status update.
-  SetFeatureStatus(FeatureStatus::kDisabled);
-  EXPECT_EQ(NotificationAccessSetupOperation::Status::kConnectionDisconnected,
-            GetNotificationAccessSetupOperationStatus());
-}
-
-TEST_F(NotificationAccessManagerImplTest, FlipAccessGrantedToNotGranted) {
-  Initialize(NotificationAccessManager::AccessStatus::kAccessGranted);
-  VerifyNotificationAccessGrantedState(
-      NotificationAccessManager::AccessStatus::kAccessGranted);
-
-  // Simulate flipping the access state to no granted.
-  SetAccessStatusInternal(
-      NotificationAccessManager::AccessStatus::kAvailableButNotGranted);
-  VerifyNotificationAccessGrantedState(
-      NotificationAccessManager::AccessStatus::kAvailableButNotGranted);
-  EXPECT_EQ(1u, GetNumObserverCalls());
-}
-
-TEST_F(NotificationAccessManagerImplTest, FlipAccessGrantedToProhibited) {
-  Initialize(NotificationAccessManager::AccessStatus::kAccessGranted);
-  VerifyNotificationAccessGrantedState(
-      NotificationAccessManager::AccessStatus::kAccessGranted);
-
-  // Simulate flipping the access state to prohibited.
-  SetAccessStatusInternal(NotificationAccessManager::AccessStatus::kProhibited);
-  VerifyNotificationAccessGrantedState(
-      NotificationAccessManager::AccessStatus::kProhibited);
-  EXPECT_EQ(1u, GetNumObserverCalls());
-}
-
-TEST_F(NotificationAccessManagerImplTest, AccessNotChanged) {
-  Initialize(NotificationAccessManager::AccessStatus::kAccessGranted);
-  VerifyNotificationAccessGrantedState(
-      NotificationAccessManager::AccessStatus::kAccessGranted);
-
-  // If the access state is unchanged, we do not expect any notifications.
-  SetAccessStatusInternal(
-      NotificationAccessManager::AccessStatus::kAccessGranted);
-  VerifyNotificationAccessGrantedState(
-      NotificationAccessManager::AccessStatus::kAccessGranted);
-  EXPECT_EQ(0u, GetNumObserverCalls());
-}
-
-TEST_F(NotificationAccessManagerImplTest,
-       NeedsOneTimeNotificationAccessUpdate_AccessGranted) {
-  Initialize(NotificationAccessManager::AccessStatus::kAccessGranted);
-  VerifyNotificationAccessGrantedState(
-      NotificationAccessManager::AccessStatus::kAccessGranted);
-
-  // Send a one-time signal to observers if access is granted. See
-  // http://crbug.com/1215559.
-  SetNeedsOneTimeNotificationAccessUpdate(/*needs_update=*/true);
-  SetAccessStatusInternal(
-      NotificationAccessManager::AccessStatus::kAccessGranted);
-  VerifyNotificationAccessGrantedState(
-      NotificationAccessManager::AccessStatus::kAccessGranted);
-  EXPECT_EQ(1u, GetNumObserverCalls());
-
-  // Observers should be notified only once ever.
-  SetAccessStatusInternal(
-      NotificationAccessManager::AccessStatus::kAccessGranted);
-  VerifyNotificationAccessGrantedState(
-      NotificationAccessManager::AccessStatus::kAccessGranted);
-  EXPECT_EQ(1u, GetNumObserverCalls());
-}
-
-TEST_F(NotificationAccessManagerImplTest,
-       NeedsOneTimeNotificationAccessUpdate_Prohibited) {
-  Initialize(NotificationAccessManager::AccessStatus::kProhibited);
-  VerifyNotificationAccessGrantedState(
-      NotificationAccessManager::AccessStatus::kProhibited);
-
-  // Only send the one-time signal to observers if access is granted. See
-  // http://crbug.com/1215559.
-  SetNeedsOneTimeNotificationAccessUpdate(/*needs_update=*/true);
-  SetAccessStatusInternal(NotificationAccessManager::AccessStatus::kProhibited);
-  VerifyNotificationAccessGrantedState(
-      NotificationAccessManager::AccessStatus::kProhibited);
-  EXPECT_EQ(0u, GetNumObserverCalls());
-}
-
-TEST_F(NotificationAccessManagerImplTest,
-       NotificationAccessProhibitedReason_FromProhibited) {
-  Initialize(NotificationAccessManager::AccessStatus::kProhibited);
-  VerifyNotificationAccessGrantedState(
-      NotificationAccessManager::AccessStatus::kProhibited);
-
-  // Simulates an initial update after the pref is first added
-  SetAccessStatusInternal(
-      NotificationAccessManager::AccessStatus::kProhibited,
-      NotificationAccessManager::AccessProhibitedReason::kWorkProfile);
-  VerifyNotificationAccessGrantedState(
-      NotificationAccessManager::AccessStatus::kProhibited,
-      NotificationAccessManager::AccessProhibitedReason::kWorkProfile);
-  EXPECT_EQ(1u, GetNumObserverCalls());
-
-  // No update or observer notification should occur with no change
-  SetAccessStatusInternal(
-      NotificationAccessManager::AccessStatus::kProhibited,
-      NotificationAccessManager::AccessProhibitedReason::kWorkProfile);
-  VerifyNotificationAccessGrantedState(
-      NotificationAccessManager::AccessStatus::kProhibited,
-      NotificationAccessManager::AccessProhibitedReason::kWorkProfile);
-  EXPECT_EQ(1u, GetNumObserverCalls());
-
-  // This can happen if a user updates from Android <N to >=N
-  SetAccessStatusInternal(NotificationAccessManager::AccessStatus::kProhibited,
-                          NotificationAccessManager::AccessProhibitedReason::
-                              kDisabledByPhonePolicy);
-  VerifyNotificationAccessGrantedState(
-      NotificationAccessManager::AccessStatus::kProhibited,
-      NotificationAccessManager::AccessProhibitedReason::
-          kDisabledByPhonePolicy);
-  EXPECT_EQ(2u, GetNumObserverCalls());
-}
-
-TEST_F(NotificationAccessManagerImplTest,
-       NotificationAccessProhibitedReason_FromGranted) {
-  Initialize(NotificationAccessManager::AccessStatus::kAccessGranted);
-  VerifyNotificationAccessGrantedState(
-      NotificationAccessManager::AccessStatus::kAccessGranted);
-
-  SetAccessStatusInternal(NotificationAccessManager::AccessStatus::kProhibited,
-                          NotificationAccessManager::AccessProhibitedReason::
-                              kDisabledByPhonePolicy);
-  VerifyNotificationAccessGrantedState(
-      NotificationAccessManager::AccessStatus::kProhibited,
-      NotificationAccessManager::AccessProhibitedReason::
-          kDisabledByPhonePolicy);
-  EXPECT_EQ(1u, GetNumObserverCalls());
-}
-
-}  // namespace phonehub
-}  // namespace ash
diff --git a/ash/components/phonehub/notification_access_setup_operation.h b/ash/components/phonehub/notification_access_setup_operation.h
index e772fa85..efa59d3 100644
--- a/ash/components/phonehub/notification_access_setup_operation.h
+++ b/ash/components/phonehub/notification_access_setup_operation.h
@@ -79,7 +79,7 @@
   virtual ~NotificationAccessSetupOperation();
 
  private:
-  friend class NotificationAccessManager;
+  friend class MultideviceFeatureAccessManager;
 
   NotificationAccessSetupOperation(Delegate* delegate,
                                    base::OnceClosure destructor_callback);
diff --git a/ash/components/phonehub/phone_hub_manager.h b/ash/components/phonehub/phone_hub_manager.h
index c4526b2..35917c1 100644
--- a/ash/components/phonehub/phone_hub_manager.h
+++ b/ash/components/phonehub/phone_hub_manager.h
@@ -14,7 +14,7 @@
 class DoNotDisturbController;
 class FeatureStatusProvider;
 class FindMyDeviceController;
-class NotificationAccessManager;
+class MultideviceFeatureAccessManager;
 class NotificationInteractionHandler;
 class NotificationManager;
 class OnboardingUiTracker;
@@ -40,7 +40,8 @@
   virtual DoNotDisturbController* GetDoNotDisturbController() = 0;
   virtual FeatureStatusProvider* GetFeatureStatusProvider() = 0;
   virtual FindMyDeviceController* GetFindMyDeviceController() = 0;
-  virtual NotificationAccessManager* GetNotificationAccessManager() = 0;
+  virtual MultideviceFeatureAccessManager*
+  GetMultideviceFeatureAccessManager() = 0;
   virtual NotificationInteractionHandler*
   GetNotificationInteractionHandler() = 0;
   virtual NotificationManager* GetNotificationManager() = 0;
diff --git a/ash/components/phonehub/phone_hub_manager_impl.cc b/ash/components/phonehub/phone_hub_manager_impl.cc
index 9a22a19..cb71310 100644
--- a/ash/components/phonehub/phone_hub_manager_impl.cc
+++ b/ash/components/phonehub/phone_hub_manager_impl.cc
@@ -17,9 +17,9 @@
 #include "ash/components/phonehub/invalid_connection_disconnector.h"
 #include "ash/components/phonehub/message_receiver_impl.h"
 #include "ash/components/phonehub/message_sender_impl.h"
+#include "ash/components/phonehub/multidevice_feature_access_manager_impl.h"
 #include "ash/components/phonehub/multidevice_setup_state_updater.h"
 #include "ash/components/phonehub/mutable_phone_model.h"
-#include "ash/components/phonehub/notification_access_manager_impl.h"
 #include "ash/components/phonehub/notification_interaction_handler_impl.h"
 #include "ash/components/phonehub/notification_manager_impl.h"
 #include "ash/components/phonehub/notification_processor.h"
@@ -88,8 +88,8 @@
       find_my_device_controller_(std::make_unique<FindMyDeviceControllerImpl>(
           message_sender_.get(),
           user_action_recorder_.get())),
-      notification_access_manager_(
-          std::make_unique<NotificationAccessManagerImpl>(
+      multidevice_feature_access_manager_(
+          std::make_unique<MultideviceFeatureAccessManagerImpl>(
               pref_service,
               feature_status_provider_.get(),
               message_sender_.get(),
@@ -118,14 +118,14 @@
               ? std::make_unique<RecentAppsInteractionHandlerImpl>(
                     pref_service,
                     multidevice_setup_client,
-                    notification_access_manager_.get())
+                    multidevice_feature_access_manager_.get())
               : nullptr),
       phone_status_processor_(std::make_unique<PhoneStatusProcessor>(
           do_not_disturb_controller_.get(),
           feature_status_provider_.get(),
           message_receiver_.get(),
           find_my_device_controller_.get(),
-          notification_access_manager_.get(),
+          multidevice_feature_access_manager_.get(),
           screen_lock_manager_.get(),
           notification_processor_.get(),
           multidevice_setup_client,
@@ -145,7 +145,7 @@
           std::make_unique<MultideviceSetupStateUpdater>(
               pref_service,
               multidevice_setup_client,
-              notification_access_manager_.get())),
+              multidevice_feature_access_manager_.get())),
       invalid_connection_disconnector_(
           std::make_unique<InvalidConnectionDisconnector>(
               connection_manager_.get(),
@@ -186,8 +186,9 @@
   return find_my_device_controller_.get();
 }
 
-NotificationAccessManager* PhoneHubManagerImpl::GetNotificationAccessManager() {
-  return notification_access_manager_.get();
+MultideviceFeatureAccessManager*
+PhoneHubManagerImpl::GetMultideviceFeatureAccessManager() {
+  return multidevice_feature_access_manager_.get();
 }
 
 NotificationInteractionHandler*
@@ -240,7 +241,7 @@
   notification_manager_.reset();
   notification_interaction_handler_.reset();
   screen_lock_manager_.reset();
-  notification_access_manager_.reset();
+  multidevice_feature_access_manager_.reset();
   find_my_device_controller_.reset();
   connection_scheduler_.reset();
   do_not_disturb_controller_.reset();
diff --git a/ash/components/phonehub/phone_hub_manager_impl.h b/ash/components/phonehub/phone_hub_manager_impl.h
index 218eab9..3e549f9 100644
--- a/ash/components/phonehub/phone_hub_manager_impl.h
+++ b/ash/components/phonehub/phone_hub_manager_impl.h
@@ -59,7 +59,8 @@
   DoNotDisturbController* GetDoNotDisturbController() override;
   FeatureStatusProvider* GetFeatureStatusProvider() override;
   FindMyDeviceController* GetFindMyDeviceController() override;
-  NotificationAccessManager* GetNotificationAccessManager() override;
+  MultideviceFeatureAccessManager* GetMultideviceFeatureAccessManager()
+      override;
   NotificationInteractionHandler* GetNotificationInteractionHandler() override;
   NotificationManager* GetNotificationManager() override;
   OnboardingUiTracker* GetOnboardingUiTracker() override;
@@ -83,7 +84,8 @@
   std::unique_ptr<DoNotDisturbController> do_not_disturb_controller_;
   std::unique_ptr<ConnectionScheduler> connection_scheduler_;
   std::unique_ptr<FindMyDeviceController> find_my_device_controller_;
-  std::unique_ptr<NotificationAccessManager> notification_access_manager_;
+  std::unique_ptr<MultideviceFeatureAccessManager>
+      multidevice_feature_access_manager_;
   std::unique_ptr<ScreenLockManager> screen_lock_manager_;
   std::unique_ptr<NotificationInteractionHandler>
       notification_interaction_handler_;
diff --git a/ash/components/phonehub/phone_status_processor.cc b/ash/components/phonehub/phone_status_processor.cc
index 5fc0717..d1d4dd8b 100644
--- a/ash/components/phonehub/phone_status_processor.cc
+++ b/ash/components/phonehub/phone_status_processor.cc
@@ -10,8 +10,8 @@
 #include "ash/components/phonehub/do_not_disturb_controller.h"
 #include "ash/components/phonehub/find_my_device_controller.h"
 #include "ash/components/phonehub/message_receiver.h"
+#include "ash/components/phonehub/multidevice_feature_access_manager.h"
 #include "ash/components/phonehub/mutable_phone_model.h"
-#include "ash/components/phonehub/notification_access_manager.h"
 #include "ash/components/phonehub/notification_processor.h"
 #include "ash/components/phonehub/recent_apps_interaction_handler.h"
 #include "ash/components/phonehub/screen_lock_manager_impl.h"
@@ -86,33 +86,46 @@
   }
 }
 
-NotificationAccessManager::AccessStatus ComputeNotificationAccessState(
+MultideviceFeatureAccessManager::AccessStatus ComputeNotificationAccessState(
     const proto::PhoneProperties& phone_properties) {
   // If the user has a Work Profile active, notification access is not allowed
   // by Android. See https://crbug.com/1155151.
   if (phone_properties.profile_type() == proto::ProfileType::WORK_PROFILE)
-    return NotificationAccessManager::AccessStatus::kProhibited;
+    return MultideviceFeatureAccessManager::AccessStatus::kProhibited;
 
   if (phone_properties.notification_access_state() ==
       proto::NotificationAccessState::ACCESS_GRANTED) {
-    return NotificationAccessManager::AccessStatus::kAccessGranted;
+    return MultideviceFeatureAccessManager::AccessStatus::kAccessGranted;
   }
 
-  return NotificationAccessManager::AccessStatus::kAvailableButNotGranted;
+  return MultideviceFeatureAccessManager::AccessStatus::kAvailableButNotGranted;
 }
 
-NotificationAccessManager::AccessProhibitedReason
+// User has to consent and agree for phoneHub to have storage permission on the
+// phone
+MultideviceFeatureAccessManager::AccessStatus ComputeCameraRollAccessState(
+    const proto::PhoneProperties& phone_properties) {
+  if (phone_properties.camera_roll_access_state().feature_enabled()) {
+    return MultideviceFeatureAccessManager::AccessStatus::kAccessGranted;
+  } else {
+    return MultideviceFeatureAccessManager::AccessStatus::
+        kAvailableButNotGranted;
+  }
+}
+
+MultideviceFeatureAccessManager::AccessProhibitedReason
 ComputeNotificationAccessProhibitedReason(
     const proto::PhoneProperties& phone_properties) {
   if (phone_properties.profile_disable_reason() ==
       proto::ProfileDisableReason::DISABLE_REASON_DISABLED_BY_POLICY) {
-    return NotificationAccessManager::AccessProhibitedReason::
+    return MultideviceFeatureAccessManager::AccessProhibitedReason::
         kDisabledByPhonePolicy;
   }
   if (phone_properties.profile_type() == proto::ProfileType::WORK_PROFILE) {
-    return NotificationAccessManager::AccessProhibitedReason::kWorkProfile;
+    return MultideviceFeatureAccessManager::AccessProhibitedReason::
+        kWorkProfile;
   }
-  return NotificationAccessManager::AccessProhibitedReason::kUnknown;
+  return MultideviceFeatureAccessManager::AccessProhibitedReason::kUnknown;
 }
 
 ScreenLockManager::LockStatus ComputeScreenLockState(
@@ -174,7 +187,7 @@
     FeatureStatusProvider* feature_status_provider,
     MessageReceiver* message_receiver,
     FindMyDeviceController* find_my_device_controller,
-    NotificationAccessManager* notification_access_manager,
+    MultideviceFeatureAccessManager* multidevice_feature_access_manager,
     ScreenLockManager* screen_lock_manager,
     NotificationProcessor* notification_processor_,
     MultiDeviceSetupClient* multidevice_setup_client,
@@ -184,7 +197,7 @@
       feature_status_provider_(feature_status_provider),
       message_receiver_(message_receiver),
       find_my_device_controller_(find_my_device_controller),
-      notification_access_manager_(notification_access_manager),
+      multidevice_feature_access_manager_(multidevice_feature_access_manager),
       screen_lock_manager_(screen_lock_manager),
       notification_processor_(notification_processor_),
       multidevice_setup_client_(multidevice_setup_client),
@@ -194,7 +207,7 @@
   DCHECK(feature_status_provider_);
   DCHECK(message_receiver_);
   DCHECK(find_my_device_controller_);
-  DCHECK(notification_access_manager_);
+  DCHECK(multidevice_feature_access_manager_);
   DCHECK(notification_processor_);
   DCHECK(multidevice_setup_client_);
   DCHECK(phone_model_);
@@ -251,10 +264,15 @@
           proto::NotificationMode::DO_NOT_DISTURB_ON,
       phone_properties.profile_type() != proto::ProfileType::WORK_PROFILE);
 
-  notification_access_manager_->SetAccessStatusInternal(
+  multidevice_feature_access_manager_->SetNotificationAccessStatusInternal(
       ComputeNotificationAccessState(phone_properties),
       ComputeNotificationAccessProhibitedReason(phone_properties));
 
+  if (features::IsPhoneHubCameraRollEnabled()) {
+    multidevice_feature_access_manager_->SetCameraRollAccessStatusInternal(
+        ComputeCameraRollAccessState(phone_properties));
+  }
+
   if (screen_lock_manager_) {
     screen_lock_manager_->SetLockStatusInternal(
         ComputeScreenLockState(phone_properties));
diff --git a/ash/components/phonehub/phone_status_processor.h b/ash/components/phonehub/phone_status_processor.h
index 331f5d0..cd23f02 100644
--- a/ash/components/phonehub/phone_status_processor.h
+++ b/ash/components/phonehub/phone_status_processor.h
@@ -20,7 +20,7 @@
 class DoNotDisturbController;
 class FindMyDeviceController;
 class MutablePhoneModel;
-class NotificationAccessManager;
+class MultideviceFeatureAccessManager;
 class NotificationProcessor;
 class ScreenLockManager;
 class RecentAppsInteractionHandler;
@@ -37,7 +37,7 @@
       FeatureStatusProvider* feature_status_provider,
       MessageReceiver* message_receiver,
       FindMyDeviceController* find_my_device_controller,
-      NotificationAccessManager* notification_access_manager,
+      MultideviceFeatureAccessManager* multidevice_feature_access_manager,
       ScreenLockManager* screen_lock_manager,
       NotificationProcessor* notification_processor_,
       multidevice_setup::MultiDeviceSetupClient* multidevice_setup_client,
@@ -80,7 +80,7 @@
   FeatureStatusProvider* feature_status_provider_;
   MessageReceiver* message_receiver_;
   FindMyDeviceController* find_my_device_controller_;
-  NotificationAccessManager* notification_access_manager_;
+  MultideviceFeatureAccessManager* multidevice_feature_access_manager_;
   ScreenLockManager* screen_lock_manager_;
   NotificationProcessor* notification_processor_;
   multidevice_setup::MultiDeviceSetupClient* multidevice_setup_client_;
diff --git a/ash/components/phonehub/phone_status_processor_unittest.cc b/ash/components/phonehub/phone_status_processor_unittest.cc
index fab6b609..7b8472b 100644
--- a/ash/components/phonehub/phone_status_processor_unittest.cc
+++ b/ash/components/phonehub/phone_status_processor_unittest.cc
@@ -13,7 +13,7 @@
 #include "ash/components/phonehub/fake_feature_status_provider.h"
 #include "ash/components/phonehub/fake_find_my_device_controller.h"
 #include "ash/components/phonehub/fake_message_receiver.h"
-#include "ash/components/phonehub/fake_notification_access_manager.h"
+#include "ash/components/phonehub/fake_multidevice_feature_access_manager.h"
 #include "ash/components/phonehub/fake_notification_manager.h"
 #include "ash/components/phonehub/fake_recent_apps_interaction_handler.h"
 #include "ash/components/phonehub/fake_screen_lock_manager.h"
@@ -83,8 +83,8 @@
     fake_message_receiver_ = std::make_unique<FakeMessageReceiver>();
     fake_find_my_device_controller_ =
         std::make_unique<FakeFindMyDeviceController>();
-    fake_notification_access_manager_ =
-        std::make_unique<FakeNotificationAccessManager>();
+    fake_multidevice_feature_access_manager_ =
+        std::make_unique<FakeMultideviceFeatureAccessManager>();
     fake_screen_lock_manager_ = std::make_unique<FakeScreenLockManager>();
     fake_notification_manager_ = std::make_unique<FakeNotificationManager>();
     fake_notification_processor_ = std::make_unique<FakeNotificationProcessor>(
@@ -95,7 +95,8 @@
     fake_recent_apps_interaction_handler_ =
         std::make_unique<FakeRecentAppsInteractionHandler>();
     scoped_feature_list_.InitWithFeatures(
-        /*enabled_features=*/{features::kPhoneHubRecentApps},
+        /*enabled_features=*/{features::kPhoneHubRecentApps,
+                              features::kPhoneHubCameraRoll},
         /*disabled_features=*/{});
   }
 
@@ -104,7 +105,7 @@
         fake_do_not_disturb_controller_.get(),
         fake_feature_status_provider_.get(), fake_message_receiver_.get(),
         fake_find_my_device_controller_.get(),
-        fake_notification_access_manager_.get(),
+        fake_multidevice_feature_access_manager_.get(),
         fake_screen_lock_manager_.get(), fake_notification_processor_.get(),
         fake_multidevice_setup_client_.get(), mutable_phone_model_.get(),
         fake_recent_apps_interaction_handler_.get());
@@ -139,8 +140,8 @@
   std::unique_ptr<FakeFeatureStatusProvider> fake_feature_status_provider_;
   std::unique_ptr<FakeMessageReceiver> fake_message_receiver_;
   std::unique_ptr<FakeFindMyDeviceController> fake_find_my_device_controller_;
-  std::unique_ptr<FakeNotificationAccessManager>
-      fake_notification_access_manager_;
+  std::unique_ptr<FakeMultideviceFeatureAccessManager>
+      fake_multidevice_feature_access_manager_;
   std::unique_ptr<FakeScreenLockManager> fake_screen_lock_manager_;
   std::unique_ptr<FakeNotificationManager> fake_notification_manager_;
   std::unique_ptr<FakeNotificationProcessor> fake_notification_processor_;
@@ -176,6 +177,9 @@
       proto::MobileConnectionState::SIM_WITH_RECEPTION);
   expected_phone_properties->set_screen_lock_state(
       proto::ScreenLockState::SCREEN_LOCK_UNKNOWN);
+  proto::CameraRollAccessState* access_state =
+      expected_phone_properties->mutable_camera_roll_access_state();
+  access_state->set_feature_enabled(true);
 
   expected_phone_properties->add_user_states();
   proto::UserState* mutable_user_state =
@@ -205,8 +209,12 @@
   EXPECT_TRUE(fake_do_not_disturb_controller_->CanRequestNewDndState());
   EXPECT_EQ(FindMyDeviceController::Status::kRingingOn,
             fake_find_my_device_controller_->GetPhoneRingingStatus());
-  EXPECT_EQ(NotificationAccessManager::AccessStatus::kAvailableButNotGranted,
-            fake_notification_access_manager_->GetAccessStatus());
+  EXPECT_EQ(
+      MultideviceFeatureAccessManager::AccessStatus::kAvailableButNotGranted,
+      fake_multidevice_feature_access_manager_->GetNotificationAccessStatus());
+  EXPECT_EQ(
+      MultideviceFeatureAccessManager::AccessStatus::kAccessGranted,
+      fake_multidevice_feature_access_manager_->GetCameraRollAccessStatus());
   EXPECT_EQ(ScreenLockManager::LockStatus::kUnknown,
             fake_screen_lock_manager_->GetLockStatus());
 
@@ -262,6 +270,9 @@
       proto::MobileConnectionState::SIM_WITH_RECEPTION);
   expected_phone_properties->set_screen_lock_state(
       proto::ScreenLockState::SCREEN_LOCK_OFF);
+  proto::CameraRollAccessState* access_state =
+      expected_phone_properties->mutable_camera_roll_access_state();
+  access_state->set_feature_enabled(false);
 
   expected_phone_properties->add_user_states();
   proto::UserState* mutable_user_state =
@@ -290,8 +301,12 @@
   EXPECT_FALSE(fake_do_not_disturb_controller_->CanRequestNewDndState());
   EXPECT_EQ(FindMyDeviceController::Status::kRingingNotAvailable,
             fake_find_my_device_controller_->GetPhoneRingingStatus());
-  EXPECT_EQ(NotificationAccessManager::AccessStatus::kProhibited,
-            fake_notification_access_manager_->GetAccessStatus());
+  EXPECT_EQ(
+      MultideviceFeatureAccessManager::AccessStatus::kProhibited,
+      fake_multidevice_feature_access_manager_->GetNotificationAccessStatus());
+  EXPECT_EQ(
+      MultideviceFeatureAccessManager::AccessStatus::kAvailableButNotGranted,
+      fake_multidevice_feature_access_manager_->GetCameraRollAccessStatus());
   EXPECT_EQ(ScreenLockManager::LockStatus::kLockedOff,
             fake_screen_lock_manager_->GetLockStatus());
 
@@ -331,8 +346,12 @@
   EXPECT_TRUE(fake_do_not_disturb_controller_->CanRequestNewDndState());
   EXPECT_EQ(FindMyDeviceController::Status::kRingingOn,
             fake_find_my_device_controller_->GetPhoneRingingStatus());
-  EXPECT_EQ(NotificationAccessManager::AccessStatus::kAccessGranted,
-            fake_notification_access_manager_->GetAccessStatus());
+  EXPECT_EQ(
+      MultideviceFeatureAccessManager::AccessStatus::kAccessGranted,
+      fake_multidevice_feature_access_manager_->GetNotificationAccessStatus());
+  EXPECT_EQ(
+      MultideviceFeatureAccessManager::AccessStatus::kAvailableButNotGranted,
+      fake_multidevice_feature_access_manager_->GetCameraRollAccessStatus());
   EXPECT_EQ(ScreenLockManager::LockStatus::kLockedOn,
             fake_screen_lock_manager_->GetLockStatus());
 
@@ -375,10 +394,13 @@
       Feature::kPhoneHubNotifications, FeatureState::kDisabledByUser);
 
   EXPECT_FALSE(fake_do_not_disturb_controller_->CanRequestNewDndState());
-  EXPECT_EQ(NotificationAccessManager::AccessStatus::kProhibited,
-            fake_notification_access_manager_->GetAccessStatus());
-  EXPECT_EQ(NotificationAccessManager::AccessProhibitedReason::kWorkProfile,
-            fake_notification_access_manager_->GetAccessProhibitedReason());
+  EXPECT_EQ(
+      MultideviceFeatureAccessManager::AccessStatus::kProhibited,
+      fake_multidevice_feature_access_manager_->GetNotificationAccessStatus());
+  EXPECT_EQ(
+      MultideviceFeatureAccessManager::AccessProhibitedReason::kWorkProfile,
+      fake_multidevice_feature_access_manager_
+          ->GetNotificationAccessProhibitedReason());
 
   // Verify that adding a reason properly gets processed even when the current
   // profile type does not change.
@@ -387,10 +409,13 @@
   fake_message_receiver_->NotifyPhoneStatusUpdateReceived(expected_update);
 
   EXPECT_FALSE(fake_do_not_disturb_controller_->CanRequestNewDndState());
-  EXPECT_EQ(NotificationAccessManager::AccessStatus::kProhibited,
-            fake_notification_access_manager_->GetAccessStatus());
-  EXPECT_EQ(NotificationAccessManager::AccessProhibitedReason::kWorkProfile,
-            fake_notification_access_manager_->GetAccessProhibitedReason());
+  EXPECT_EQ(
+      MultideviceFeatureAccessManager::AccessStatus::kProhibited,
+      fake_multidevice_feature_access_manager_->GetNotificationAccessStatus());
+  EXPECT_EQ(
+      MultideviceFeatureAccessManager::AccessProhibitedReason::kWorkProfile,
+      fake_multidevice_feature_access_manager_
+          ->GetNotificationAccessProhibitedReason());
 }
 
 TEST_F(PhoneStatusProcessorTest, PhoneName) {
diff --git a/ash/components/phonehub/pref_names.cc b/ash/components/phonehub/pref_names.cc
index 5b0efa7..9bf11c1 100644
--- a/ash/components/phonehub/pref_names.cc
+++ b/ash/components/phonehub/pref_names.cc
@@ -8,16 +8,22 @@
 namespace phonehub {
 namespace prefs {
 
+// The last provided camera roll access status provided by the phone. This pref
+// stores the numerical value associated with the
+// MultideviceFeatureAccessManager::CameraRollAccessStatus enum.
+const char kCameraRollAccessStatus[] =
+    "cros.phonehub.camera_roll_access_status";
+
 // The last provided notification access status provided by the phone. This pref
 // stores the numerical value associated with the
-// NotificationAccessManager::AccessStatus enum.
+// MultideviceFeatureAccessManager::AccessStatus enum.
 const char kNotificationAccessStatus[] =
     "cros.phonehub.notification_access_status";
 
 // The last provided reason for notification access being prohibited. This pref
 // stores the numerical value associated with the
-// NotificationAccessManager::AccessProhibitedReason enum. This pref may be left
-// in an undefined state if notification access is not prohibited.
+// MultideviceFeatureAccessManager::AccessProhibitedReason enum. This pref may
+// be left in an undefined state if notification access is not prohibited.
 const char kNotificationAccessProhibitedReason[] =
     "cros.phonehub.notification_access_prohibited_reason";
 
diff --git a/ash/components/phonehub/pref_names.h b/ash/components/phonehub/pref_names.h
index 1fbcdf9..85e9278 100644
--- a/ash/components/phonehub/pref_names.h
+++ b/ash/components/phonehub/pref_names.h
@@ -9,6 +9,7 @@
 namespace phonehub {
 namespace prefs {
 
+extern const char kCameraRollAccessStatus[];
 extern const char kNotificationAccessStatus[];
 extern const char kNotificationAccessProhibitedReason[];
 extern const char kHideOnboardingUi[];
diff --git a/ash/components/phonehub/recent_apps_interaction_handler_impl.cc b/ash/components/phonehub/recent_apps_interaction_handler_impl.cc
index dac96b9..c776589 100644
--- a/ash/components/phonehub/recent_apps_interaction_handler_impl.cc
+++ b/ash/components/phonehub/recent_apps_interaction_handler_impl.cc
@@ -33,17 +33,17 @@
 RecentAppsInteractionHandlerImpl::RecentAppsInteractionHandlerImpl(
     PrefService* pref_service,
     multidevice_setup::MultiDeviceSetupClient* multidevice_setup_client,
-    NotificationAccessManager* notification_access_manager)
+    MultideviceFeatureAccessManager* multidevice_feature_access_manager)
     : pref_service_(pref_service),
       multidevice_setup_client_(multidevice_setup_client),
-      notification_access_manager_(notification_access_manager) {
+      multidevice_feature_access_manager_(multidevice_feature_access_manager) {
   multidevice_setup_client_->AddObserver(this);
-  notification_access_manager_->AddObserver(this);
+  multidevice_feature_access_manager_->AddObserver(this);
 }
 
 RecentAppsInteractionHandlerImpl::~RecentAppsInteractionHandlerImpl() {
   multidevice_setup_client_->RemoveObserver(this);
-  notification_access_manager_->RemoveObserver(this);
+  multidevice_feature_access_manager_->RemoveObserver(this);
 }
 
 void RecentAppsInteractionHandlerImpl::AddRecentAppClickObserver(
@@ -204,8 +204,8 @@
         multidevice_setup_client_->GetFeatureState(
             Feature::kPhoneHubNotifications) == FeatureState::kEnabledByUser;
     bool grant_notification_access_on_host =
-        notification_access_manager_->GetAccessStatus() ==
-        phonehub::NotificationAccessManager::AccessStatus::kAccessGranted;
+        multidevice_feature_access_manager_->GetNotificationAccessStatus() ==
+        phonehub::MultideviceFeatureAccessManager::AccessStatus::kAccessGranted;
     if (notifications_enabled && grant_notification_access_on_host)
       ui_state_ = RecentAppsUiState::PLACEHOLDER_VIEW;
   } else {
diff --git a/ash/components/phonehub/recent_apps_interaction_handler_impl.h b/ash/components/phonehub/recent_apps_interaction_handler_impl.h
index e17fd40..eb625eb 100644
--- a/ash/components/phonehub/recent_apps_interaction_handler_impl.h
+++ b/ash/components/phonehub/recent_apps_interaction_handler_impl.h
@@ -7,8 +7,8 @@
 
 #include <stdint.h>
 
+#include "ash/components/phonehub/multidevice_feature_access_manager.h"
 #include "ash/components/phonehub/notification.h"
-#include "ash/components/phonehub/notification_access_manager.h"
 #include "ash/components/phonehub/recent_app_click_observer.h"
 #include "ash/components/phonehub/recent_apps_interaction_handler.h"
 #include "ash/services/multidevice_setup/public/cpp/multidevice_setup_client.h"
@@ -26,14 +26,14 @@
 class RecentAppsInteractionHandlerImpl
     : public RecentAppsInteractionHandler,
       public multidevice_setup::MultiDeviceSetupClient::Observer,
-      public NotificationAccessManager::Observer {
+      public MultideviceFeatureAccessManager::Observer {
  public:
   static void RegisterPrefs(PrefRegistrySimple* registry);
 
   explicit RecentAppsInteractionHandlerImpl(
       PrefService* pref_service,
       multidevice_setup::MultiDeviceSetupClient* multidevice_setup_client,
-      NotificationAccessManager* notification_access_manager);
+      MultideviceFeatureAccessManager* multidevice_feature_access_manager);
   ~RecentAppsInteractionHandlerImpl() override;
 
   // RecentAppsInteractionHandler:
@@ -54,7 +54,7 @@
       const multidevice_setup::MultiDeviceSetupClient::HostStatusWithDevice&
           host_device_with_status) override;
 
-  // NotificationAccessManager::Observer:
+  // MultideviceFeatureAccessManager::Observer:
   void OnNotificationAccessChanged() override;
 
  private:
@@ -75,7 +75,7 @@
       recent_app_metadata_list_;
   PrefService* pref_service_;
   multidevice_setup::MultiDeviceSetupClient* multidevice_setup_client_;
-  NotificationAccessManager* notification_access_manager_;
+  MultideviceFeatureAccessManager* multidevice_feature_access_manager_;
 };
 
 }  // namespace phonehub
diff --git a/ash/components/phonehub/recent_apps_interaction_handler_impl_unittest.cc b/ash/components/phonehub/recent_apps_interaction_handler_impl_unittest.cc
index 630ce84..8f77fdf1 100644
--- a/ash/components/phonehub/recent_apps_interaction_handler_impl_unittest.cc
+++ b/ash/components/phonehub/recent_apps_interaction_handler_impl_unittest.cc
@@ -6,7 +6,7 @@
 
 #include <memory>
 
-#include "ash/components/phonehub/fake_notification_access_manager.h"
+#include "ash/components/phonehub/fake_multidevice_feature_access_manager.h"
 #include "ash/components/phonehub/notification.h"
 #include "ash/components/phonehub/pref_names.h"
 #include "ash/services/multidevice_setup/public/cpp/fake_multidevice_setup_client.h"
@@ -55,7 +55,7 @@
         std::make_unique<multidevice_setup::FakeMultiDeviceSetupClient>();
     interaction_handler_ = std::make_unique<RecentAppsInteractionHandlerImpl>(
         &pref_service_, fake_multidevice_setup_client_.get(),
-        &fake_notification_access_manager_);
+        &fake_multidevice_feature_access_manager_);
     interaction_handler_->AddRecentAppClickObserver(&fake_click_handler_);
   }
 
@@ -107,10 +107,12 @@
   }
 
   void SetNotificationAccess(bool enabled) {
-    fake_notification_access_manager_.SetAccessStatusInternal(
-        enabled
-            ? NotificationAccessManager::AccessStatus::kAccessGranted
-            : NotificationAccessManager::AccessStatus::kAvailableButNotGranted);
+    fake_multidevice_feature_access_manager_
+        .SetNotificationAccessStatusInternal(
+            enabled
+                ? MultideviceFeatureAccessManager::AccessStatus::kAccessGranted
+                : MultideviceFeatureAccessManager::AccessStatus::
+                      kAvailableButNotGranted);
   }
 
   std::vector<RecentAppsInteractionHandler::UserState> GetDefaultUserStates() {
@@ -178,7 +180,7 @@
   FakeClickHandler fake_click_handler_;
   std::unique_ptr<RecentAppsInteractionHandlerImpl> interaction_handler_;
   TestingPrefServiceSimple pref_service_;
-  FakeNotificationAccessManager fake_notification_access_manager_;
+  FakeMultideviceFeatureAccessManager fake_multidevice_feature_access_manager_;
 };
 
 TEST_F(RecentAppsInteractionHandlerTest, RecentAppsClicked) {
diff --git a/ash/display/mirror_window_controller.cc b/ash/display/mirror_window_controller.cc
index 8ffea3af..2792896 100644
--- a/ash/display/mirror_window_controller.cc
+++ b/ash/display/mirror_window_controller.cc
@@ -192,7 +192,7 @@
       AshWindowTreeHostInitParams init_params;
       init_params.initial_bounds = display_info.bounds_in_native();
       init_params.display_id = display_info.id();
-      init_params.mirroring_delegate = this;
+      init_params.delegate = this;
       init_params.mirroring_unified = display_manager->IsInUnifiedMode();
       init_params.device_scale_factor = display_info.device_scale_factor();
       MirroringHostInfo* host_info = new MirroringHostInfo;
@@ -367,7 +367,7 @@
     if (pair.second->ash_host->AsWindowTreeHost()->window() == root) {
       // Sanity check to catch an error early.
       const int64_t id = pair.first;
-      const display::Display* display = GetMirroringDisplayById(id);
+      const display::Display* display = GetDisplayById(id);
       DCHECK(display);
       if (display)
         return *display;
@@ -390,7 +390,7 @@
   return root_windows;
 }
 
-const display::Display* MirrorWindowController::GetMirroringDisplayById(
+const display::Display* MirrorWindowController::GetDisplayById(
     int64_t display_id) const {
   const display::Displays& list =
       Shell::Get()->display_manager()->software_mirroring_display_list();
diff --git a/ash/display/mirror_window_controller.h b/ash/display/mirror_window_controller.h
index d50189b..6deca66 100644
--- a/ash/display/mirror_window_controller.h
+++ b/ash/display/mirror_window_controller.h
@@ -12,7 +12,7 @@
 #include <vector>
 
 #include "ash/ash_export.h"
-#include "ash/host/ash_window_tree_host_mirroring_delegate.h"
+#include "ash/host/ash_window_tree_host_delegate.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_tree_host_observer.h"
 #include "ui/display/manager/display_manager.h"
@@ -37,9 +37,8 @@
 // An object that copies the content of the primary root window to a
 // mirror window. This also draws a mouse cursor as the mouse cursor
 // is typically drawn by the window system.
-class ASH_EXPORT MirrorWindowController
-    : public aura::WindowTreeHostObserver,
-      public AshWindowTreeHostMirroringDelegate {
+class ASH_EXPORT MirrorWindowController : public aura::WindowTreeHostObserver,
+                                          public AshWindowTreeHostDelegate {
  public:
   MirrorWindowController();
 
@@ -72,9 +71,8 @@
   // Returns all root windows hosting mirroring displays.
   aura::Window::Windows GetAllRootWindows() const;
 
-  // AshWindowTreeHostMirroringDelegate:
-  const display::Display* GetMirroringDisplayById(
-      int64_t display_id) const override;
+  // AshWindowTreeHostDelegate:
+  const display::Display* GetDisplayById(int64_t display_id) const override;
   void SetCurrentEventTargeterSourceHost(
       aura::WindowTreeHost* targeter_src_host) override;
 
diff --git a/ash/display/window_tree_host_manager.cc b/ash/display/window_tree_host_manager.cc
index df3c6ee..ccb99ecf 100644
--- a/ash/display/window_tree_host_manager.cc
+++ b/ash/display/window_tree_host_manager.cc
@@ -27,10 +27,10 @@
 #include "ash/system/unified/unified_system_tray.h"
 #include "ash/wm/window_util.h"
 #include "base/bind.h"
-#include "base/callback_helpers.h"
 #include "base/command_line.h"
 #include "base/metrics/histogram.h"
 #include "base/metrics/histogram_functions.h"
+#include "base/notreached.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/threading/thread_task_runner_handle.h"
@@ -68,13 +68,8 @@
 // This is initialized in the constructor, and then in CreatePrimaryHost().
 int64_t primary_display_id = -1;
 
-// The default compositor memory limit.
-constexpr char kUICompositorDefaultMemoryLimitMB[] = "512";
-// The compositor memory limit when display size is larger than a threshold.
-constexpr char kUICompositorLargeMemoryLimitMB[] = "1024";
-// The display size threshold, above which the larger memory limit is used.
-// Pixel size was chosen to trigger for 4K+ displays. See: crbug.com/1261776
-constexpr int kUICompositorMemoryLimitDisplaySizeThreshold = 3500;
+// The default memory limit: 512mb.
+const char kUICompositorDefaultMemoryLimitMB[] = "512";
 
 // An UMA signal for the current effective resolution is sent at this rate. This
 // keeps track of the effective resolution most used on internal display by the
@@ -91,10 +86,9 @@
   const display::Display::Rotation effective_rotation =
       display.panel_rotation();
   aura::WindowTreeHost* host = ash_host->AsWindowTreeHost();
-  ash_host->SetCursorConfig(display, effective_rotation);
-  std::unique_ptr<RootWindowTransformer> transformer(
+  ash_host->UpdateCursorConfig();
+  ash_host->SetRootWindowTransformer(
       CreateRootWindowTransformerForDisplay(display));
-  ash_host->SetRootWindowTransformer(std::move(transformer));
 
   host->SetDisplayTransformHint(
       display::DisplayRotationToOverlayTransform(effective_rotation));
@@ -304,6 +298,14 @@
 
 void WindowTreeHostManager::CreatePrimaryHost(
     const AshWindowTreeHostInitParams& init_params) {
+  auto* command_line = base::CommandLine::ForCurrentProcess();
+  if (!command_line->HasSwitch(
+          switches::kUiCompositorMemoryLimitWhenVisibleMB)) {
+    command_line->AppendSwitchASCII(
+        switches::kUiCompositorMemoryLimitWhenVisibleMB,
+        kUICompositorDefaultMemoryLimitMB);
+  }
+
   const display::Display& primary_candidate =
       GetDisplayManager()->GetPrimaryDisplayCandidate();
   primary_display_id = primary_candidate.id();
@@ -857,6 +859,18 @@
   return root_window->GetHost()->DispatchKeyEventPostIME(event);
 }
 
+const display::Display* WindowTreeHostManager::GetDisplayById(
+    int64_t display_id) const {
+  const display::Display& display =
+      GetDisplayManager()->GetDisplayForId(display_id);
+  return display.id() == display::kInvalidDisplayId ? nullptr : &display;
+}
+
+void WindowTreeHostManager::SetCurrentEventTargeterSourceHost(
+    aura::WindowTreeHost* targeter_src_host) {
+  NOTIMPLEMENTED();
+}
+
 AshWindowTreeHost* WindowTreeHostManager::AddWindowTreeHostForDisplay(
     const display::Display& display,
     const AshWindowTreeHostInitParams& init_params) {
@@ -867,33 +881,12 @@
   params_with_bounds.initial_bounds = display_info.bounds_in_native();
   if (display.id() == display::kUnifiedDisplayId) {
     params_with_bounds.offscreen = true;
-    params_with_bounds.mirroring_delegate = mirror_window_controller();
+    params_with_bounds.delegate = mirror_window_controller();
+  } else {
+    params_with_bounds.delegate = this;
   }
   params_with_bounds.display_id = display.id();
   params_with_bounds.device_scale_factor = display.device_scale_factor();
-
-  auto* command_line = base::CommandLine::ForCurrentProcess();
-  base::ScopedClosureRunner switch_remover;
-  if (!command_line->HasSwitch(
-          switches::kUiCompositorMemoryLimitWhenVisibleMB)) {
-    // TODO(crbug/1261776): Temporarily increase compositor memory limit for
-    // 4K+ displays to avoid rendering corruption.
-    // Check both width and height in case of rotated display.
-    const char* memory_limit =
-        std::max(display.GetSizeInPixel().width(),
-                 display.GetSizeInPixel().height()) >
-                kUICompositorMemoryLimitDisplaySizeThreshold
-            ? kUICompositorLargeMemoryLimitMB
-            : kUICompositorDefaultMemoryLimitMB;
-    command_line->AppendSwitchASCII(
-        switches::kUiCompositorMemoryLimitWhenVisibleMB, memory_limit);
-    switch_remover.ReplaceClosure(base::BindOnce(
-        [](base::CommandLine* cmd) {
-          cmd->RemoveSwitch(switches::kUiCompositorMemoryLimitWhenVisibleMB);
-        },
-        command_line));
-  }
-
   // The AshWindowTreeHost ends up owned by the RootWindowControllers created
   // by this class.
   AshWindowTreeHost* ash_host =
diff --git a/ash/display/window_tree_host_manager.h b/ash/display/window_tree_host_manager.h
index 336aa5ed..49bfa260 100644
--- a/ash/display/window_tree_host_manager.h
+++ b/ash/display/window_tree_host_manager.h
@@ -12,6 +12,7 @@
 #include <vector>
 
 #include "ash/ash_export.h"
+#include "ash/host/ash_window_tree_host_delegate.h"
 #include "base/compiler_specific.h"
 #include "base/gtest_prod_util.h"
 #include "base/memory/weak_ptr.h"
@@ -49,7 +50,8 @@
       public aura::WindowTreeHostObserver,
       public display::ContentProtectionManager::Observer,
       public display::DisplayManager::Delegate,
-      public ui::internal::InputMethodDelegate {
+      public ui::internal::InputMethodDelegate,
+      public AshWindowTreeHostDelegate {
  public:
   // TODO(oshima): Consider moving this to display::DisplayObserver.
   class ASH_EXPORT Observer {
@@ -165,6 +167,11 @@
   ui::EventDispatchDetails DispatchKeyEventPostIME(
       ui::KeyEvent* event) override;
 
+  // ash::AshWindowTreeHostDelegate overrides:
+  const display::Display* GetDisplayById(int64_t display_id) const override;
+  void SetCurrentEventTargeterSourceHost(
+      aura::WindowTreeHost* targeter_src_host) override;
+
  private:
   FRIEND_TEST_ALL_PREFIXES(WindowTreeHostManagerTest, BoundsUpdated);
   FRIEND_TEST_ALL_PREFIXES(WindowTreeHostManagerTest, SecondaryDisplayLayout);
diff --git a/ash/drag_drop/drag_drop_controller.cc b/ash/drag_drop/drag_drop_controller.cc
index 7c49df69..7072ff7b 100644
--- a/ash/drag_drop/drag_drop_controller.cc
+++ b/ash/drag_drop/drag_drop_controller.cc
@@ -714,6 +714,11 @@
 }
 
 void DragDropController::Cleanup() {
+  if (!capture_delegate_ && drag_source_window_ &&
+      current_drag_event_source_ == ui::mojom::DragEventSource::kMouse) {
+    drag_source_window_->ReleaseCapture();
+  }
+
   // Do not remove observer `the drag_window_1 is same as `drag_source_window_`.
   // `drag_source_window_` is still necessary to process long tab and the
   // observer will be reset when `drag_source_window_` is destroyed.
diff --git a/ash/drag_drop/drag_drop_controller.h b/ash/drag_drop/drag_drop_controller.h
index 05d6549..06e1fda 100644
--- a/ash/drag_drop/drag_drop_controller.h
+++ b/ash/drag_drop/drag_drop_controller.h
@@ -193,7 +193,7 @@
   base::OnceClosure quit_closure_;
 
   // If non-null, a drag is active which required a capture window.
-  DragDropCaptureDelegate* capture_delegate_;
+  DragDropCaptureDelegate* capture_delegate_ = nullptr;
 
   ui::mojom::DragEventSource current_drag_event_source_ =
       ui::mojom::DragEventSource::kMouse;
diff --git a/ash/drag_drop/drag_drop_controller_unittest.cc b/ash/drag_drop/drag_drop_controller_unittest.cc
index 6904f940..7d60498e 100644
--- a/ash/drag_drop/drag_drop_controller_unittest.cc
+++ b/ash/drag_drop/drag_drop_controller_unittest.cc
@@ -586,6 +586,36 @@
   EXPECT_TRUE(drag_view->drag_done_received_);
 }
 
+TEST_F(DragDropControllerTest, DragDropMouseReleasesWindowCapture) {
+  std::unique_ptr<views::Widget> widget = CreateFramelessWidget();
+  DragTestView* drag_view = new DragTestView;
+  AddViewToWidgetAndResize(widget.get(), drag_view);
+  aura::Window* window = widget->GetNativeView();
+
+  EXPECT_FALSE(window->HasCapture());
+
+  ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow(), window);
+
+  generator.PressLeftButton();
+  window->SetCapture();  // aura::Window do not explicitly take capture, so call
+                         // this
+  // manually to simulate dragging a view which does take capture.
+  EXPECT_TRUE(window->HasCapture());
+
+  int n = 0;
+  auto loop_task = [&](bool inside) {
+    generator.MoveMouseBy(0, 1);
+    n++;
+
+    if (n == 17)
+      generator.ReleaseLeftButton();
+  };
+  RunWithClosure(base::BindLambdaForTesting(loop_task));
+
+  EXPECT_TRUE(drag_view->drag_done_received_);
+  EXPECT_FALSE(window->HasCapture());
+}
+
 TEST_F(DragDropControllerTest, DragDropWithZeroDragUpdates) {
   std::unique_ptr<views::Widget> widget = CreateFramelessWidget();
   DragTestView* drag_view = new DragTestView;
diff --git a/ash/host/DEPS b/ash/host/DEPS
index 9f1d28bc..dba3bec9 100644
--- a/ash/host/DEPS
+++ b/ash/host/DEPS
@@ -8,5 +8,9 @@
 specific_include_rules = {
   "ash_window_tree_host_platform_unittest.cc" : [
     "+ash/test/ash_test_base.h",
-  ]
+  ],
+  "ash_window_tree_host_unified_unittest.cc" : [
+    "+ash/display/mirror_window_test_api.h",
+    "+ash/test/ash_test_base.h",
+  ],
 }
diff --git a/ash/host/ash_window_tree_host.cc b/ash/host/ash_window_tree_host.cc
index 907fb9c..014eb97 100644
--- a/ash/host/ash_window_tree_host.cc
+++ b/ash/host/ash_window_tree_host.cc
@@ -70,16 +70,17 @@
   if (init_params.mirroring_unified) {
     return std::make_unique<AshWindowTreeHostMirroringUnified>(
         init_params.initial_bounds, init_params.display_id,
-        init_params.mirroring_delegate);
+        init_params.delegate);
   }
   if (init_params.offscreen) {
     return std::make_unique<AshWindowTreeHostUnified>(
-        init_params.initial_bounds, init_params.mirroring_delegate);
+        init_params.initial_bounds, init_params.delegate);
   }
   return std::make_unique<AshWindowTreeHostPlatform>(
       ui::PlatformWindowInitProperties{
           init_params.initial_bounds,
-          features::IsCompositingBasedThrottlingEnabled()});
+          features::IsCompositingBasedThrottlingEnabled()},
+      init_params.delegate);
 }
 
 }  // namespace ash
diff --git a/ash/host/ash_window_tree_host.h b/ash/host/ash_window_tree_host.h
index 7bb22aae..eed5fda 100644
--- a/ash/host/ash_window_tree_host.h
+++ b/ash/host/ash_window_tree_host.h
@@ -58,8 +58,7 @@
 
   virtual void RegisterMirroringHost(AshWindowTreeHost* mirroring_ash_host) {}
 
-  virtual void SetCursorConfig(const display::Display& display,
-                               display::Display::Rotation rotation) = 0;
+  virtual void UpdateCursorConfig() = 0;
   virtual void ClearCursorConfig() = 0;
 
  protected:
diff --git a/ash/host/ash_window_tree_host_delegate.h b/ash/host/ash_window_tree_host_delegate.h
new file mode 100644
index 0000000..ea03e77
--- /dev/null
+++ b/ash/host/ash_window_tree_host_delegate.h
@@ -0,0 +1,45 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_HOST_ASH_WINDOW_TREE_HOST_DELEGATE_H_
+#define ASH_HOST_ASH_WINDOW_TREE_HOST_DELEGATE_H_
+
+#include <stdint.h>
+
+#include "ash/ash_export.h"
+
+namespace aura {
+class WindowTreeHost;
+}  // namespace aura
+
+namespace display {
+class Display;
+}  // namespace display
+
+namespace ash {
+
+// A delegate for the window tree host.
+class ASH_EXPORT AshWindowTreeHostDelegate {
+ public:
+  AshWindowTreeHostDelegate() = default;
+
+  AshWindowTreeHostDelegate(const AshWindowTreeHostDelegate&) = delete;
+  AshWindowTreeHostDelegate& operator=(const AshWindowTreeHostDelegate&) =
+      delete;
+
+  virtual ~AshWindowTreeHostDelegate() = default;
+
+  // Returns a pointer to the display with |display_id| if found, or nullptr if
+  // not found.
+  virtual const display::Display* GetDisplayById(int64_t display_id) const = 0;
+
+  // Sets the current window tree host that is the source of events which will
+  // be forwarded by the unified mode event targeter to the unified host.
+  virtual void SetCurrentEventTargeterSourceHost(
+      aura::WindowTreeHost* targeter_src_host) = 0;
+};
+
+}  // namespace ash
+
+#endif  // ASH_HOST_ASH_WINDOW_TREE_HOST_DELEGATE_H_
diff --git a/ash/host/ash_window_tree_host_init_params.h b/ash/host/ash_window_tree_host_init_params.h
index e322ab7..4a40ae9 100644
--- a/ash/host/ash_window_tree_host_init_params.h
+++ b/ash/host/ash_window_tree_host_init_params.h
@@ -12,11 +12,11 @@
 
 namespace ash {
 
-class AshWindowTreeHostMirroringDelegate;
+class AshWindowTreeHostDelegate;
 
 struct ASH_EXPORT AshWindowTreeHostInitParams {
   // Not owned.
-  AshWindowTreeHostMirroringDelegate* mirroring_delegate = nullptr;
+  AshWindowTreeHostDelegate* delegate = nullptr;
   // This corresponds to display::ManagedDisplayInfo::bounds_in_native.
   gfx::Rect initial_bounds;
   bool offscreen = false;
diff --git a/ash/host/ash_window_tree_host_mirroring_delegate.h b/ash/host/ash_window_tree_host_mirroring_delegate.h
deleted file mode 100644
index 1722be68..0000000
--- a/ash/host/ash_window_tree_host_mirroring_delegate.h
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef ASH_HOST_ASH_WINDOW_TREE_HOST_MIRRORING_DELEGATE_H_
-#define ASH_HOST_ASH_WINDOW_TREE_HOST_MIRRORING_DELEGATE_H_
-
-#include <stdint.h>
-
-#include "ash/ash_export.h"
-
-namespace aura {
-class WindowTreeHost;
-}  // namespace aura
-
-namespace display {
-class Display;
-}  // namespace display
-
-namespace ash {
-
-// A delegate for the unified window tree host as well as the mirroring display
-// hosts.
-class ASH_EXPORT AshWindowTreeHostMirroringDelegate {
- public:
-  AshWindowTreeHostMirroringDelegate() = default;
-
-  AshWindowTreeHostMirroringDelegate(
-      const AshWindowTreeHostMirroringDelegate&) = delete;
-  AshWindowTreeHostMirroringDelegate& operator=(
-      const AshWindowTreeHostMirroringDelegate&) = delete;
-
-  virtual ~AshWindowTreeHostMirroringDelegate() = default;
-
-  // Returns a pointer to the mirroring display with |display_id| if found, or
-  // nullptr if not found.
-  virtual const display::Display* GetMirroringDisplayById(
-      int64_t display_id) const = 0;
-
-  // Sets the current window tree host that is the source of events which will
-  // be forwarded by the unified mode event targeter to the unified host.
-  virtual void SetCurrentEventTargeterSourceHost(
-      aura::WindowTreeHost* targeter_src_host) = 0;
-};
-
-}  // namespace ash
-
-#endif  // ASH_HOST_ASH_WINDOW_TREE_HOST_MIRRORING_DELEGATE_H_
diff --git a/ash/host/ash_window_tree_host_mirroring_unified.cc b/ash/host/ash_window_tree_host_mirroring_unified.cc
index b835db26..2185f75 100644
--- a/ash/host/ash_window_tree_host_mirroring_unified.cc
+++ b/ash/host/ash_window_tree_host_mirroring_unified.cc
@@ -4,7 +4,7 @@
 
 #include "ash/host/ash_window_tree_host_mirroring_unified.h"
 
-#include "ash/host/ash_window_tree_host_mirroring_delegate.h"
+#include "ash/host/ash_window_tree_host_delegate.h"
 #include "ui/gfx/geometry/point3_f.h"
 #include "ui/gfx/geometry/point_conversions.h"
 #include "ui/gfx/geometry/transform.h"
@@ -15,11 +15,11 @@
 AshWindowTreeHostMirroringUnified::AshWindowTreeHostMirroringUnified(
     const gfx::Rect& initial_bounds,
     int64_t mirroring_display_id,
-    AshWindowTreeHostMirroringDelegate* delegate)
+    AshWindowTreeHostDelegate* delegate)
     : AshWindowTreeHostPlatform(
-          ui::PlatformWindowInitProperties{initial_bounds}),
-      mirroring_display_id_(mirroring_display_id),
-      delegate_(delegate) {
+          ui::PlatformWindowInitProperties{initial_bounds},
+          delegate),
+      mirroring_display_id_(mirroring_display_id) {
   DCHECK(delegate_);
 }
 
@@ -32,8 +32,7 @@
   gfx::Transform trans = GetRootTransform();
 
   if (!is_shutting_down_) {
-    const auto* display =
-        delegate_->GetMirroringDisplayById(mirroring_display_id_);
+    const auto* display = delegate_->GetDisplayById(mirroring_display_id_);
     DCHECK(display);
     // Undo the translation in the root window transform, since this transform
     // should be applied on local points to this host.
diff --git a/ash/host/ash_window_tree_host_mirroring_unified.h b/ash/host/ash_window_tree_host_mirroring_unified.h
index 756967bd..63ab009 100644
--- a/ash/host/ash_window_tree_host_mirroring_unified.h
+++ b/ash/host/ash_window_tree_host_mirroring_unified.h
@@ -9,17 +9,16 @@
 
 namespace ash {
 
-class AshWindowTreeHostMirroringDelegate;
+class AshWindowTreeHostDelegate;
 
 // A window tree host for the mirroing displays that constitute the unified
 // desktop. This correctly handles coordinates conversion from DIP to pixels and
 // vice versa.
 class AshWindowTreeHostMirroringUnified : public AshWindowTreeHostPlatform {
  public:
-  AshWindowTreeHostMirroringUnified(
-      const gfx::Rect& initial_bounds,
-      int64_t mirroring_display_id,
-      AshWindowTreeHostMirroringDelegate* delegate);
+  AshWindowTreeHostMirroringUnified(const gfx::Rect& initial_bounds,
+                                    int64_t mirroring_display_id,
+                                    AshWindowTreeHostDelegate* delegate);
 
   AshWindowTreeHostMirroringUnified(const AshWindowTreeHostMirroringUnified&) =
       delete;
@@ -42,8 +41,6 @@
  private:
   int64_t mirroring_display_id_;
 
-  AshWindowTreeHostMirroringDelegate* delegate_;  // Not owned.
-
   bool is_shutting_down_ = false;
 };
 
diff --git a/ash/host/ash_window_tree_host_platform.cc b/ash/host/ash_window_tree_host_platform.cc
index 26961d5f..14c3275e9 100644
--- a/ash/host/ash_window_tree_host_platform.cc
+++ b/ash/host/ash_window_tree_host_platform.cc
@@ -6,6 +6,7 @@
 
 #include <utility>
 
+#include "ash/host/ash_window_tree_host_delegate.h"
 #include "ash/host/root_window_transformer.h"
 #include "ash/host/transformer_helper.h"
 #include "base/feature_list.h"
@@ -30,8 +31,8 @@
     : public aura::ScopedEnableUnadjustedMouseEvents {
  public:
   explicit ScopedEnableUnadjustedMouseEventsOzone(
-      ui::InputController* input_controller) {
-    input_controller_ = input_controller;
+      ui::InputController* input_controller)
+      : input_controller_(input_controller) {
     input_controller_->SuspendMouseAcceleration();
   }
 
@@ -44,18 +45,24 @@
 };
 
 AshWindowTreeHostPlatform::AshWindowTreeHostPlatform(
-    ui::PlatformWindowInitProperties properties)
+    ui::PlatformWindowInitProperties properties,
+    AshWindowTreeHostDelegate* delegate)
     : aura::WindowTreeHostPlatform(std::move(properties),
                                    std::make_unique<aura::Window>(nullptr)),
+      delegate_(delegate),
       transformer_helper_(this),
       input_controller_(
           ui::OzonePlatform::GetInstance()->GetInputController()) {
+  DCHECK(delegate_);
   CommonInit();
 }
 
-AshWindowTreeHostPlatform::AshWindowTreeHostPlatform()
+AshWindowTreeHostPlatform::AshWindowTreeHostPlatform(
+    AshWindowTreeHostDelegate* delegate)
     : aura::WindowTreeHostPlatform(std::make_unique<aura::Window>(nullptr)),
+      delegate_(delegate),
       transformer_helper_(this) {
+  DCHECK(delegate_);
   CreateCompositor(/* force_software_compositor */ false,
                    /* use_external_begin_frame_control */ false);
   CommonInit();
@@ -90,17 +97,23 @@
   return last_cursor_confine_bounds_in_pixels_;
 }
 
-void AshWindowTreeHostPlatform::SetCursorConfig(
-    const display::Display& display,
-    display::Display::Rotation rotation) {
-  // Scale all motion on High-DPI displays.
-  float scale = display.device_scale_factor();
+void AshWindowTreeHostPlatform::UpdateCursorConfig() {
+  const display::Display* display = delegate_->GetDisplayById(GetDisplayId());
+  if (!display) {
+    LOG(ERROR)
+        << "While updating cursor config, could not find display with id="
+        << GetDisplayId();
+    return;
+  }
 
-  if (!display.IsInternal())
+  // Scale all motion on High-DPI displays.
+  float scale = display->device_scale_factor();
+
+  if (!display->IsInternal())
     scale *= 1.2;
 
   ui::CursorController::GetInstance()->SetCursorConfigForWindow(
-      GetAcceleratedWidget(), rotation, scale);
+      GetAcceleratedWidget(), display->panel_rotation(), scale);
 }
 
 void AshWindowTreeHostPlatform::ClearCursorConfig() {
diff --git a/ash/host/ash_window_tree_host_platform.h b/ash/host/ash_window_tree_host_platform.h
index 90fc8607..1d436ff 100644
--- a/ash/host/ash_window_tree_host_platform.h
+++ b/ash/host/ash_window_tree_host_platform.h
@@ -19,6 +19,8 @@
 }
 
 namespace ash {
+class AshWindowTreeHostDelegate;
+
 class ExtendedMouseWarpControllerTest;
 class AshWindowTreeHostPlatformTest;
 
@@ -26,8 +28,8 @@
     : public AshWindowTreeHost,
       public aura::WindowTreeHostPlatform {
  public:
-  explicit AshWindowTreeHostPlatform(
-      ui::PlatformWindowInitProperties properties);
+  AshWindowTreeHostPlatform(ui::PlatformWindowInitProperties properties,
+                            AshWindowTreeHostDelegate* delegate);
 
   AshWindowTreeHostPlatform(const AshWindowTreeHostPlatform&) = delete;
   AshWindowTreeHostPlatform& operator=(const AshWindowTreeHostPlatform&) =
@@ -42,7 +44,7 @@
   friend AshWindowTreeHostPlatformTest;
   FRIEND_TEST_ALL_PREFIXES(AshWindowTreeHostPlatformTest, UnadjustedMovement);
 
-  AshWindowTreeHostPlatform();
+  explicit AshWindowTreeHostPlatform(AshWindowTreeHostDelegate* delegate);
 
   // AshWindowTreeHost:
   void ConfineCursorToRootWindow() override;
@@ -53,8 +55,7 @@
   gfx::Insets GetHostInsets() const override;
   aura::WindowTreeHost* AsWindowTreeHost() override;
   void PrepareForShutdown() override;
-  void SetCursorConfig(const display::Display& display,
-                       display::Display::Rotation rotation) override;
+  void UpdateCursorConfig() override;
   void ClearCursorConfig() override;
 
   // aura::WindowTreeHostPlatform:
@@ -69,6 +70,8 @@
   std::unique_ptr<aura::ScopedEnableUnadjustedMouseEvents>
   RequestUnadjustedMovement() override;
 
+  AshWindowTreeHostDelegate* delegate_ = nullptr;  // Not owned.
+
  private:
   // All constructors call into this.
   void CommonInit();
@@ -78,7 +81,7 @@
 
   TransformerHelper transformer_helper_;
 
-  ui::InputController* input_controller_;
+  ui::InputController* input_controller_ = nullptr;
 
   gfx::Rect last_cursor_confine_bounds_in_pixels_;
 };
diff --git a/ash/host/ash_window_tree_host_platform_unittest.cc b/ash/host/ash_window_tree_host_platform_unittest.cc
index 563bb348..d417e40 100644
--- a/ash/host/ash_window_tree_host_platform_unittest.cc
+++ b/ash/host/ash_window_tree_host_platform_unittest.cc
@@ -4,8 +4,8 @@
 
 #include "ash/host/ash_window_tree_host_platform.h"
 
+#include "ash/host/ash_window_tree_host_delegate.h"
 #include "ash/test/ash_test_base.h"
-
 #include "ui/events/devices/haptic_touchpad_effects.h"
 #include "ui/events/devices/stylus_state.h"
 #include "ui/ozone/public/input_controller.h"
@@ -13,15 +13,13 @@
 
 namespace ash {
 
-class AshWindowTreeHostPlatformTest : public AshTestBase {
- public:
-  AshWindowTreeHostPlatformTest() = default;
-  ~AshWindowTreeHostPlatformTest() override = default;
-};
+namespace {
 
 class TestInputController : public ui::InputController {
  public:
   TestInputController() = default;
+  TestInputController(const TestInputController&) = delete;
+  TestInputController& operator=(const TestInputController&) = delete;
   ~TestInputController() override = default;
 
   // InputController:
@@ -107,8 +105,34 @@
   bool acceleration_suspended_ = false;
 };
 
+class FakeAshWindowTreeHostDelegate : public AshWindowTreeHostDelegate {
+ public:
+  FakeAshWindowTreeHostDelegate() = default;
+  FakeAshWindowTreeHostDelegate(const FakeAshWindowTreeHostDelegate&) = delete;
+  FakeAshWindowTreeHostDelegate& operator=(
+      const FakeAshWindowTreeHostDelegate&) = delete;
+  ~FakeAshWindowTreeHostDelegate() override = default;
+
+  const display::Display* GetDisplayById(int64_t display_id) const override {
+    return nullptr;
+  }
+  void SetCurrentEventTargeterSourceHost(aura::WindowTreeHost*) override {}
+};
+
+}  // namespace
+
+class AshWindowTreeHostPlatformTest : public AshTestBase {
+ public:
+  AshWindowTreeHostPlatformTest() = default;
+  AshWindowTreeHostPlatformTest(const AshWindowTreeHostPlatformTest&) = delete;
+  AshWindowTreeHostPlatformTest& operator=(
+      const AshWindowTreeHostPlatformTest&) = delete;
+  ~AshWindowTreeHostPlatformTest() override = default;
+};
+
 TEST_F(AshWindowTreeHostPlatformTest, UnadjustedMovement) {
-  AshWindowTreeHostPlatform host;
+  FakeAshWindowTreeHostDelegate fake_delegate;
+  AshWindowTreeHostPlatform host(&fake_delegate);
   auto test_input_controller = std::make_unique<TestInputController>();
   host.input_controller_ = test_input_controller.get();
 
diff --git a/ash/host/ash_window_tree_host_unified.cc b/ash/host/ash_window_tree_host_unified.cc
index bf35a0a5..57ca082 100644
--- a/ash/host/ash_window_tree_host_unified.cc
+++ b/ash/host/ash_window_tree_host_unified.cc
@@ -8,7 +8,7 @@
 #include <tuple>
 #include <utility>
 
-#include "ash/host/ash_window_tree_host_mirroring_delegate.h"
+#include "ash/host/ash_window_tree_host_delegate.h"
 #include "ash/host/root_window_transformer.h"
 #include "base/check.h"
 #include "base/containers/contains.h"
@@ -26,7 +26,7 @@
  public:
   UnifiedEventTargeter(aura::Window* src_root,
                        aura::Window* dst_root,
-                       AshWindowTreeHostMirroringDelegate* delegate)
+                       AshWindowTreeHostDelegate* delegate)
       : src_root_(src_root), dst_root_(dst_root), delegate_(delegate) {
     DCHECK(delegate);
   }
@@ -60,14 +60,13 @@
  private:
   aura::Window* src_root_;
   aura::Window* dst_root_;
-  AshWindowTreeHostMirroringDelegate* delegate_;  // Not owned.
+  AshWindowTreeHostDelegate* delegate_;  // Not owned.
 };
 
 AshWindowTreeHostUnified::AshWindowTreeHostUnified(
     const gfx::Rect& initial_bounds,
-    AshWindowTreeHostMirroringDelegate* delegate)
-    : AshWindowTreeHostPlatform(), delegate_(delegate) {
-  DCHECK(delegate);
+    AshWindowTreeHostDelegate* delegate)
+    : AshWindowTreeHostPlatform(delegate) {
   std::unique_ptr<ui::PlatformWindow> window(new ui::StubWindow(this));
   window->SetBounds(initial_bounds);
   SetPlatformWindow(std::move(window));
@@ -92,7 +91,17 @@
       std::make_unique<UnifiedEventTargeter>(src_root, window(), delegate_));
   DCHECK(!base::Contains(mirroring_hosts_, mirroring_ash_host));
   mirroring_hosts_.push_back(mirroring_ash_host);
-  mirroring_ash_host->AsWindowTreeHost()->window()->AddObserver(this);
+  src_root->AddObserver(this);
+  mirroring_ash_host->UpdateCursorConfig();
+}
+
+// Do nothing, since mirroring hosts had their cursor config updated when they
+// were registered.
+void AshWindowTreeHostUnified::UpdateCursorConfig() {}
+
+void AshWindowTreeHostUnified::ClearCursorConfig() {
+  for (auto* host : mirroring_hosts_)
+    host->ClearCursorConfig();
 }
 
 void AshWindowTreeHostUnified::SetCursorNative(gfx::NativeCursor cursor) {
diff --git a/ash/host/ash_window_tree_host_unified.h b/ash/host/ash_window_tree_host_unified.h
index 5e71c10..347a7b8ac 100644
--- a/ash/host/ash_window_tree_host_unified.h
+++ b/ash/host/ash_window_tree_host_unified.h
@@ -12,7 +12,7 @@
 
 namespace ash {
 
-class AshWindowTreeHostMirroringDelegate;
+class AshWindowTreeHostDelegate;
 
 // A WTH used for unified desktop mode. This creates an offscreen
 // compositor whose texture will be copied into each displays'
@@ -21,7 +21,7 @@
                                  public aura::WindowObserver {
  public:
   AshWindowTreeHostUnified(const gfx::Rect& initial_bounds,
-                           AshWindowTreeHostMirroringDelegate* delegate);
+                           AshWindowTreeHostDelegate* delegate);
 
   AshWindowTreeHostUnified(const AshWindowTreeHostUnified&) = delete;
   AshWindowTreeHostUnified& operator=(const AshWindowTreeHostUnified&) = delete;
@@ -32,6 +32,8 @@
   // AshWindowTreeHost:
   void PrepareForShutdown() override;
   void RegisterMirroringHost(AshWindowTreeHost* mirroring_ash_host) override;
+  void UpdateCursorConfig() override;
+  void ClearCursorConfig() override;
 
   // aura::WindowTreeHost:
   void SetCursorNative(gfx::NativeCursor cursor) override;
@@ -43,8 +45,6 @@
   // aura::WindowObserver:
   void OnWindowDestroying(aura::Window* window) override;
 
-  AshWindowTreeHostMirroringDelegate* delegate_;  // Not owned.
-
   std::vector<AshWindowTreeHost*> mirroring_hosts_;
 };
 
diff --git a/ash/host/ash_window_tree_host_unified_unittest.cc b/ash/host/ash_window_tree_host_unified_unittest.cc
new file mode 100644
index 0000000..0e57e64
--- /dev/null
+++ b/ash/host/ash_window_tree_host_unified_unittest.cc
@@ -0,0 +1,87 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/host/ash_window_tree_host_unified.h"
+
+#include "ash/display/mirror_window_test_api.h"
+#include "ash/test/ash_test_base.h"
+#include "ui/display/manager/display_manager.h"
+#include "ui/display/test/display_manager_test_api.h"
+#include "ui/events/ozone/chromeos/cursor_controller.h"
+
+namespace ash {
+
+namespace {
+
+class AshWindowTreeHostUnifiedTest : public AshTestBase {
+ public:
+  AshWindowTreeHostUnifiedTest() = default;
+  AshWindowTreeHostUnifiedTest(const AshWindowTreeHostUnifiedTest&) = delete;
+  AshWindowTreeHostUnifiedTest& operator=(const AshWindowTreeHostUnifiedTest&) =
+      delete;
+
+  void SetUp() override {
+    AshTestBase::SetUp();
+    display_manager()->SetUnifiedDesktopEnabled(true);
+  }
+};
+
+}  // namespace
+
+TEST_F(AshWindowTreeHostUnifiedTest, RotatedMouseMovement) {
+  display::test::DisplayManagerTestApi(display_manager())
+      .SetFirstDisplayAsInternalDisplay();
+  UpdateDisplay("1920x1080*2,800x600/r");
+
+  MirrorWindowTestApi test_api;
+  std::vector<aura::WindowTreeHost*> hosts = test_api.GetHosts();
+  // Have 2 WindowTreeHosts, one per display.
+  ASSERT_EQ(2u, hosts.size());
+
+  gfx::Vector2dF move;
+  ui::CursorController* controller = ui::CursorController::GetInstance();
+
+  gfx::AcceleratedWidget widget0 = hosts[0]->GetAcceleratedWidget();
+  move = {2, 0};
+  controller->ApplyCursorConfigForWindow(widget0, &move);
+  EXPECT_EQ(gfx::Vector2dF(2, 0), move);
+  move = {0, 2};
+  controller->ApplyCursorConfigForWindow(widget0, &move);
+  EXPECT_EQ(gfx::Vector2dF(0, 2), move);
+
+  gfx::AcceleratedWidget widget1 = hosts[1]->GetAcceleratedWidget();
+  move = {2, 0};
+  controller->ApplyCursorConfigForWindow(widget1, &move);
+  // There's a 1.2 scaling for external displays.
+  EXPECT_EQ(gfx::Vector2dF(0, 2.4), move);
+  move = {0, 2};
+  controller->ApplyCursorConfigForWindow(widget1, &move);
+  EXPECT_EQ(gfx::Vector2dF(-2.4, 0), move);
+
+  // Verify the old config is removed when display is updated.
+  gfx::AcceleratedWidget old_widget1 = widget1;
+
+  // TODO(crbug.com/1297396): Just updating the display rotation doesn't update
+  // the cursor config, so need to remove and re-add the display for it to
+  // re-register a new cursor config. After the bug is fixed, change to just
+  // update to the new rotation directly. In that case, the widget id may stay
+  // the same, so update the test accordingly.
+  UpdateDisplay("1920x1080*2");
+  UpdateDisplay("1920x1080*2,800x600/l");
+
+  hosts = test_api.GetHosts();
+  gfx::AcceleratedWidget new_widget1 = hosts[1]->GetAcceleratedWidget();
+  EXPECT_NE(new_widget1, old_widget1);
+
+  move = {2, 0};
+  controller->ApplyCursorConfigForWindow(new_widget1, &move);
+  EXPECT_EQ(gfx::Vector2dF(0, -2.4), move);
+
+  // Old widget should have no transformation.
+  move = {2, 0};
+  controller->ApplyCursorConfigForWindow(old_widget1, &move);
+  EXPECT_EQ(gfx::Vector2dF(2, 0), move);
+}
+
+}  // namespace ash
diff --git a/ash/projector/projector_annotation_tray.cc b/ash/projector/projector_annotation_tray.cc
index 4fcd653..15c8c69 100644
--- a/ash/projector/projector_annotation_tray.cc
+++ b/ash/projector/projector_annotation_tray.cc
@@ -5,7 +5,6 @@
 #include "ash/projector/projector_annotation_tray.h"
 
 #include "ash/constants/ash_features.h"
-#include "ash/fast_ink/laser/laser_pointer_controller.h"
 #include "ash/projector/projector_controller_impl.h"
 #include "ash/projector/ui/projector_color_button.h"
 #include "ash/public/cpp/shelf_config.h"
@@ -31,19 +30,16 @@
 namespace {
 
 // Margins between the title view and the edges around it (dp).
-constexpr int kPaddingBetweenTitleAndSeparator = 3;
 constexpr int kPaddingBetweenBottomAndLastTrayItem = 4;
 
 // Width of the bubble itself (dp).
 constexpr int kBubbleWidth = 372;
 
 // Insets for the views (dp).
-constexpr gfx::Insets kTitleViewPadding(4, 16, 0, 24);
 constexpr gfx::Insets kPenViewPadding(4, 0, 0, 24);
-constexpr gfx::Insets kLaserViewPadding(0, 0, 0, 24);
 
-// Spacing between buttons in the title view (dp).
-constexpr int kTitleViewChildSpacing = 20;
+// Spacing between buttons (dp).
+constexpr int kButtonsPadding = 20;
 
 // Size of menu rows.
 constexpr int kMenuRowHeight = 48;
@@ -57,7 +53,7 @@
 constexpr SkColor kYellowPenColor = SkColorSetRGB(0xFB, 0xBC, 0x04);
 
 // TODO(b/201664243): Use AnnotatorToolType.
-enum ProjectorTool { kToolNone, kToolLaser, kToolPen };
+enum ProjectorTool { kToolNone, kToolPen };
 
 ProjectorTool GetCurrentTool() {
   auto* controller = Shell::Get()->projector_controller();
@@ -68,15 +64,13 @@
 
   if (controller->IsAnnotatorEnabled())
     return kToolPen;
-  return controller->IsLaserPointerEnabled() ? kToolLaser : kToolNone;
+  return kToolNone;
 }
 
 const gfx::VectorIcon& GetIconForTool(ProjectorTool tool) {
   switch (tool) {
     case kToolNone:
       return kPaletteTrayIconProjectorIcon;
-    case kToolLaser:
-      return kPaletteModeLaserPointerIcon;
     case kToolPen:
       return kInkPenIcon;
   }
@@ -91,7 +85,6 @@
     : TrayBackgroundView(shelf),
       image_view_(
           tray_container()->AddChildView(std::make_unique<views::ImageView>())),
-      laser_view_(nullptr),
       pen_view_(nullptr) {
   image_view_->SetTooltipText(GetAccessibleNameForTray());
   image_view_->SetHorizontalAlignment(views::ImageView::Alignment::kCenter);
@@ -133,7 +126,6 @@
 }
 
 void ProjectorAnnotationTray::CloseBubble() {
-  laser_view_ = nullptr;
   pen_view_ = nullptr;
   bubble_.reset();
 
@@ -176,38 +168,6 @@
     view->layer()->SetFillsBoundsOpaquely(false);
   };
 
-  // Add title.
-  auto* title_view = bubble_view->AddChildView(std::make_unique<views::View>());
-
-  auto box_layout = std::make_unique<views::BoxLayout>(
-      views::BoxLayout::Orientation::kHorizontal, kTitleViewPadding);
-  box_layout->set_cross_axis_alignment(
-      views::BoxLayout::CrossAxisAlignment::kCenter);
-  box_layout->set_minimum_cross_axis_size(kMenuRowHeight);
-  views::BoxLayout* layout_ptr =
-      title_view->SetLayoutManager(std::move(box_layout));
-
-  auto* title_label = title_view->AddChildView(
-      std::make_unique<views::Label>(l10n_util::GetStringUTF16(
-          IDS_ASH_STATUS_AREA_PROJECTOR_ANNOTATION_TRAY_TITLE)));
-  title_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
-  title_label->SetEnabledColor(AshColorProvider::Get()->GetContentLayerColor(
-      AshColorProvider::ContentLayerType::kTextColorPrimary));
-  TrayPopupUtils::SetLabelFontList(title_label,
-                                   TrayPopupUtils::FontStyle::kPodMenuHeader);
-  layout_ptr->SetFlexForView(title_label, 1, true);
-
-  setup_layered_view(title_view);
-
-  // Add horizontal separator between the title and tools.
-  auto* separator =
-      bubble_view->AddChildView(std::make_unique<views::Separator>());
-  setup_layered_view(separator);
-  separator->SetColor(AshColorProvider::Get()->GetContentLayerColor(
-      AshColorProvider::ContentLayerType::kSeparatorColor));
-  separator->SetBorder(views::CreateEmptyBorder(gfx::Insets(
-      kPaddingBetweenTitleAndSeparator, 0, kMenuSeparatorVerticalPadding, 0)));
-
   // Add drawing tools
   {
     auto* marker_view_container =
@@ -215,7 +175,7 @@
 
     auto box_layout = std::make_unique<views::BoxLayout>(
         views::BoxLayout::Orientation::kHorizontal, kPenViewPadding,
-        kTitleViewChildSpacing);
+        kButtonsPadding);
     box_layout->set_cross_axis_alignment(
         views::BoxLayout::CrossAxisAlignment::kCenter);
     box_layout->set_minimum_cross_axis_size(kMenuRowHeight);
@@ -256,33 +216,6 @@
     setup_layered_view(marker_view_container);
   }
 
-  // Add Laser Pointer
-  {
-    auto* laser_view_container =
-        bubble_view->AddChildView(std::make_unique<views::View>());
-
-    auto box_layout = std::make_unique<views::BoxLayout>(
-        views::BoxLayout::Orientation::kHorizontal, kLaserViewPadding,
-        kTitleViewChildSpacing);
-    box_layout->set_cross_axis_alignment(
-        views::BoxLayout::CrossAxisAlignment::kCenter);
-    box_layout->set_minimum_cross_axis_size(kMenuRowHeight);
-    views::BoxLayout* layout_ptr =
-        laser_view_container->SetLayoutManager(std::move(box_layout));
-
-    SkColor icon_color = AshColorProvider::Get()->GetContentLayerColor(
-        AshColorProvider::ContentLayerType::kButtonIconColor);
-    gfx::ImageSkia icon = CreateVectorIcon(kPaletteModeLaserPointerIcon,
-                                           kMenuIconSize, icon_color);
-    laser_view_ = laser_view_container->AddChildView(
-        std::make_unique<HoverHighlightView>(this));
-    laser_view_->AddIconAndLabel(
-        icon, l10n_util::GetStringUTF16(IDS_LASER_POINTER_BUTTON));
-
-    layout_ptr->SetFlexForView(laser_view_, 1, true);
-    setup_layered_view(laser_view_container);
-  }
-
   // Show the bubble.
   bubble_ = std::make_unique<TrayBubbleWrapper>(this, bubble_view);
   SetIsActive(true);
@@ -307,8 +240,6 @@
 
   if (sender == pen_view_) {
     projector_controller->OnMarkerPressed();
-  } else if (sender == laser_view_) {
-    projector_controller->OnLaserPointerPressed();
   }
 
   CloseBubble();
diff --git a/ash/projector/projector_annotation_tray.h b/ash/projector/projector_annotation_tray.h
index 3583b2d..a0d3cd54 100644
--- a/ash/projector/projector_annotation_tray.h
+++ b/ash/projector/projector_annotation_tray.h
@@ -50,7 +50,6 @@
   // Image view of the tray icon.
   views::ImageView* const image_view_;
 
-  HoverHighlightView* laser_view_;
   HoverHighlightView* pen_view_;
 
   // The bubble that appears after clicking the annotation tools tray button.
diff --git a/ash/projector/projector_controller_impl.cc b/ash/projector/projector_controller_impl.cc
index 8bf987e..58bb94d 100644
--- a/ash/projector/projector_controller_impl.cc
+++ b/ash/projector/projector_controller_impl.cc
@@ -312,11 +312,6 @@
   RecordCreationFlowMetrics(ProjectorCreationFlow::kRecordingAborted);
 }
 
-void ProjectorControllerImpl::OnLaserPointerPressed() {
-  DCHECK(ui_controller_);
-  ui_controller_->OnLaserPointerPressed();
-}
-
 void ProjectorControllerImpl::OnMarkerPressed() {
   DCHECK(ui_controller_);
   ui_controller_->OnMarkerPressed();
@@ -327,10 +322,6 @@
     ui_controller_->ResetTools();
 }
 
-bool ProjectorControllerImpl::IsLaserPointerEnabled() {
-  return ui_controller_ && ui_controller_->IsLaserPointerEnabled();
-}
-
 bool ProjectorControllerImpl::IsAnnotatorEnabled() {
   return ui_controller_ && ui_controller_->is_annotator_enabled();
 }
diff --git a/ash/projector/projector_controller_impl.h b/ash/projector/projector_controller_impl.h
index bb1c7ab..bb90568 100644
--- a/ash/projector/projector_controller_impl.h
+++ b/ash/projector/projector_controller_impl.h
@@ -79,14 +79,10 @@
   // cancellation, an error, or a DLP/HDCP restriction.
   void OnRecordingStartAborted();
 
-  // Invoked when laser pointer button is pressed.
-  void OnLaserPointerPressed();
   // Invoked when marker button is pressed.
   void OnMarkerPressed();
-  // Reset and disable the laser pointer and the annotator tools.
+  // Reset and disable the the annotator tools.
   void ResetTools();
-  // Returns true if laser pointer is active.
-  bool IsLaserPointerEnabled();
   // Returns true if annotator is active.
   bool IsAnnotatorEnabled();
 
diff --git a/ash/projector/projector_controller_unittest.cc b/ash/projector/projector_controller_unittest.cc
index 18f3c8d..c2a26363 100644
--- a/ash/projector/projector_controller_unittest.cc
+++ b/ash/projector/projector_controller_unittest.cc
@@ -225,12 +225,6 @@
   EXPECT_FALSE(controller_->IsEligible());
 }
 
-TEST_F(ProjectorControllerTest, OnLaserPointerPressed) {
-  // Verify that |OnLaserPointerPressed| in |ProjectorUiController| is called.
-  EXPECT_CALL(*mock_ui_controller_, OnLaserPointerPressed());
-  controller_->OnLaserPointerPressed();
-}
-
 TEST_F(ProjectorControllerTest, OnMarkerPressed) {
   // Verify that |OnMarkerPressed| in |ProjectorUiController| is called.
   EXPECT_CALL(*mock_ui_controller_, OnMarkerPressed());
diff --git a/ash/projector/projector_ui_controller.cc b/ash/projector/projector_ui_controller.cc
index df3d93d..e0f26b51 100644
--- a/ash/projector/projector_ui_controller.cc
+++ b/ash/projector/projector_ui_controller.cc
@@ -49,12 +49,6 @@
 constexpr char kProjectorSaveErrorNotificationId[] =
     "projector_save_error_notification";
 
-void EnableLaserPointer(bool enabled) {
-  auto* laser_pointer_controller = Shell::Get()->laser_pointer_controller();
-  DCHECK(laser_pointer_controller);
-  Shell::Get()->laser_pointer_controller()->SetEnabled(enabled);
-}
-
 void ToggleAnnotator() {
   auto* capture_mode_controller = CaptureModeController::Get();
   // TODO(b/200292852): This check should not be necessary, but because
@@ -120,10 +114,6 @@
 
 ProjectorUiController::ProjectorUiController(
     ProjectorControllerImpl* projector_controller) {
-  auto* laser_pointer_controller = Shell::Get()->laser_pointer_controller();
-  DCHECK(laser_pointer_controller);
-  laser_pointer_controller_observation_.Observe(laser_pointer_controller);
-
   projector_session_observation_.Observe(
       projector_controller->projector_session());
 }
@@ -148,13 +138,7 @@
   projector_annotation_tray->SetVisiblePreferred(false);
 }
 
-void ProjectorUiController::OnLaserPointerPressed() {
-  EnableLaserPointer(!IsLaserPointerEnabled());
-  RecordToolbarMetrics(ProjectorToolbar::kLaserPointer);
-}
-
 void ProjectorUiController::OnMarkerPressed() {
-  EnableLaserPointer(false);
   ToggleAnnotator();
   annotator_enabled_ = !annotator_enabled_;
   RecordToolbarMetrics(ProjectorToolbar::kMarkerTool);
@@ -170,27 +154,10 @@
     ToggleAnnotator();
     annotator_enabled_ = false;
   }
-
-  if (IsLaserPointerEnabled())
-    EnableLaserPointer(false);
-}
-
-bool ProjectorUiController::IsLaserPointerEnabled() {
-  return Shell::Get()->laser_pointer_controller()->is_enabled();
 }
 
 void ProjectorUiController::OnProjectorSessionActiveStateChanged(bool active) {
   if (!active)
     ResetTools();
 }
-
-void ProjectorUiController::OnLaserPointerStateChanged(bool enabled) {
-  // If laser pointer is enabled, disable marker and magnifier.
-  if (!enabled || !annotator_enabled_)
-    return;
-
-  ToggleAnnotator();
-  annotator_enabled_ = false;
-}
-
 }  // namespace ash
diff --git a/ash/projector/projector_ui_controller.h b/ash/projector/projector_ui_controller.h
index 421c29c..ee908ea 100644
--- a/ash/projector/projector_ui_controller.h
+++ b/ash/projector/projector_ui_controller.h
@@ -6,7 +6,6 @@
 #define ASH_PROJECTOR_PROJECTOR_UI_CONTROLLER_H_
 
 #include "ash/ash_export.h"
-#include "ash/fast_ink/laser/laser_pointer_controller.h"
 #include "ash/public/cpp/projector/projector_session.h"
 #include "base/scoped_observation.h"
 #include "third_party/skia/include/core/SkColor.h"
@@ -17,8 +16,7 @@
 struct AnnotatorTool;
 
 // The controller in charge of UI.
-class ASH_EXPORT ProjectorUiController : public ProjectorSessionObserver,
-                                         public LaserPointerObserver {
+class ASH_EXPORT ProjectorUiController : public ProjectorSessionObserver {
  public:
   // Shows a notification informing the user that a Projector error has
   // occurred.
@@ -37,30 +35,21 @@
   virtual void ShowToolbar();
   // Close Projector toolbar. Virtual for testing.
   virtual void CloseToolbar();
-  // Invoked when laser pointer button is pressed. Virtual for testing.
-  virtual void OnLaserPointerPressed();
   // Invoked when marker button is pressed. Virtual for testing.
   virtual void OnMarkerPressed();
   // Sets the annotator tool.
   virtual void SetAnnotatorTool(const AnnotatorTool& tool);
-  // Reset and disable the laser pointer and the annotator tools.
+  // Reset and disable the annotator tools.
   void ResetTools();
 
-  bool IsLaserPointerEnabled();
   bool is_annotator_enabled() { return annotator_enabled_; }
 
  private:
   // ProjectorSessionObserver:
   void OnProjectorSessionActiveStateChanged(bool active) override;
 
-  // LaserPointerObserver:
-  void OnLaserPointerStateChanged(bool enabled) override;
-
   bool annotator_enabled_ = false;
 
-  base::ScopedObservation<LaserPointerController, LaserPointerObserver>
-      laser_pointer_controller_observation_{this};
-
   base::ScopedObservation<ProjectorSession, ProjectorSessionObserver>
       projector_session_observation_{this};
 };
diff --git a/ash/projector/projector_ui_controller_unittest.cc b/ash/projector/projector_ui_controller_unittest.cc
index 0f0cd19b..01b041a 100644
--- a/ash/projector/projector_ui_controller_unittest.cc
+++ b/ash/projector/projector_ui_controller_unittest.cc
@@ -8,7 +8,6 @@
 #include <string>
 
 #include "ash/constants/ash_features.h"
-#include "ash/fast_ink/laser/laser_pointer_controller.h"
 #include "ash/projector/projector_annotation_tray.h"
 #include "ash/projector/projector_controller_impl.h"
 #include "ash/projector/projector_metrics.h"
@@ -85,27 +84,12 @@
   EXPECT_FALSE(projector_annotation_tray->visible_preferred());
 }
 
-// Verifies that toggling on the laser pointer on Projector tools propagates to
-// the laser pointer controller.
-TEST_F(ProjectorUiControllerTest, EnablingDisablingLaserPointer) {
-  auto* laser_pointer_controller = Shell::Get()->laser_pointer_controller();
+// Verifies that toggling on the marker on Projector tools enables the
+// annotator.
+TEST_F(ProjectorUiControllerTest, EnablingDisablingMarker) {
   // Enable marker.
   controller_->OnMarkerPressed();
   EXPECT_TRUE(controller_->is_annotator_enabled());
-  EXPECT_FALSE(laser_pointer_controller->is_enabled());
-
-  controller_->OnLaserPointerPressed();
-  EXPECT_TRUE(laser_pointer_controller->is_enabled());
-  EXPECT_FALSE(controller_->is_annotator_enabled());
-
-  controller_->OnMarkerPressed();
-  EXPECT_TRUE(controller_->is_annotator_enabled());
-  EXPECT_FALSE(laser_pointer_controller->is_enabled());
-
-  auto* laser_pointer_controller_ = Shell::Get()->laser_pointer_controller();
-  laser_pointer_controller_->SetEnabled(true);
-  EXPECT_TRUE(laser_pointer_controller->is_enabled());
-  EXPECT_FALSE(controller_->is_annotator_enabled());
 }
 
 TEST_F(ProjectorUiControllerTest, SetAnnotatorTool) {
diff --git a/ash/projector/test/mock_projector_ui_controller.h b/ash/projector/test/mock_projector_ui_controller.h
index 9de93ab..c54a719d6 100644
--- a/ash/projector/test/mock_projector_ui_controller.h
+++ b/ash/projector/test/mock_projector_ui_controller.h
@@ -28,7 +28,6 @@
   // ProjectorUiController:
   MOCK_METHOD0(ShowToolbar, void());
   MOCK_METHOD0(CloseToolbar, void());
-  MOCK_METHOD0(OnLaserPointerPressed, void());
   MOCK_METHOD0(OnMarkerPressed, void());
 };
 
diff --git a/ash/public/cpp/wallpaper/wallpaper_controller.h b/ash/public/cpp/wallpaper/wallpaper_controller.h
index dd06788..38891b8d 100644
--- a/ash/public/cpp/wallpaper/wallpaper_controller.h
+++ b/ash/public/cpp/wallpaper/wallpaper_controller.h
@@ -32,6 +32,10 @@
 // Used by Chrome to set the wallpaper displayed by ash.
 class ASH_PUBLIC_EXPORT WallpaperController {
  public:
+  // A callback for confirming if Set*Wallpaper operations completed
+  // successfully.
+  using SetWallpaperCallback = base::OnceCallback<void(bool success)>;
+
   WallpaperController();
   virtual ~WallpaperController();
 
@@ -59,12 +63,11 @@
   //                 the user wallpaper info until |ConfirmPreviewWallpaper| is
   //                 called.
   // |callback|: called when the image is read from file and decoded.
-  using SetCustomWallpaperCallback = base::OnceCallback<void(bool success)>;
   virtual void SetCustomWallpaper(const AccountId& account_id,
                                   const base::FilePath& file_path,
                                   WallpaperLayout layout,
                                   bool preview_mode,
-                                  SetCustomWallpaperCallback callback) = 0;
+                                  SetWallpaperCallback callback) = 0;
 
   // Sets wallpaper from a local file and updates the saved wallpaper info for
   // the user.
@@ -90,16 +93,13 @@
   // preferences. Call |ConfirmPreviewMode| or |CancelPreviewMode| to finalize.
   // |callback| is required and will be called after the image is fetched (from
   // network or disk) and decoded.
-  using SetOnlineWallpaperCallback = base::OnceCallback<void(bool success)>;
   virtual void SetOnlineWallpaper(const OnlineWallpaperParams& params,
-                                  SetOnlineWallpaperCallback callback) = 0;
+                                  SetWallpaperCallback callback) = 0;
 
   // Sets the Google Photos photo with id |params.id| as the active wallpaper.
-  using SetGooglePhotosWallpaperCallback =
-      base::OnceCallback<void(bool success)>;
   virtual void SetGooglePhotosWallpaper(
       const GooglePhotosWallpaperParams& params,
-      SetGooglePhotosWallpaperCallback callback) = 0;
+      SetWallpaperCallback callback) = 0;
 
   // Deprecated. Use |SetOnlineWallpaper| instead because it will handle
   // downloading the image if it is not on disk yet.
@@ -109,9 +109,8 @@
   // returns true and sets wallpaper for the user, otherwise returns false.
   // |params|: The parameters of the online wallpaper.
   // Responds with true if the wallpaper file exists in local file system.
-  virtual void SetOnlineWallpaperIfExists(
-      const OnlineWallpaperParams& params,
-      SetOnlineWallpaperCallback callback) = 0;
+  virtual void SetOnlineWallpaperIfExists(const OnlineWallpaperParams& params,
+                                          SetWallpaperCallback callback) = 0;
 
   // Sets wallpaper from the Chrome OS wallpaper picker and saves the wallpaper
   // to local file system. After this, |SetOnlineWallpaperIfExists| will return
@@ -120,10 +119,9 @@
   // user. |params|: The parameters of the online wallpaper.
   // Responds with true if the wallpaper is set successfully (i.e. no decoding
   // error etc.).
-  virtual void SetOnlineWallpaperFromData(
-      const OnlineWallpaperParams& params,
-      const std::string& image_data,
-      SetOnlineWallpaperCallback callback) = 0;
+  virtual void SetOnlineWallpaperFromData(const OnlineWallpaperParams& params,
+                                          const std::string& image_data,
+                                          SetWallpaperCallback callback) = 0;
 
   // Sets the user's wallpaper to be the default wallpaper. Note: different user
   // types may have different default wallpapers.
diff --git a/ash/quick_pair/keyed_service/quick_pair_mediator.h b/ash/quick_pair/keyed_service/quick_pair_mediator.h
index 1ac111a..2d02897 100644
--- a/ash/quick_pair/keyed_service/quick_pair_mediator.h
+++ b/ash/quick_pair/keyed_service/quick_pair_mediator.h
@@ -73,7 +73,7 @@
   // FeatureStatusTracker::Observer
   void OnFastPairEnabledChanged(bool is_enabled) override;
 
-  // SannerBroker::Observer
+  // ScannerBroker::Observer
   void OnDeviceFound(scoped_refptr<Device> device) override;
   void OnDeviceLost(scoped_refptr<Device> device) override;
 
diff --git a/ash/quick_pair/pairing/fast_pair/fast_pair_pairer_impl.cc b/ash/quick_pair/pairing/fast_pair/fast_pair_pairer_impl.cc
index fbaa7f8..de5b8b9 100644
--- a/ash/quick_pair/pairing/fast_pair/fast_pair_pairer_impl.cc
+++ b/ash/quick_pair/pairing/fast_pair/fast_pair_pairer_impl.cc
@@ -267,6 +267,9 @@
   ask_confirm_passkey_initial_time_ = base::TimeTicks::Now();
   QP_LOG(VERBOSE) << "Connect device successful.";
   RecordConnectDeviceResult(/*success=*/true);
+  // The device ID can change between device discovery and connection, so
+  // ensure that device images are mapped to the current device ID.
+  FastPairRepository::Get()->FetchDeviceImages(device_);
 }
 
 void FastPairPairerImpl::OnConnectError() {
diff --git a/ash/quick_pair/repository/BUILD.gn b/ash/quick_pair/repository/BUILD.gn
index da885c4..063acb1 100644
--- a/ash/quick_pair/repository/BUILD.gn
+++ b/ash/quick_pair/repository/BUILD.gn
@@ -68,6 +68,8 @@
   testonly = true
 
   sources = [
+    "fake_device_metadata_http_fetcher.cc",
+    "fake_device_metadata_http_fetcher.h",
     "fake_fast_pair_repository.cc",
     "fake_fast_pair_repository.h",
     "fast_pair/fake_footprints_fetcher.cc",
@@ -88,7 +90,11 @@
     "//base/test:test_support",
     "//chromeos/services/bluetooth_config/public/cpp",
     "//device/bluetooth",
+    "//net",
     "//net/traffic_annotation:test_support",
+    "//services/network:test_support",
+    "//services/network/public/cpp",
+    "//services/network/public/mojom",
     "//testing/gtest",
     "//url",
   ]
@@ -103,6 +109,7 @@
     "fast_pair/device_metadata_fetcher_unittest.cc",
     "fast_pair/pending_write_store_unittest.cc",
     "fast_pair/saved_device_registry_unittest.cc",
+    "fast_pair_repository_impl_unittest.cc",
   ]
 
   deps = [
@@ -112,6 +119,7 @@
     "//ash/quick_pair/common",
     "//ash/quick_pair/common:test_support",
     "//ash/quick_pair/proto:fastpair_proto",
+    "//ash/services/quick_pair/public/cpp",
     "//base",
     "//base/test:test_support",
     "//chromeos/services/bluetooth_config/public/cpp",
diff --git a/ash/quick_pair/repository/fake_device_metadata_http_fetcher.cc b/ash/quick_pair/repository/fake_device_metadata_http_fetcher.cc
new file mode 100644
index 0000000..4a85e56
--- /dev/null
+++ b/ash/quick_pair/repository/fake_device_metadata_http_fetcher.cc
@@ -0,0 +1,99 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/quick_pair/repository/fake_device_metadata_http_fetcher.h"
+
+#include "ash/quick_pair/proto/fastpair.pb.h"
+#include "base/base64.h"
+#include "base/logging.h"
+#include "base/strings/string_util.h"
+#include "chromeos/services/bluetooth_config/public/cpp/device_image_info.h"
+#include "device/bluetooth/bluetooth_device.h"
+#include "net/base/net_errors.h"
+#include "net/http/http_status_code.h"
+#include "services/network/public/mojom/url_response_head.mojom.h"
+#include "services/network/test/test_utils.h"
+
+namespace ash {
+namespace quick_pair {
+
+namespace {
+
+constexpr char kValidResponseEncoded[] =
+    "Cr0HCKy4kAMYASJ4aHR0cHM6Ly9saDMuZ29vZ2xldXNlcmNvbnRlbnQuY29tL3dLM3YzcVI1d3"
+    "pHMk5JbXBuS1UyYlpfblFpdjh4elJoVDFadWRhT0NhSzlOVzRVS3lZNWtvYlNrSHlxeUJZTzVO"
+    "M1h3Um84XzRERkdGcHEtUjNWbW5nKgpQaXhlbCBCdWRzMrQCaW50ZW50OiNJbnRlbnQ7YWN0aW"
+    "9uPWNvbS5nb29nbGUuYW5kcm9pZC5nbXMubmVhcmJ5LmRpc2NvdmVyeSUzQUFDVElPTl9NQUdJ"
+    "Q19QQUlSO3BhY2thZ2U9Y29tLmdvb2dsZS5hbmRyb2lkLmdtcztjb21wb25lbnQ9Y29tLmdvb2"
+    "dsZS5hbmRyb2lkLmdtcy8ubmVhcmJ5LmRpc2NvdmVyeS5zZXJ2aWNlLkRpc2NvdmVyeVNlcnZp"
+    "Y2U7Uy5jb20uZ29vZ2xlLmFuZHJvaWQuZ21zLm5lYXJieS5kaXNjb3ZlcnklM0FFWFRSQV9DT0"
+    "1QQU5JT05fQVBQPWNvbS5nb29nbGUuYW5kcm9pZC5hcHBzLndlYXJhYmxlcy5tYWVzdHJvLmNv"
+    "bXBhbmlvbjtlbmRFmpkZP0pCEkAqaamccP9akybjAVzzvMeRhVAJjHOrKnyKet1/"
+    "L9H3JlQQdpFD2w1lclPx5B9I2+mjvU9IHsW2Xlsn6z0+HgdTUgIIA1oMCNye8e4FEIDe/"
+    "OIBaAd67QIKeGh0dHBzOi8vbGgzLmdvb2dsZXVzZXJjb250ZW50LmNvbS9POUo4V0dSQzg5Q08"
+    "1OEFTamFGbWxjbVM1NWYtMWJJZjJvUWFXVzFyS2REMDZtRlpTS1E3RWlNT3JKSWpjRUtRR0RTL"
+    "UlwUmN2TndhUGhvWXA1YVFuZxJ2aHR0cHM6Ly9saDMuZ29vZ2xldXNlcmNvbnRlbnQuY29tL1l"
+    "YeWk4Vy1VQkRKYklySU40cnhUQXljR2dSb2lXTWNuUFpsVnJRMU1MTHl1WjJEbVBIdGJzRDIzY"
+    "m1leTdJbXlHZWpSbWR2YWZIS0tzbXV6Zm5mVBp5aHR0cHM6Ly9saDMuZ29vZ2xldXNlcmNvbnR"
+    "lbnQuY29tL2hXeWdJM2liTjRIV0QtRmp3dlNuV1BIbkdzMUFNMTVLclh2VWx6Tmo2SlJDMmpNM"
+    "jZuTFZjY0tYNDFRZEtmOHE3aFN2eVJMYjZMcVJCRDJWVTlQWkNQY4ABAZoBBkdvb2dsZaoBF1B"
+    "yZXN0byBFVlQgQWxtb3N0IEJsYWNrsAECugEAGpEBiVBORw0KGgoAAAANSUhEUgAAAAQAAAAEC"
+    "AYAAACp8Z5+"
+    "AAAABHNCSVQICAgIfAhkiAAAAEhJREFUCJkFwTENgDAQQNGfYIAEXdjARTVUARI6UBMsnU9ADd"
+    "zM0Hze2wBKKfda65lzvqhna83eu+qOemWmESEA6jHG+NQK8AOtZCpIT/"
+    "9elAAAAABJRU5ErkJggiKjBRInVGFwIHRvIHBhaXIuIEVhcmJ1ZHMgd2lsbCBiZSB0aWVkIHRv"
+    "ICVzGhxUYXAgdG8gcGFpciB3aXRoIHRoaXMgZGV2aWNlIhNUYXAgdG8gZmluaXNoIHNldHVwKi"
+    "5UYXAgdG8gdXBkYXRlIGRldmljZSBzZXR0aW5ncyBhbmQgZmluaXNoIHNldHVwMj5UYXAgdG8g"
+    "ZG93bmxvYWQgZGV2aWNlIGFwcCBvbiBHb29nbGUgUGxheSBhbmQgc2VlIGFsbCBmZWF0dXJlcz"
+    "oRVW5hYmxlIHRvIGNvbm5lY3RCIlRyeSBtYW51YWxseSBwYWlyaW5nIHRvIHRoZSBkZXZpY2VK"
+    "KCVzIHdpbGwgYXBwZWFyIG9uIGRldmljZXMgbGlua2VkIHdpdGggJXNSIVlvdXIgZGV2aWNlIG"
+    "lzIHJlYWR5IHRvIGJlIHNldCB1cFpERG93bmxvYWQgdGhlIGRldmljZSBhcHAgb24gR29vZ2xl"
+    "IFBsYXkgdG8gc2VlIGFsbCBhdmFpbGFibGUgZmVhdHVyZXNiGENvbm5lY3QgJXMgdG8gdGhpcy"
+    "BwaG9uZWo6U2F2ZSBkZXZpY2UgdG8gJXMgZm9yIGZhc3RlciBwYWlyaW5nIHRvIHlvdXIgb3Ro"
+    "ZXIgZGV2aWNlc3IcVGhpcyB3aWxsIHRha2UgYSBmZXcgbW9tZW50c3o3VHJ5IG1hbnVhbGx5IH"
+    "BhaXJpbmcgdG8gdGhlIGRldmljZSBieSBnb2luZyB0byBTZXR0aW5nc7IBN0dldCB0aGUgaGFu"
+    "ZHMtZnJlZSBoZWxwIG9uIHRoZSBnbyBmcm9tIEdvb2dsZSBBc3Npc3RhbnS6ASNUYXAgdG8gc2"
+    "V0IHVwIHlvdXIgR29vZ2xlIEFzc2lzdGFudA==";
+constexpr char kInvalidResponse[] = "<html>404 error</html>";
+constexpr char kValidUrl[] =
+    "https://nearbydevices-pa.googleapis.com/v1/device/2748";
+
+}  // namespace
+
+FakeDeviceMetadataHttpFetcher::FakeDeviceMetadataHttpFetcher()
+    : HttpFetcher() {}
+
+FakeDeviceMetadataHttpFetcher::~FakeDeviceMetadataHttpFetcher() = default;
+
+void FakeDeviceMetadataHttpFetcher::ExecuteGetRequest(
+    const GURL& url,
+    FetchCompleteCallback callback) {
+  num_gets_++;
+
+  if (has_network_error_) {
+    std::move(callback).Run(std::make_unique<std::string>(kInvalidResponse),
+                            nullptr);
+    return;
+  }
+
+  if (base::StartsWith(url.spec(), kValidUrl)) {
+    LOG(ERROR) << "executing valid url cb";
+    std::string decoded;
+    base::Base64Decode(kValidResponseEncoded, &decoded);
+    std::move(callback).Run(
+        std::make_unique<std::string>(decoded),
+        std::make_unique<FastPairHttpResult>(net::Error::OK, nullptr));
+    return;
+  }
+  LOG(ERROR) << "executing invalid url cb " << url.spec()
+             << " != " << kValidUrl;
+  std::move(callback).Run(
+      nullptr,
+      std::make_unique<FastPairHttpResult>(
+          net::OK,
+          network::CreateURLResponseHead(net::HttpStatusCode::HTTP_OK).get()));
+}
+
+}  // namespace quick_pair
+}  // namespace ash
diff --git a/ash/quick_pair/repository/fake_device_metadata_http_fetcher.h b/ash/quick_pair/repository/fake_device_metadata_http_fetcher.h
new file mode 100644
index 0000000..0d44b89c
--- /dev/null
+++ b/ash/quick_pair/repository/fake_device_metadata_http_fetcher.h
@@ -0,0 +1,37 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_QUICK_PAIR_REPOSITORY_FAKE_DEVICE_METADATA_HTTP_FETCHER_H_
+#define ASH_QUICK_PAIR_REPOSITORY_FAKE_DEVICE_METADATA_HTTP_FETCHER_H_
+
+#include "ash/quick_pair/repository/http_fetcher.h"
+
+namespace ash {
+namespace quick_pair {
+
+class FakeDeviceMetadataHttpFetcher : public HttpFetcher {
+ public:
+  FakeDeviceMetadataHttpFetcher();
+  FakeDeviceMetadataHttpFetcher(const FakeDeviceMetadataHttpFetcher&) = delete;
+  FakeDeviceMetadataHttpFetcher& operator=(
+      const FakeDeviceMetadataHttpFetcher&) = delete;
+  ~FakeDeviceMetadataHttpFetcher() override;
+
+  // Performs a GET request to the desired URL and returns the response, if
+  // available, as a string to the provided |callback|.
+  void ExecuteGetRequest(const GURL& url,
+                         FetchCompleteCallback callback) override;
+
+  int num_gets() { return num_gets_; }
+  void set_network_error(bool error) { has_network_error_ = error; }
+
+ private:
+  int num_gets_ = 0;
+  bool has_network_error_ = false;
+};
+
+}  // namespace quick_pair
+}  // namespace ash
+
+#endif  // ASH_QUICK_PAIR_REPOSITORY_FAKE_DEVICE_METADATA_HTTP_FETCHER_H_
diff --git a/ash/quick_pair/repository/fake_fast_pair_repository.cc b/ash/quick_pair/repository/fake_fast_pair_repository.cc
index c2e8ac0..f77dc54 100644
--- a/ash/quick_pair/repository/fake_fast_pair_repository.cc
+++ b/ash/quick_pair/repository/fake_fast_pair_repository.cc
@@ -58,12 +58,6 @@
   std::move(callback).Run(nullptr, /*has_retryable_error=*/true);
 }
 
-void FakeFastPairRepository::IsValidModelId(
-    const std::string& hex_model_id,
-    base::OnceCallback<void(bool)> callback) {
-  std::move(callback).Run(data_.contains(base::ToUpperASCII(hex_model_id)));
-}
-
 void FakeFastPairRepository::CheckAccountKeys(
     const AccountKeyFilter& account_key_filter,
     CheckAccountKeysCallback callback) {
diff --git a/ash/quick_pair/repository/fake_fast_pair_repository.h b/ash/quick_pair/repository/fake_fast_pair_repository.h
index 9c1fc0ed..8036598 100644
--- a/ash/quick_pair/repository/fake_fast_pair_repository.h
+++ b/ash/quick_pair/repository/fake_fast_pair_repository.h
@@ -52,8 +52,6 @@
   // FastPairRepository::
   void GetDeviceMetadata(const std::string& hex_model_id,
                          DeviceMetadataCallback callback) override;
-  void IsValidModelId(const std::string& hex_model_id,
-                      base::OnceCallback<void(bool)> callback) override;
   void CheckAccountKeys(const AccountKeyFilter& account_key_filter,
                         CheckAccountKeysCallback callback) override;
   void AssociateAccountKey(scoped_refptr<Device> device,
diff --git a/ash/quick_pair/repository/fast_pair/device_id_map.cc b/ash/quick_pair/repository/fast_pair/device_id_map.cc
index 1ebdddd..7efde6a 100644
--- a/ash/quick_pair/repository/fast_pair/device_id_map.cc
+++ b/ash/quick_pair/repository/fast_pair/device_id_map.cc
@@ -31,26 +31,30 @@
 
 DeviceIdMap::~DeviceIdMap() = default;
 
-void DeviceIdMap::SaveModelIdForDevice(scoped_refptr<Device> device) {
+bool DeviceIdMap::SaveModelIdForDevice(scoped_refptr<Device> device) {
   // In some cases, BLE and classic address can map to different devices (with
   // the same model ID) so we want to capture both device ID -> model ID
   // records.
+  bool did_save = false;
   absl::optional<const std::string> ble_device_id =
       GetDeviceIdForAddress(device->ble_address);
   if (ble_device_id) {
+    did_save = true;
     device_id_to_model_id_[ble_device_id.value()] = device->metadata_id;
   }
 
   absl::optional<const std::string> classic_address = device->classic_address();
   if (!classic_address) {
-    return;
+    return did_save;
   }
 
   absl::optional<const std::string> classic_device_id =
       GetDeviceIdForAddress(classic_address.value());
   if (classic_device_id) {
+    did_save = true;
     device_id_to_model_id_[classic_device_id.value()] = device->metadata_id;
   }
+  return did_save;
 }
 
 bool DeviceIdMap::PersistRecordsForDevice(scoped_refptr<Device> device) {
@@ -156,6 +160,12 @@
   return false;
 }
 
+void DeviceIdMap::RefreshCacheForTest() {
+  QP_LOG(INFO) << __func__;
+  device_id_to_model_id_.clear();
+  LoadPersistedRecordsFromPrefs();
+}
+
 void DeviceIdMap::LoadPersistedRecordsFromPrefs() {
   QP_LOG(INFO) << __func__;
   PrefService* local_state = Shell::Get()->local_state();
diff --git a/ash/quick_pair/repository/fast_pair/device_id_map.h b/ash/quick_pair/repository/fast_pair/device_id_map.h
index 49386bd..8a53ff9 100644
--- a/ash/quick_pair/repository/fast_pair/device_id_map.h
+++ b/ash/quick_pair/repository/fast_pair/device_id_map.h
@@ -39,7 +39,7 @@
 
   // Saves device ID -> model ID records for the devices matching both
   // the BLE and Classic address in memory, stored in device_id_to_model_id.
-  void SaveModelIdForDevice(scoped_refptr<Device> device);
+  bool SaveModelIdForDevice(scoped_refptr<Device> device);
 
   // Persists the device ID -> model ID records for |device|
   // to local state prefs. Returns true if a record was persisted, false
@@ -60,6 +60,9 @@
   // local state prefs, false otherwise.
   bool HasPersistedRecordsForModelId(const std::string& model_id);
 
+  // Clears the in-memory map and reloads from prefs.
+  void RefreshCacheForTest();
+
  private:
   // Returns the device ID that owns |address|, if found.
   absl::optional<const std::string> GetDeviceIdForAddress(
diff --git a/ash/quick_pair/repository/fast_pair/device_id_map_unittest.cc b/ash/quick_pair/repository/fast_pair/device_id_map_unittest.cc
index fd6809f..866631ff 100644
--- a/ash/quick_pair/repository/fast_pair/device_id_map_unittest.cc
+++ b/ash/quick_pair/repository/fast_pair/device_id_map_unittest.cc
@@ -76,7 +76,7 @@
 };
 
 TEST_F(DeviceIdMapTest, SaveModelIdForDeviceValid) {
-  device_id_map_->SaveModelIdForDevice(device_);
+  EXPECT_TRUE(device_id_map_->SaveModelIdForDevice(device_));
   absl::optional<const std::string> ble_model_id =
       device_id_map_->GetModelIdForDeviceId(kTestBLEDeviceId);
   EXPECT_TRUE(ble_model_id);
@@ -93,7 +93,7 @@
   EXPECT_CALL(*adapter_, GetDevice(kTestBLEAddress)).WillOnce(Return(nullptr));
   EXPECT_CALL(*adapter_, GetDevice(kTestClassicAddress))
       .WillOnce(Return(&classic_bluetooth_device_));
-  device_id_map_->SaveModelIdForDevice(device_);
+  EXPECT_TRUE(device_id_map_->SaveModelIdForDevice(device_));
   absl::optional<const std::string> ble_model_id =
       device_id_map_->GetModelIdForDeviceId(kTestBLEDeviceId);
   EXPECT_FALSE(ble_model_id);
@@ -110,7 +110,7 @@
       .WillOnce(Return(&ble_bluetooth_device_));
   EXPECT_CALL(*adapter_, GetDevice(kTestClassicAddress))
       .WillOnce(Return(nullptr));
-  device_id_map_->SaveModelIdForDevice(device_);
+  EXPECT_TRUE(device_id_map_->SaveModelIdForDevice(device_));
   absl::optional<const std::string> ble_model_id =
       device_id_map_->GetModelIdForDeviceId(kTestBLEDeviceId);
   EXPECT_TRUE(ble_model_id);
@@ -125,7 +125,7 @@
   EXPECT_CALL(*adapter_, GetDevice(kTestBLEAddress)).WillOnce(Return(nullptr));
   EXPECT_CALL(*adapter_, GetDevice(kTestClassicAddress))
       .WillOnce(Return(nullptr));
-  device_id_map_->SaveModelIdForDevice(device_);
+  EXPECT_FALSE(device_id_map_->SaveModelIdForDevice(device_));
   absl::optional<const std::string> ble_model_id =
       device_id_map_->GetModelIdForDeviceId(kTestBLEDeviceId);
   EXPECT_FALSE(ble_model_id);
@@ -136,7 +136,7 @@
 
 TEST_F(DeviceIdMapTest, PersistRecordsForDeviceValid) {
   // First, save the device ID records to memory.
-  device_id_map_->SaveModelIdForDevice(device_);
+  EXPECT_TRUE(device_id_map_->SaveModelIdForDevice(device_));
   EXPECT_TRUE(device_id_map_->PersistRecordsForDevice(device_));
 
   // Validate that the ID records are persisted to prefs.
@@ -163,7 +163,7 @@
   EXPECT_CALL(*adapter_, GetDevice(kTestClassicAddress))
       .Times(2)
       .WillRepeatedly(Return(&classic_bluetooth_device_));
-  device_id_map_->SaveModelIdForDevice(device_);
+  EXPECT_TRUE(device_id_map_->SaveModelIdForDevice(device_));
   EXPECT_TRUE(device_id_map_->PersistRecordsForDevice(device_));
 }
 
@@ -176,13 +176,13 @@
   EXPECT_CALL(*adapter_, GetDevice(kTestClassicAddress))
       .Times(2)
       .WillRepeatedly(Return(nullptr));
-  device_id_map_->SaveModelIdForDevice(device_);
+  EXPECT_TRUE(device_id_map_->SaveModelIdForDevice(device_));
   EXPECT_TRUE(device_id_map_->PersistRecordsForDevice(device_));
 }
 
 TEST_F(DeviceIdMapTest, PersistRecordsForDeviceValidDoublePersist) {
   // First, save the device ID records to memory.
-  device_id_map_->SaveModelIdForDevice(device_);
+  EXPECT_TRUE(device_id_map_->SaveModelIdForDevice(device_));
   EXPECT_TRUE(device_id_map_->PersistRecordsForDevice(device_));
 
   // When persisting a second time, should overwrite record and
@@ -197,7 +197,7 @@
 
 TEST_F(DeviceIdMapTest, EvictDeviceIdRecordValid) {
   // First, persist the device ID record to disk.
-  device_id_map_->SaveModelIdForDevice(device_);
+  EXPECT_TRUE(device_id_map_->SaveModelIdForDevice(device_));
   EXPECT_TRUE(device_id_map_->PersistRecordsForDevice(device_));
   EXPECT_TRUE(device_id_map_->EvictDeviceIdRecord(kTestBLEDeviceId));
 
@@ -218,7 +218,7 @@
 
 TEST_F(DeviceIdMapTest, EvictDeviceIdRecordInvalidDoubleFree) {
   // First, persist the device ID records to disk.
-  device_id_map_->SaveModelIdForDevice(device_);
+  EXPECT_TRUE(device_id_map_->SaveModelIdForDevice(device_));
   EXPECT_TRUE(device_id_map_->PersistRecordsForDevice(device_));
   EXPECT_TRUE(device_id_map_->EvictDeviceIdRecord(kTestBLEDeviceId));
 
@@ -227,7 +227,7 @@
 }
 
 TEST_F(DeviceIdMapTest, GetModelIdForDeviceIdValid) {
-  device_id_map_->SaveModelIdForDevice(device_);
+  EXPECT_TRUE(device_id_map_->SaveModelIdForDevice(device_));
 
   absl::optional<const std::string> model_id =
       device_id_map_->GetModelIdForDeviceId(kTestBLEDeviceId);
@@ -243,7 +243,7 @@
 }
 
 TEST_F(DeviceIdMapTest, GetModelIdForDeviceIdInvalidNotAdded) {
-  device_id_map_->SaveModelIdForDevice(device_);
+  EXPECT_TRUE(device_id_map_->SaveModelIdForDevice(device_));
 
   absl::optional<const std::string> model_id =
       device_id_map_->GetModelIdForDeviceId("not found id");
@@ -252,14 +252,14 @@
 
 TEST_F(DeviceIdMapTest, HasPersistedRecordsForModelIdTrueAfterPersist) {
   // First, persist the device ID records to disk.
-  device_id_map_->SaveModelIdForDevice(device_);
+  EXPECT_TRUE(device_id_map_->SaveModelIdForDevice(device_));
   EXPECT_TRUE(device_id_map_->PersistRecordsForDevice(device_));
   EXPECT_TRUE(device_id_map_->HasPersistedRecordsForModelId(kTestModelId));
 }
 
 TEST_F(DeviceIdMapTest, HasPersistedRecordsForModelIdTrueAfterOneEviction) {
   // First, persist the device ID records to disk.
-  device_id_map_->SaveModelIdForDevice(device_);
+  EXPECT_TRUE(device_id_map_->SaveModelIdForDevice(device_));
   EXPECT_TRUE(device_id_map_->PersistRecordsForDevice(device_));
   // Evict one of the records that points to this model ID.
   EXPECT_TRUE(device_id_map_->EvictDeviceIdRecord(kTestClassicDeviceId));
@@ -268,7 +268,7 @@
 
 TEST_F(DeviceIdMapTest, HasPersistedRecordsForModelIdFalseAfterAllEvictions) {
   // First, persist the device ID records to disk.
-  device_id_map_->SaveModelIdForDevice(device_);
+  EXPECT_TRUE(device_id_map_->SaveModelIdForDevice(device_));
   EXPECT_TRUE(device_id_map_->PersistRecordsForDevice(device_));
   // Evict all of the records that points to this model ID.
   EXPECT_TRUE(device_id_map_->EvictDeviceIdRecord(kTestClassicDeviceId));
@@ -278,13 +278,13 @@
 
 TEST_F(DeviceIdMapTest, HasPersistedRecordsForModelIdFalseNoPersist) {
   // Don't persist the device ID records to disk.
-  device_id_map_->SaveModelIdForDevice(device_);
+  EXPECT_TRUE(device_id_map_->SaveModelIdForDevice(device_));
   EXPECT_FALSE(device_id_map_->HasPersistedRecordsForModelId(kTestModelId));
 }
 
 TEST_F(DeviceIdMapTest, LoadPersistedIdRecordFromPrefs) {
   // First, persist the device ID records to disk.
-  device_id_map_->SaveModelIdForDevice(device_);
+  EXPECT_TRUE(device_id_map_->SaveModelIdForDevice(device_));
   EXPECT_TRUE(device_id_map_->PersistRecordsForDevice(device_));
 
   // A new/restarted DeviceIdMap instance should load persisted ID records
diff --git a/ash/quick_pair/repository/fast_pair_repository.h b/ash/quick_pair/repository/fast_pair_repository.h
index e10b17f..0d3f9ac 100644
--- a/ash/quick_pair/repository/fast_pair_repository.h
+++ b/ash/quick_pair/repository/fast_pair_repository.h
@@ -48,11 +48,6 @@
   virtual void GetDeviceMetadata(const std::string& hex_model_id,
                                  DeviceMetadataCallback callback) = 0;
 
-  // Checks if the input |hex_model_id| is valid and notifies the requester
-  // through the provided |callback|.
-  virtual void IsValidModelId(const std::string& hex_model_id,
-                              ValidModelIdCallback callback) = 0;
-
   // Checks all account keys associated with the user's account against the
   // given filter.  If a match is found, metadata for the associated device will
   // be returned through the callback.
diff --git a/ash/quick_pair/repository/fast_pair_repository_impl.cc b/ash/quick_pair/repository/fast_pair_repository_impl.cc
index 05f454cf..45d8ee1 100644
--- a/ash/quick_pair/repository/fast_pair_repository_impl.cc
+++ b/ash/quick_pair/repository/fast_pair_repository_impl.cc
@@ -38,6 +38,22 @@
       saved_device_registry_(std::make_unique<SavedDeviceRegistry>()),
       footprints_last_updated_(base::Time::UnixEpoch()) {}
 
+FastPairRepositoryImpl::FastPairRepositoryImpl(
+    std::unique_ptr<DeviceMetadataFetcher> device_metadata_fetcher,
+    std::unique_ptr<FootprintsFetcher> footprints_fetcher,
+    std::unique_ptr<FastPairImageDecoder> image_decoder,
+    std::unique_ptr<DeviceIdMap> device_id_map,
+    std::unique_ptr<DeviceImageStore> device_image_store,
+    std::unique_ptr<SavedDeviceRegistry> saved_device_registry)
+    : FastPairRepository(),
+      device_metadata_fetcher_(std::move(device_metadata_fetcher)),
+      footprints_fetcher_(std::move(footprints_fetcher)),
+      image_decoder_(std::move(image_decoder)),
+      device_id_map_(std::move(device_id_map)),
+      device_image_store_(std::move(device_image_store)),
+      saved_device_registry_(std::move(saved_device_registry)),
+      footprints_last_updated_(base::Time::UnixEpoch()) {}
+
 FastPairRepositoryImpl::~FastPairRepositoryImpl() = default;
 
 void FastPairRepositoryImpl::GetDeviceMetadata(
@@ -98,13 +114,6 @@
                           /*has_retryable_error=*/false);
 }
 
-void FastPairRepositoryImpl::IsValidModelId(
-    const std::string& hex_model_id,
-    base::OnceCallback<void(bool)> callback) {
-  QP_LOG(INFO) << __func__;
-  std::move(callback).Run(false);
-}
-
 void FastPairRepositoryImpl::CheckAccountKeys(
     const AccountKeyFilter& account_key_filter,
     CheckAccountKeysCallback callback) {
@@ -221,7 +230,7 @@
     const std::vector<uint8_t>& account_key,
     bool success) {
   if (!success) {
-    // TODO(jonmann): Handle caching to disk + retries.
+    // TODO(b/221126805): Handle caching to disk + retries.
     return;
   }
 
@@ -239,7 +248,7 @@
   QP_LOG(INFO) << __func__ << ": Removing device from Footprints.";
   footprints_fetcher_->DeleteUserDevice(base::HexEncode(*account_key),
                                         base::DoNothing());
-  // TODO(jonmann): Handle saving pending update to disk + retries.
+  // TODO(b/221126805): Handle saving pending update to disk + retries.
   return true;
 }
 
@@ -249,7 +258,12 @@
   // Save a record of the device ID -> model ID for this device so that we can
   // display images for device objects that lack a model ID, such as
   // device::BluetoothDevice.
-  device_id_map_->SaveModelIdForDevice(device);
+  if (!device_id_map_->SaveModelIdForDevice(device)) {
+    QP_LOG(WARNING) << __func__
+                    << ": Unable to save address -> model ID"
+                       " mapping for model ID "
+                    << device->metadata_id;
+  }
 
   GetDeviceMetadata(
       device->metadata_id,
@@ -277,7 +291,12 @@
 bool FastPairRepositoryImpl::PersistDeviceImages(scoped_refptr<Device> device) {
   QP_LOG(INFO) << __func__ << ": Persisting device images for model ID "
                << device->metadata_id;
-  device_id_map_->PersistRecordsForDevice(device);
+  if (!device_id_map_->PersistRecordsForDevice(device)) {
+    QP_LOG(WARNING) << __func__
+                    << ": Unable to persist address -> model ID"
+                       " mapping for model ID "
+                    << device->metadata_id;
+  }
   return device_image_store_->PersistDeviceImages(device->metadata_id);
 }
 
@@ -301,8 +320,13 @@
 FastPairRepositoryImpl::GetImagesForDevice(const std::string& device_id) {
   absl::optional<const std::string> hex_model_id =
       device_id_map_->GetModelIdForDeviceId(device_id);
-  if (!hex_model_id)
+  if (!hex_model_id) {
+    QP_LOG(WARNING) << __func__
+                    << ": Could not find a matching model ID for "
+                       "device ID: "
+                    << device_id;
     return absl::nullopt;
+  }
 
   return device_image_store_->GetImagesForDeviceModel(hex_model_id.value());
 }
diff --git a/ash/quick_pair/repository/fast_pair_repository_impl.h b/ash/quick_pair/repository/fast_pair_repository_impl.h
index 41c128e..40f48da 100644
--- a/ash/quick_pair/repository/fast_pair_repository_impl.h
+++ b/ash/quick_pair/repository/fast_pair_repository_impl.h
@@ -44,6 +44,13 @@
 class FastPairRepositoryImpl : public FastPairRepository {
  public:
   FastPairRepositoryImpl();
+  FastPairRepositoryImpl(
+      std::unique_ptr<DeviceMetadataFetcher> device_metadata_fetcher,
+      std::unique_ptr<FootprintsFetcher> footprints_fetcher,
+      std::unique_ptr<FastPairImageDecoder> image_decoder,
+      std::unique_ptr<DeviceIdMap> device_id_map,
+      std::unique_ptr<DeviceImageStore> device_image_store,
+      std::unique_ptr<SavedDeviceRegistry> saved_device_registry);
   FastPairRepositoryImpl(const FastPairRepositoryImpl&) = delete;
   FastPairRepositoryImpl& operator=(const FastPairRepositoryImpl&) = delete;
   ~FastPairRepositoryImpl() override;
@@ -51,8 +58,6 @@
   // FastPairRepository::
   void GetDeviceMetadata(const std::string& hex_model_id,
                          DeviceMetadataCallback callback) override;
-  void IsValidModelId(const std::string& hex_model_id,
-                      base::OnceCallback<void(bool)> callback) override;
   void CheckAccountKeys(const AccountKeyFilter& account_key_filter,
                         CheckAccountKeysCallback callback) override;
   void AssociateAccountKey(scoped_refptr<Device> device,
diff --git a/ash/quick_pair/repository/fast_pair_repository_impl_unittest.cc b/ash/quick_pair/repository/fast_pair_repository_impl_unittest.cc
new file mode 100644
index 0000000..bbc8ab5
--- /dev/null
+++ b/ash/quick_pair/repository/fast_pair_repository_impl_unittest.cc
@@ -0,0 +1,350 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/quick_pair/repository/fast_pair_repository_impl.h"
+
+#include "ash/quick_pair/common/device.h"
+#include "ash/quick_pair/common/mock_quick_pair_browser_delegate.h"
+#include "ash/quick_pair/common/protocol.h"
+#include "ash/quick_pair/repository/fake_device_metadata_http_fetcher.h"
+#include "ash/quick_pair/repository/fast_pair/device_id_map.h"
+#include "ash/quick_pair/repository/fast_pair/device_image_store.h"
+#include "ash/quick_pair/repository/fast_pair/device_metadata.h"
+#include "ash/quick_pair/repository/fast_pair/device_metadata_fetcher.h"
+#include "ash/quick_pair/repository/fast_pair/fake_footprints_fetcher.h"
+#include "ash/quick_pair/repository/fast_pair/mock_fast_pair_image_decoder.h"
+#include "ash/quick_pair/repository/fast_pair/proto_conversions.h"
+#include "ash/quick_pair/repository/fast_pair/saved_device_registry.h"
+#include "ash/services/quick_pair/public/cpp/account_key_filter.h"
+#include "ash/shell.h"
+#include "ash/test/ash_test_base.h"
+#include "base/callback_helpers.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/test/gmock_callback_support.h"
+#include "components/prefs/testing_pref_service.h"
+#include "device/bluetooth/bluetooth_adapter_factory.h"
+#include "device/bluetooth/test/mock_bluetooth_adapter.h"
+#include "device/bluetooth/test/mock_bluetooth_device.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "ui/gfx/image/image_unittest_util.h"
+
+namespace {
+
+constexpr char kValidModelId[] = "abc";
+constexpr char kInvalidModelId[] = "666";
+constexpr char kTestModelId[] = "test_model_id";
+constexpr char kTestDeviceId[] = "test_ble_device_id";
+constexpr char kTestBLEAddress[] = "test_ble_address";
+constexpr char kTestClassicAddress[] = "test_classic_address";
+const std::vector<uint8_t> kAccountKey1{0x11, 0x22, 0x33, 0x44, 0x55, 0x66,
+                                        0x77, 0x88, 0x99, 0x00, 0xAA, 0xBB,
+                                        0xCC, 0xDD, 0xEE, 0xFF};
+const std::vector<uint8_t> kFilterBytes1{0x0A, 0x42, 0x88, 0x10};
+const uint8_t salt = 0xC7;
+
+}  // namespace
+
+namespace ash {
+namespace quick_pair {
+
+using ::base::test::RunOnceCallback;
+using ::testing::_;
+using ::testing::Return;
+
+class FastPairRepositoryImplTest : public AshTestBase {
+ public:
+  FastPairRepositoryImplTest()
+      : adapter_(new testing::NiceMock<device::MockBluetoothAdapter>),
+        ble_bluetooth_device_(adapter_.get(),
+                              0,
+                              "Test ble name",
+                              kTestBLEAddress,
+                              false,
+                              true),
+        classic_bluetooth_device_(adapter_.get(),
+                                  0,
+                                  "Test classic name",
+                                  kTestClassicAddress,
+                                  false,
+                                  true) {
+    ON_CALL(ble_bluetooth_device_, GetIdentifier)
+        .WillByDefault(Return(kTestDeviceId));
+    ON_CALL(classic_bluetooth_device_, GetIdentifier)
+        .WillByDefault(Return(kTestDeviceId));
+    ON_CALL(*adapter_, GetDevice(kTestBLEAddress))
+        .WillByDefault(Return(&ble_bluetooth_device_));
+    ON_CALL(*adapter_, GetDevice(kTestClassicAddress))
+        .WillByDefault(Return(&classic_bluetooth_device_));
+  }
+
+  void SetUp() override {
+    AshTestBase::SetUp();
+    device::BluetoothAdapterFactory::SetAdapterForTesting(adapter_);
+    device_ = base::MakeRefCounted<Device>(kTestModelId, kTestBLEAddress,
+                                           Protocol::kFastPairInitial);
+    device_->set_classic_address(kTestClassicAddress);
+
+    auto http_fetcher = std::make_unique<FakeDeviceMetadataHttpFetcher>();
+    metadata_http_fetcher_ = http_fetcher.get();
+
+    auto device_metadata_fetcher =
+        std::make_unique<DeviceMetadataFetcher>(std::move(http_fetcher));
+    device_metadata_fetcher_ = device_metadata_fetcher.get();
+
+    auto footprints_fetcher = std::make_unique<FakeFootprintsFetcher>();
+    footprints_fetcher_ = footprints_fetcher.get();
+
+    auto image_decoder = std::make_unique<MockFastPairImageDecoder>();
+    image_decoder_ = image_decoder.get();
+    test_image_ = gfx::test::CreateImage(100, 100);
+    ON_CALL(*image_decoder_, DecodeImage(_, _, _))
+        .WillByDefault(RunOnceCallback<2>(test_image_));
+
+    auto device_id_map = std::make_unique<DeviceIdMap>();
+    device_id_map_ = device_id_map.get();
+
+    auto device_image_store =
+        std::make_unique<DeviceImageStore>(image_decoder_);
+    device_image_store_ = device_image_store.get();
+
+    auto saved_device_registry = std::make_unique<SavedDeviceRegistry>();
+    saved_device_registry_ = saved_device_registry.get();
+
+    fast_pair_repository_ = std::make_unique<FastPairRepositoryImpl>(
+        std::move(device_metadata_fetcher), std::move(footprints_fetcher),
+        std::move(image_decoder), std::move(device_id_map),
+        std::move(device_image_store), std::move(saved_device_registry));
+
+    pref_service_ = std::make_unique<TestingPrefServiceSimple>();
+    ON_CALL(browser_delegate_, GetActivePrefService())
+        .WillByDefault(testing::Return(pref_service_.get()));
+    SavedDeviceRegistry::RegisterProfilePrefs(pref_service_->registry());
+    DeviceIdMap::RegisterLocalStatePrefs(pref_service_->registry());
+  }
+
+  void VerifyMetadata(base::OnceClosure on_complete,
+                      DeviceMetadata* device_metadata,
+                      bool should_retry) {
+    EXPECT_NE(nullptr, device_metadata);
+    std::move(on_complete).Run();
+  }
+
+  void VerifyMetadataFailure(base::OnceClosure on_complete,
+                             bool expected_retry,
+                             DeviceMetadata* device_metadata,
+                             bool should_retry) {
+    EXPECT_EQ(nullptr, device_metadata);
+    EXPECT_EQ(expected_retry, should_retry);
+    std::move(on_complete).Run();
+  }
+
+  void VerifyAccountKeyCheck(base::OnceClosure on_complete,
+                             bool expected_result,
+                             absl::optional<PairingMetadata> pairing_metadata) {
+    if (expected_result) {
+      EXPECT_NE(absl::nullopt, pairing_metadata);
+    } else {
+      EXPECT_EQ(absl::nullopt, pairing_metadata);
+    }
+    std::move(on_complete).Run();
+  }
+
+ protected:
+  std::unique_ptr<FastPairRepositoryImpl> fast_pair_repository_;
+
+  scoped_refptr<testing::NiceMock<device::MockBluetoothAdapter>> adapter_;
+  testing::NiceMock<device::MockBluetoothDevice> ble_bluetooth_device_;
+  testing::NiceMock<device::MockBluetoothDevice> classic_bluetooth_device_;
+  scoped_refptr<Device> device_;
+  gfx::Image test_image_;
+  std::unique_ptr<TestingPrefServiceSimple> pref_service_;
+  MockQuickPairBrowserDelegate browser_delegate_;
+
+  DeviceMetadataFetcher* device_metadata_fetcher_;
+  FakeDeviceMetadataHttpFetcher* metadata_http_fetcher_;
+  FakeFootprintsFetcher* footprints_fetcher_;
+  MockFastPairImageDecoder* image_decoder_;
+  DeviceIdMap* device_id_map_;
+  DeviceImageStore* device_image_store_;
+  SavedDeviceRegistry* saved_device_registry_;
+};
+
+TEST_F(FastPairRepositoryImplTest, GetDeviceMetadata) {
+  auto run_loop = std::make_unique<base::RunLoop>();
+  fast_pair_repository_->GetDeviceMetadata(
+      kValidModelId,
+      base::BindOnce(&FastPairRepositoryImplTest::VerifyMetadata,
+                     base::Unretained(this), run_loop->QuitClosure()));
+  run_loop->Run();
+  EXPECT_EQ(1, metadata_http_fetcher_->num_gets());
+
+  run_loop = std::make_unique<base::RunLoop>();
+  fast_pair_repository_->GetDeviceMetadata(
+      kValidModelId,
+      base::BindOnce(&FastPairRepositoryImplTest::VerifyMetadata,
+                     base::Unretained(this), run_loop->QuitClosure()));
+  run_loop->Run();
+  // Indicates that the cache was used instead of a second GET.
+  EXPECT_EQ(1, metadata_http_fetcher_->num_gets());
+}
+
+TEST_F(FastPairRepositoryImplTest, GetDeviceMetadata_Failed_Retryable) {
+  base::RunLoop run_loop;
+  metadata_http_fetcher_->set_network_error(true);
+  fast_pair_repository_->GetDeviceMetadata(
+      kInvalidModelId,
+      base::BindOnce(&FastPairRepositoryImplTest::VerifyMetadataFailure,
+                     base::Unretained(this), run_loop.QuitClosure(),
+                     /*expected_retry=*/true));
+  run_loop.Run();
+}
+
+TEST_F(FastPairRepositoryImplTest, GetDeviceMetadata_Failed_NotRetryable) {
+  base::RunLoop run_loop;
+  fast_pair_repository_->GetDeviceMetadata(
+      kInvalidModelId,
+      base::BindOnce(&FastPairRepositoryImplTest::VerifyMetadataFailure,
+                     base::Unretained(this), run_loop.QuitClosure(),
+                     /*expected_retry=*/false));
+  run_loop.Run();
+  EXPECT_EQ(1, metadata_http_fetcher_->num_gets());
+}
+
+TEST_F(FastPairRepositoryImplTest, CheckAccountKeys_NoMatch) {
+  AccountKeyFilter filter(kFilterBytes1, {salt});
+
+  auto run_loop = std::make_unique<base::RunLoop>();
+  fast_pair_repository_->CheckAccountKeys(
+      filter, base::BindOnce(&FastPairRepositoryImplTest::VerifyAccountKeyCheck,
+                             base::Unretained(this), run_loop->QuitClosure(),
+                             /*expected_result=*/false));
+  run_loop->Run();
+}
+
+TEST_F(FastPairRepositoryImplTest, CheckAccountKeys_Match) {
+  AccountKeyFilter filter(kFilterBytes1, {salt});
+  nearby::fastpair::GetObservedDeviceResponse device;
+  DeviceMetadata metadata(device, gfx::Image());
+
+  // FakeFootprintsFetcher APIs are actually synchronous.
+  footprints_fetcher_->AddUserDevice(
+      BuildFastPairInfo(kValidModelId, kAccountKey1, &metadata),
+      base::DoNothing());
+
+  auto run_loop = std::make_unique<base::RunLoop>();
+  fast_pair_repository_->CheckAccountKeys(
+      filter, base::BindOnce(&FastPairRepositoryImplTest::VerifyAccountKeyCheck,
+                             base::Unretained(this), run_loop->QuitClosure(),
+                             /*expected_result=*/true));
+  run_loop->Run();
+}
+
+TEST_F(FastPairRepositoryImplTest, AssociateAccountKey_InvalidId) {
+  auto device = base::MakeRefCounted<Device>(kInvalidModelId, kTestBLEAddress,
+                                             Protocol::kFastPairInitial);
+  device->set_classic_address(kTestClassicAddress);
+  fast_pair_repository_->AssociateAccountKey(device, kAccountKey1);
+  base::RunLoop().RunUntilIdle();
+
+  ASSERT_FALSE(footprints_fetcher_->ContainsKey(kAccountKey1));
+}
+
+TEST_F(FastPairRepositoryImplTest, AssociateAccountKey_ValidId) {
+  auto device = base::MakeRefCounted<Device>(kValidModelId, kTestBLEAddress,
+                                             Protocol::kFastPairInitial);
+  device->set_classic_address(kTestClassicAddress);
+  fast_pair_repository_->AssociateAccountKey(device, kAccountKey1);
+  base::RunLoop().RunUntilIdle();
+
+  ASSERT_TRUE(footprints_fetcher_->ContainsKey(kAccountKey1));
+}
+
+TEST_F(FastPairRepositoryImplTest, DeleteAssociatedDevice_Valid) {
+  AccountKeyFilter filter(kFilterBytes1, {salt});
+  nearby::fastpair::GetObservedDeviceResponse response;
+  DeviceMetadata metadata(response, gfx::Image());
+
+  auto device = base::MakeRefCounted<Device>(kValidModelId, kTestBLEAddress,
+                                             Protocol::kFastPairInitial);
+  device->set_classic_address(kTestClassicAddress);
+  fast_pair_repository_->AssociateAccountKey(device, kAccountKey1);
+  base::RunLoop().RunUntilIdle();
+  ASSERT_TRUE(footprints_fetcher_->ContainsKey(kAccountKey1));
+
+  ASSERT_TRUE(fast_pair_repository_->DeleteAssociatedDevice(
+      &classic_bluetooth_device_));
+  base::RunLoop().RunUntilIdle();
+
+  ASSERT_FALSE(footprints_fetcher_->ContainsKey(kAccountKey1));
+}
+
+TEST_F(FastPairRepositoryImplTest, DeleteAssociatedDevice_Invalid) {
+  ASSERT_FALSE(fast_pair_repository_->DeleteAssociatedDevice(
+      &classic_bluetooth_device_));
+}
+
+TEST_F(FastPairRepositoryImplTest, FetchDeviceImages) {
+  ASSERT_FALSE(device_id_map_->GetModelIdForDeviceId(kTestDeviceId));
+  ASSERT_FALSE(device_image_store_->GetImagesForDeviceModel(kValidModelId));
+
+  AccountKeyFilter filter(kFilterBytes1, {salt});
+  nearby::fastpair::GetObservedDeviceResponse response;
+  DeviceMetadata metadata(response, gfx::Image());
+
+  auto device = base::MakeRefCounted<Device>(kValidModelId, kTestBLEAddress,
+                                             Protocol::kFastPairInitial);
+  device->set_classic_address(kTestClassicAddress);
+  fast_pair_repository_->FetchDeviceImages(device);
+  base::RunLoop().RunUntilIdle();
+
+  ASSERT_TRUE(device_id_map_->GetModelIdForDeviceId(kTestDeviceId));
+  ASSERT_TRUE(fast_pair_repository_->GetImagesForDevice(kTestDeviceId));
+}
+
+TEST_F(FastPairRepositoryImplTest, PersistDeviceImages) {
+  ASSERT_FALSE(device_id_map_->GetModelIdForDeviceId(kTestDeviceId));
+  ASSERT_FALSE(device_image_store_->GetImagesForDeviceModel(kValidModelId));
+
+  AccountKeyFilter filter(kFilterBytes1, {salt});
+  nearby::fastpair::GetObservedDeviceResponse response;
+  DeviceMetadata metadata(response, gfx::Image());
+
+  auto device = base::MakeRefCounted<Device>(kValidModelId, kTestBLEAddress,
+                                             Protocol::kFastPairInitial);
+  device->set_classic_address(kTestClassicAddress);
+  fast_pair_repository_->FetchDeviceImages(device);
+  fast_pair_repository_->PersistDeviceImages(device);
+  base::RunLoop().RunUntilIdle();
+
+  ASSERT_TRUE(device_id_map_->GetModelIdForDeviceId(kTestDeviceId));
+  ASSERT_TRUE(fast_pair_repository_->GetImagesForDevice(kTestDeviceId));
+}
+
+TEST_F(FastPairRepositoryImplTest, EvictDeviceImages) {
+  AccountKeyFilter filter(kFilterBytes1, {salt});
+  nearby::fastpair::GetObservedDeviceResponse response;
+  DeviceMetadata metadata(response, gfx::Image());
+
+  auto device = base::MakeRefCounted<Device>(kValidModelId, kTestBLEAddress,
+                                             Protocol::kFastPairInitial);
+  device->set_classic_address(kTestClassicAddress);
+  fast_pair_repository_->FetchDeviceImages(device);
+  fast_pair_repository_->PersistDeviceImages(device);
+  base::RunLoop().RunUntilIdle();
+
+  ASSERT_TRUE(device_id_map_->GetModelIdForDeviceId(kTestDeviceId));
+  ASSERT_TRUE(device_image_store_->GetImagesForDeviceModel(kValidModelId));
+
+  fast_pair_repository_->EvictDeviceImages(&classic_bluetooth_device_);
+  base::RunLoop().RunUntilIdle();
+
+  device_id_map_->RefreshCacheForTest();
+  ASSERT_FALSE(device_id_map_->GetModelIdForDeviceId(kTestDeviceId));
+}
+
+}  // namespace quick_pair
+}  // namespace ash
diff --git a/ash/quick_pair/repository/mock_fast_pair_repository.h b/ash/quick_pair/repository/mock_fast_pair_repository.h
index ee218e6e..475fe571 100644
--- a/ash/quick_pair/repository/mock_fast_pair_repository.h
+++ b/ash/quick_pair/repository/mock_fast_pair_repository.h
@@ -26,10 +26,6 @@
                DeviceMetadataCallback callback),
               (override));
   MOCK_METHOD(void,
-              IsValidModelId,
-              (const std::string& hex_model_id, ValidModelIdCallback callback),
-              (override));
-  MOCK_METHOD(void,
               CheckAccountKeys,
               (const AccountKeyFilter& account_key_filter,
                CheckAccountKeysCallback callback),
diff --git a/ash/services/quick_pair/quick_pair_process_unittest.cc b/ash/services/quick_pair/quick_pair_process_unittest.cc
index cc569e9..c2a49a8 100644
--- a/ash/services/quick_pair/quick_pair_process_unittest.cc
+++ b/ash/services/quick_pair/quick_pair_process_unittest.cc
@@ -7,13 +7,85 @@
 #include <cstdint>
 #include <vector>
 
+#include "ash/quick_pair/fast_pair_handshake/fast_pair_encryption.h"
+#include "ash/services/quick_pair/fast_pair_data_parser.h"
 #include "ash/services/quick_pair/public/cpp/decrypted_passkey.h"
 #include "ash/services/quick_pair/public/cpp/decrypted_response.h"
 #include "ash/services/quick_pair/public/cpp/not_discoverable_advertisement.h"
+#include "ash/services/quick_pair/quick_pair_process.h"
+#include "ash/services/quick_pair/quick_pair_process_manager.h"
+#include "ash/services/quick_pair/quick_pair_process_manager_impl.h"
 #include "base/callback_helpers.h"
+#include "base/run_loop.h"
 #include "base/test/bind.h"
+#include "base/test/task_environment.h"
+#include "mojo/public/cpp/bindings/shared_remote.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+namespace {
+
+const std::array<uint8_t, 16> kResponseBytes = {
+    0x01, 0x5E, 0x3F, 0x45, 0x61, 0xC3, 0x32, 0x1D,
+    0xA0, 0xBA, 0xF0, 0xBB, 0x95, 0x1F, 0xF7, 0xB6};
+
+const std::array<uint8_t, 16> kAesKeyBytes = {
+    0xA0, 0xBA, 0xF0, 0xBB, 0x95, 0x1F, 0xF7, 0xB6,
+    0xCF, 0x5E, 0x3F, 0x45, 0x61, 0xC3, 0x32, 0x1D};
+
+const std::array<uint8_t, 16> kPasskeyBytes = {
+    0x02, 0x5E, 0x3F, 0x45, 0x61, 0xC3, 0x32, 0x1D,
+    0xA0, 0xBA, 0xF0, 0xBB, 0x95, 0x1F, 0xF7, 0xB6};
+
+class FakeQuickPairProcessManager
+    : public ash::quick_pair::QuickPairProcessManager {
+ public:
+  class FakeQuickPairProcessReference
+      : public ash::quick_pair::QuickPairProcessManager::ProcessReference {
+   public:
+    FakeQuickPairProcessReference(
+        mojo::SharedRemote<ash::quick_pair::mojom::FastPairDataParser>
+            data_parser_remote)
+        : data_parser_remote_(data_parser_remote) {}
+
+    ~FakeQuickPairProcessReference() override = default;
+
+    const mojo::SharedRemote<ash::quick_pair::mojom::FastPairDataParser>&
+    GetFastPairDataParser() const override {
+      return data_parser_remote_;
+    }
+
+   private:
+    mojo::SharedRemote<ash::quick_pair::mojom::FastPairDataParser>
+        data_parser_remote_;
+  };
+
+  FakeQuickPairProcessManager() {
+    data_parser_ = std::make_unique<ash::quick_pair::FastPairDataParser>(
+        fast_pair_data_parser_.InitWithNewPipeAndPassReceiver());
+
+    data_parser_remote_.Bind(std::move(fast_pair_data_parser_),
+                             task_enviornment_.GetMainThreadTaskRunner());
+  }
+
+  ~FakeQuickPairProcessManager() override = default;
+
+  std::unique_ptr<ProcessReference> GetProcessReference(
+      ProcessStoppedCallback on_process_stopped_callback) override {
+    return std::make_unique<FakeQuickPairProcessReference>(data_parser_remote_);
+  }
+
+ private:
+  base::test::SingleThreadTaskEnvironment task_enviornment_;
+  mojo::SharedRemote<ash::quick_pair::mojom::FastPairDataParser>
+      data_parser_remote_;
+  mojo::PendingRemote<ash::quick_pair::mojom::FastPairDataParser>
+      fast_pair_data_parser_;
+  std::unique_ptr<ash::quick_pair::FastPairDataParser> data_parser_;
+  ProcessStoppedCallback on_process_stopped_callback_;
+};
+
+}  // namespace
+
 namespace ash {
 namespace quick_pair {
 namespace quick_pair_process {
@@ -62,6 +134,57 @@
       base::DoNothing());
 }
 
+TEST_F(QuickPairProcessTest,
+       ParseMessageStreamMessages_NoValueIfNoProcessManagerSet) {
+  ParseMessageStreamMessages(
+      std::vector<uint8_t>(),
+      base::BindLambdaForTesting(
+          [](std::vector<mojom::MessageStreamMessagePtr> messages) {
+            EXPECT_TRUE(messages.empty());
+          }),
+      base::DoNothing());
+}
+
+TEST_F(QuickPairProcessTest, ParseDecryptedResponse_ValueIfProcessManagerSet) {
+  auto process_manager = std::make_unique<FakeQuickPairProcessManager>();
+  quick_pair_process::SetProcessManager(process_manager.get());
+
+  const std::array<uint8_t, 16> encrypted_response_bytes =
+      fast_pair_encryption::EncryptBytes(kAesKeyBytes, kResponseBytes);
+  base::RunLoop run_loop;
+  ParseDecryptedResponse(
+      std::vector<uint8_t>(kAesKeyBytes.begin(), kAesKeyBytes.end()),
+      std::vector<uint8_t>(encrypted_response_bytes.begin(),
+                           encrypted_response_bytes.end()),
+      base::BindLambdaForTesting(
+          [&run_loop](const absl::optional<DecryptedResponse>& result) {
+            EXPECT_TRUE(result.has_value());
+            run_loop.Quit();
+          }),
+      base::DoNothing());
+  run_loop.Run();
+}
+
+TEST_F(QuickPairProcessTest, ParseDecryptedPasskey_ValueIfProcessManagerSet) {
+  auto process_manager = std::make_unique<FakeQuickPairProcessManager>();
+  quick_pair_process::SetProcessManager(process_manager.get());
+
+  const std::array<uint8_t, 16> encrypted_passkey_bytes =
+      fast_pair_encryption::EncryptBytes(kAesKeyBytes, kPasskeyBytes);
+  base::RunLoop run_loop;
+  ParseDecryptedPasskey(
+      std::vector<uint8_t>(kAesKeyBytes.begin(), kAesKeyBytes.end()),
+      std::vector<uint8_t>(encrypted_passkey_bytes.begin(),
+                           encrypted_passkey_bytes.end()),
+      base::BindLambdaForTesting(
+          [&run_loop](const absl::optional<DecryptedPasskey>& result) {
+            EXPECT_TRUE(result.has_value());
+            run_loop.Quit();
+          }),
+      base::DoNothing());
+  run_loop.Run();
+}
+
 }  // namespace quick_pair_process
 }  // namespace quick_pair
 }  // namespace ash
diff --git a/ash/system/accessibility/dictation_button_tray.cc b/ash/system/accessibility/dictation_button_tray.cc
index c28ac7c0..9e57a58 100644
--- a/ash/system/accessibility/dictation_button_tray.cc
+++ b/ash/system/accessibility/dictation_button_tray.cc
@@ -226,8 +226,7 @@
 
 void DictationButtonTray::UpdateOnSpeechRecognitionDownloadChanged(
     int download_progress) {
-  if (!::features::IsExperimentalAccessibilityDictationOfflineEnabled() ||
-      !visible_preferred())
+  if (!visible_preferred())
     return;
 
   bool download_in_progress = download_progress > 0 && download_progress < 100;
diff --git a/ash/system/accessibility/dictation_button_tray_unittest.cc b/ash/system/accessibility/dictation_button_tray_unittest.cc
index c227f31..72f75d6 100644
--- a/ash/system/accessibility/dictation_button_tray_unittest.cc
+++ b/ash/system/accessibility/dictation_button_tray_unittest.cc
@@ -193,7 +193,6 @@
     DictationButtonTrayTest::SetUp();
 
     std::vector<base::Feature> enabled_features = {
-        ::features::kExperimentalAccessibilityDictationOffline,
         features::kOnDeviceSpeechRecognition};
     std::vector<base::Feature> disabled_features;
 
diff --git a/ash/system/accessibility/tray_accessibility.cc b/ash/system/accessibility/tray_accessibility.cc
index b84681c4..e6d6fee5 100644
--- a/ash/system/accessibility/tray_accessibility.cc
+++ b/ash/system/accessibility/tray_accessibility.cc
@@ -99,7 +99,7 @@
 }
 
 AccessibilityDetailedView::~AccessibilityDetailedView() {
-  if (!::features::IsDictationOfflineAvailableAndEnabled())
+  if (!::features::IsDictationOfflineAvailable())
     return;
 
   speech::SodaInstaller* soda_installer = speech::SodaInstaller::GetInstance();
@@ -583,7 +583,7 @@
 }
 
 void AccessibilityDetailedView::UpdateSodaInstallerObserverStatus() {
-  if (!::features::IsDictationOfflineAvailableAndEnabled())
+  if (!::features::IsDictationOfflineAvailable())
     return;
 
   speech::SodaInstaller* soda_installer = speech::SodaInstaller::GetInstance();
diff --git a/ash/system/accessibility/tray_accessibility_unittest.cc b/ash/system/accessibility/tray_accessibility_unittest.cc
index ce4941e..3fcdd27 100644
--- a/ash/system/accessibility/tray_accessibility_unittest.cc
+++ b/ash/system/accessibility/tray_accessibility_unittest.cc
@@ -688,9 +688,7 @@
     // `ChromeBrowserMainPartsAsh` initializes). Create it here so that
     // calling speech::SodaInstaller::GetInstance() returns a valid instance.
     scoped_feature_list_.InitWithFeatures(
-        {::features::kExperimentalAccessibilityDictationOffline,
-         ash::features::kOnDeviceSpeechRecognition},
-        {});
+        {ash::features::kOnDeviceSpeechRecognition}, {});
     soda_installer_impl_ =
         std::make_unique<speech::SodaInstallerImplChromeOS>();
     soda_installer()->UninstallSodaForTesting();
diff --git a/ash/system/phonehub/camera_roll_opt_in_view.cc b/ash/system/phonehub/camera_roll_opt_in_view.cc
deleted file mode 100644
index b5733d0..0000000
--- a/ash/system/phonehub/camera_roll_opt_in_view.cc
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ash/system/phonehub/camera_roll_opt_in_view.h"
-
-#include <memory>
-#include <string>
-
-#include "ash/components/phonehub/camera_roll_manager.h"
-#include "ash/components/phonehub/util/histogram_util.h"
-#include "ash/public/cpp/new_window_delegate.h"
-#include "ash/system/phonehub/phone_hub_metrics.h"
-#include "ash/system/phonehub/phone_hub_view_ids.h"
-#include "chromeos/components/multidevice/logging/logging.h"
-#include "ui/base/metadata/metadata_impl_macros.h"
-
-namespace ash {
-
-CameraRollOptInView::CameraRollOptInView(
-    phonehub::CameraRollManager* camera_roll_manager)
-    : SubFeatureOptInView(PhoneHubViewID::kCameraRollOptInView,
-                          IDS_ASH_PHONE_HUB_CAMERA_ROLL_OPT_IN_DESCRIPTION,
-                          IDS_ASH_PHONE_HUB_CAMERA_ROLL_OPT_IN_TURN_ON_BUTTON),
-      camera_roll_manager_(camera_roll_manager) {}
-
-CameraRollOptInView::~CameraRollOptInView() = default;
-
-void CameraRollOptInView::SetUpButtonPressed() {
-  LogCameraRollOptInEvent(phone_hub_metrics::InterstitialScreenEvent::kConfirm);
-  camera_roll_manager_->EnableCameraRollFeatureInSystemSetting();
-  phonehub::util::LogCameraRollFeatureOptInEntryPoint(
-      phonehub::util::CameraRollOptInEntryPoint::kOnboardingDialog);
-}
-
-void CameraRollOptInView::DismissButtonPressed() {
-  LogCameraRollOptInEvent(phone_hub_metrics::InterstitialScreenEvent::kDismiss);
-  camera_roll_manager_->OnCameraRollOnboardingUiDismissed();
-}
-
-BEGIN_METADATA(CameraRollOptInView, views::View)
-END_METADATA
-
-}  // namespace ash
diff --git a/ash/system/phonehub/camera_roll_opt_in_view.h b/ash/system/phonehub/camera_roll_opt_in_view.h
deleted file mode 100644
index 63acc33..0000000
--- a/ash/system/phonehub/camera_roll_opt_in_view.h
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-#ifndef ASH_SYSTEM_PHONEHUB_CAMERA_ROLL_OPT_IN_VIEW_H_
-#define ASH_SYSTEM_PHONEHUB_CAMERA_ROLL_OPT_IN_VIEW_H_
-
-#include "ash/ash_export.h"
-#include "ash/components/phonehub/camera_roll_manager.h"
-#include "ash/system/phonehub/sub_feature_opt_in_view.h"
-#include "ui/base/metadata/metadata_header_macros.h"
-
-namespace ash {
-
-/**
- * Dialog inside camera roll view for user to opt in camera roll feature or
- * dismiss.
- */
-class ASH_EXPORT CameraRollOptInView : public SubFeatureOptInView {
- public:
-  METADATA_HEADER(CameraRollOptInView);
-
-  CameraRollOptInView(phonehub::CameraRollManager* camera_roll_manager);
-  ~CameraRollOptInView() override;
-  CameraRollOptInView(CameraRollOptInView&) = delete;
-  CameraRollOptInView operator=(CameraRollOptInView&) = delete;
-
- private:
-  // SubFeatureOptInView::
-  void SetUpButtonPressed() override;
-  void DismissButtonPressed() override;
-
-  phonehub::CameraRollManager* camera_roll_manager_ = nullptr;
-};
-
-}  // namespace ash
-
-#endif  // ASH_SYSTEM_PHONEHUB_CAMERA_ROLL_OPT_IN_VIEW_H_
diff --git a/ash/system/phonehub/camera_roll_view.cc b/ash/system/phonehub/camera_roll_view.cc
index 18a469fc..1f093db 100644
--- a/ash/system/phonehub/camera_roll_view.cc
+++ b/ash/system/phonehub/camera_roll_view.cc
@@ -94,9 +94,6 @@
   AddChildView(std::make_unique<HeaderView>());
   items_view_ = AddChildView(std::make_unique<CameraRollItemsView>());
 
-  opt_in_view_ =
-      AddChildView(std::make_unique<CameraRollOptInView>(camera_roll_manager_));
-
   Update();
   camera_roll_manager_->AddObserver(this);
 }
@@ -221,24 +218,16 @@
   switch (current_ui_state) {
     case phonehub::CameraRollManager::CameraRollUiState::SHOULD_HIDE:
     case phonehub::CameraRollManager::CameraRollUiState::NO_STORAGE_PERMISSION:
+    case phonehub::CameraRollManager::CameraRollUiState::CAN_OPT_IN:
       SetVisible(false);
       break;
-    case phonehub::CameraRollManager::CameraRollUiState::CAN_OPT_IN:
-      opt_in_view_->SetVisible(true);
-      items_view_->SetVisible(false);
-      SetVisible(true);
-      LogCameraRollOptInEvent(
-          phone_hub_metrics::InterstitialScreenEvent::kShown);
-      break;
     case phonehub::CameraRollManager::CameraRollUiState::LOADING_VIEW:
-      opt_in_view_->SetVisible(false);
       items_view_->SetVisible(true);
       SetVisible(true);
       items_view_->AddLoadingAnimatedItem(
           should_disable_annimator_timer_for_test_);
       break;
     case phonehub::CameraRollManager::CameraRollUiState::ITEMS_VISIBLE:
-      opt_in_view_->SetVisible(false);
       items_view_->SetVisible(true);
       SetVisible(true);
       const std::vector<phonehub::CameraRollItem> camera_roll_items =
diff --git a/ash/system/phonehub/camera_roll_view.h b/ash/system/phonehub/camera_roll_view.h
index 66fb42e..f0e435a 100644
--- a/ash/system/phonehub/camera_roll_view.h
+++ b/ash/system/phonehub/camera_roll_view.h
@@ -7,7 +7,6 @@
 
 #include "ash/ash_export.h"
 #include "ash/components/phonehub/camera_roll_manager.h"
-#include "ash/system/phonehub/camera_roll_opt_in_view.h"
 #include "base/gtest_prod_util.h"
 #include "ui/views/view.h"
 #include "ui/views/view_model.h"
@@ -81,7 +80,6 @@
 
   phonehub::CameraRollManager* camera_roll_manager_ = nullptr;
   phonehub::UserActionRecorder* user_action_recorder_ = nullptr;
-  CameraRollOptInView* opt_in_view_ = nullptr;
   CameraRollItemsView* items_view_ = nullptr;
 };
 
diff --git a/ash/system/phonehub/camera_roll_view_unittest.cc b/ash/system/phonehub/camera_roll_view_unittest.cc
index 29e0571..5d2906a 100644
--- a/ash/system/phonehub/camera_roll_view_unittest.cc
+++ b/ash/system/phonehub/camera_roll_view_unittest.cc
@@ -126,15 +126,6 @@
   std::unique_ptr<phonehub::FakeCameraRollManager> fake_camera_roll_manager_;
 };
 
-TEST_F(CameraRollViewTest, DisplayOptInView) {
-  PresetCameraRollOptInState(/*has_been_dismissed=*/false,
-                             /*can_be_enabled=*/true);
-
-  fake_camera_roll_manager()->ClearCurrentItems();
-  EXPECT_TRUE(camera_roll_view()->GetVisible());
-  EXPECT_TRUE(camera_roll_view()->opt_in_view_->GetVisible());
-}
-
 TEST_F(CameraRollViewTest, OptInAlready) {
   PresetCameraRollOptInState(/*has_been_dismissed=*/false,
                              /*can_be_enabled=*/false);
@@ -144,7 +135,6 @@
 
   fake_camera_roll_manager()->SetCurrentItems(CreateFakeItems(1));
   EXPECT_TRUE(camera_roll_view()->GetVisible());
-  EXPECT_FALSE(camera_roll_view()->opt_in_view_->GetVisible());
   EXPECT_TRUE(camera_roll_view()->items_view_->GetVisible());
 }
 
@@ -154,7 +144,6 @@
   fake_camera_roll_manager()->EnableCameraRollFeatureInSystemSetting();
 
   EXPECT_TRUE(camera_roll_view()->GetVisible());
-  EXPECT_FALSE(camera_roll_view()->opt_in_view_->GetVisible());
   EXPECT_TRUE(camera_roll_view()->items_view_->GetVisible());
   // There should be 4 camera roll item placeholder.
   size_t expected_placeholder_seize = 4;
diff --git a/ash/system/phonehub/multidevice_feature_opt_in_view.cc b/ash/system/phonehub/multidevice_feature_opt_in_view.cc
new file mode 100644
index 0000000..31a4406
--- /dev/null
+++ b/ash/system/phonehub/multidevice_feature_opt_in_view.cc
@@ -0,0 +1,100 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/system/phonehub/multidevice_feature_opt_in_view.h"
+
+#include <memory>
+#include <string>
+
+#include "ash/components/phonehub/multidevice_feature_access_manager.h"
+#include "ash/constants/ash_features.h"
+#include "ash/public/cpp/new_window_delegate.h"
+#include "ash/strings/grit/ash_strings.h"
+#include "ash/style/ash_color_provider.h"
+#include "ash/system/phonehub/phone_hub_metrics.h"
+#include "ash/system/phonehub/phone_hub_view_ids.h"
+#include "ui/base/metadata/metadata_impl_macros.h"
+
+namespace ash {
+
+using phone_hub_metrics::InterstitialScreenEvent;
+using phone_hub_metrics::LogInterstitialScreenEvent;
+
+namespace {
+// URL of the multidevice settings page with the URL parameter that will
+// start up the opt-in-flow.
+// TODO: Update this URL once the new access setup dialog has been updated
+constexpr char kMultideviceSettingsUrl[] =
+    "chrome://os-settings/multidevice/"
+    "features?showNotificationAccessSetupDialog";
+
+}  // namespace
+
+MultideviceFeatureOptInView::MultideviceFeatureOptInView(
+    phonehub::MultideviceFeatureAccessManager*
+        multidevice_feature_access_manager)
+    : SubFeatureOptInView(PhoneHubViewID::kMultideviceFeatureOptInView,
+                          IDS_ASH_PHONE_HUB_NOTIFICATION_OPT_IN_DESCRIPTION,
+                          IDS_ASH_PHONE_HUB_NOTIFICATION_OPT_IN_SET_UP_BUTTON),
+      multidevice_feature_access_manager_(multidevice_feature_access_manager) {
+  DCHECK(multidevice_feature_access_manager_);
+  access_manager_observation_.Observe(multidevice_feature_access_manager_);
+
+  // Checks and updates its visibility upon creation.
+  UpdateVisibility();
+
+  // TODO: Update metric event to a non-notification specific event
+  LogNotificationOptInEvent(InterstitialScreenEvent::kShown);
+}
+
+MultideviceFeatureOptInView::~MultideviceFeatureOptInView() = default;
+
+void MultideviceFeatureOptInView::SetUpButtonPressed() {
+  // Opens the notification set up dialog in settings to start the opt in flow.
+  LogNotificationOptInEvent(InterstitialScreenEvent::kConfirm);
+  // This intentionally uses GetInstance() to open an OS Settings page in ash.
+  NewWindowDelegate::GetInstance()->OpenUrl(
+      GURL(kMultideviceSettingsUrl),
+      NewWindowDelegate::OpenUrlFrom::kUserInteraction);
+}
+
+void MultideviceFeatureOptInView::DismissButtonPressed() {
+  // Dismiss this view if user chose to opt out and update the bubble size.
+  LogNotificationOptInEvent(InterstitialScreenEvent::kDismiss);
+  SetVisible(false);
+  multidevice_feature_access_manager_->DismissSetupRequiredUi();
+}
+
+void MultideviceFeatureOptInView::OnNotificationAccessChanged() {
+  UpdateVisibility();
+}
+
+void MultideviceFeatureOptInView::OnCameraRollAccessChanged() {
+  UpdateVisibility();
+}
+
+void MultideviceFeatureOptInView::UpdateVisibility() {
+  DCHECK(multidevice_feature_access_manager_);
+
+  // Can only request access if it is available but has not yet been granted.
+  bool can_request_notification_access =
+      multidevice_feature_access_manager_->GetNotificationAccessStatus() ==
+      phonehub::MultideviceFeatureAccessManager::AccessStatus::
+          kAvailableButNotGranted;
+  bool can_request_camera_roll_access =
+      features::IsPhoneHubCameraRollEnabled() &&
+      multidevice_feature_access_manager_->GetCameraRollAccessStatus() ==
+          phonehub::MultideviceFeatureAccessManager::AccessStatus::
+              kAvailableButNotGranted;
+  const bool should_show =
+      (can_request_notification_access || can_request_camera_roll_access) &&
+      !multidevice_feature_access_manager_
+           ->HasMultideviceFeatureSetupUiBeenDismissed();
+  SetVisible(should_show);
+}
+
+BEGIN_METADATA(MultideviceFeatureOptInView, views::View)
+END_METADATA
+
+}  // namespace ash
diff --git a/ash/system/phonehub/multidevice_feature_opt_in_view.h b/ash/system/phonehub/multidevice_feature_opt_in_view.h
new file mode 100644
index 0000000..ebba6135
--- /dev/null
+++ b/ash/system/phonehub/multidevice_feature_opt_in_view.h
@@ -0,0 +1,55 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_SYSTEM_PHONEHUB_MULTIDEVICE_FEATURE_OPT_IN_VIEW_H_
+#define ASH_SYSTEM_PHONEHUB_MULTIDEVICE_FEATURE_OPT_IN_VIEW_H_
+
+#include "ash/ash_export.h"
+#include "ash/components/phonehub/multidevice_feature_access_manager.h"
+#include "ash/system/phonehub/sub_feature_opt_in_view.h"
+#include "base/scoped_observation.h"
+#include "ui/base/metadata/metadata_header_macros.h"
+
+namespace ash {
+
+// An additional entry point shown on the Phone Hub bubble for the user to grant
+// access or opt out for multidevice feature from the phone.
+// Available multidevice features: 1. Notification 2. Camera Roll
+class ASH_EXPORT MultideviceFeatureOptInView
+    : public SubFeatureOptInView,
+      public phonehub::MultideviceFeatureAccessManager::Observer {
+ public:
+  METADATA_HEADER(MultideviceFeatureOptInView);
+
+  explicit MultideviceFeatureOptInView(
+      phonehub::MultideviceFeatureAccessManager*
+          multidevice_feature_access_manager);
+  MultideviceFeatureOptInView(const MultideviceFeatureOptInView&) = delete;
+  MultideviceFeatureOptInView& operator=(const MultideviceFeatureOptInView&) =
+      delete;
+  ~MultideviceFeatureOptInView() override;
+
+  // phonehub::MultideviceFeatureAccessManager::Observer:
+  void OnNotificationAccessChanged() override;
+  void OnCameraRollAccessChanged() override;
+
+ private:
+  void SetUpButtonPressed() override;
+  void DismissButtonPressed() override;
+
+  // Calculates whether this view should be visible and updates its visibility
+  // accordingly.
+  void UpdateVisibility();
+
+  phonehub::MultideviceFeatureAccessManager*
+      multidevice_feature_access_manager_;
+
+  base::ScopedObservation<phonehub::MultideviceFeatureAccessManager,
+                          phonehub::MultideviceFeatureAccessManager::Observer>
+      access_manager_observation_{this};
+};
+
+}  // namespace ash
+
+#endif  // ASH_SYSTEM_PHONEHUB_MULTIDEVICE_FEATURE_OPT_IN_VIEW_H_
diff --git a/ash/system/phonehub/notification_opt_in_view.cc b/ash/system/phonehub/notification_opt_in_view.cc
deleted file mode 100644
index 8e8bcdf8..0000000
--- a/ash/system/phonehub/notification_opt_in_view.cc
+++ /dev/null
@@ -1,85 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ash/system/phonehub/notification_opt_in_view.h"
-
-#include <memory>
-#include <string>
-
-#include "ash/components/phonehub/notification_access_manager.h"
-#include "ash/public/cpp/new_window_delegate.h"
-#include "ash/strings/grit/ash_strings.h"
-#include "ash/style/ash_color_provider.h"
-#include "ash/system/phonehub/phone_hub_metrics.h"
-#include "ash/system/phonehub/phone_hub_view_ids.h"
-#include "ui/base/metadata/metadata_impl_macros.h"
-
-namespace ash {
-
-using phone_hub_metrics::InterstitialScreenEvent;
-using phone_hub_metrics::LogInterstitialScreenEvent;
-
-namespace {
-// URL of the multidevice settings page with the URL parameter that will
-// start up the opt-in-flow.
-constexpr char kMultideviceSettingsUrl[] =
-    "chrome://os-settings/multidevice/"
-    "features?showNotificationAccessSetupDialog";
-
-}  // namespace
-
-NotificationOptInView::NotificationOptInView(
-    phonehub::NotificationAccessManager* notification_access_manager)
-    : SubFeatureOptInView(PhoneHubViewID::kNotificationOptInView,
-                          IDS_ASH_PHONE_HUB_NOTIFICATION_OPT_IN_DESCRIPTION,
-                          IDS_ASH_PHONE_HUB_NOTIFICATION_OPT_IN_SET_UP_BUTTON),
-      notification_access_manager_(notification_access_manager) {
-  DCHECK(notification_access_manager_);
-  access_manager_observation_.Observe(notification_access_manager_);
-
-  // Checks and updates its visibility upon creation.
-  UpdateVisibility();
-
-  LogNotificationOptInEvent(InterstitialScreenEvent::kShown);
-}
-
-NotificationOptInView::~NotificationOptInView() = default;
-
-void NotificationOptInView::SetUpButtonPressed() {
-  // Opens the notification set up dialog in settings to start the opt in flow.
-  LogNotificationOptInEvent(InterstitialScreenEvent::kConfirm);
-  // This intentionally uses GetInstance() to open an OS Settings page in ash.
-  NewWindowDelegate::GetInstance()->OpenUrl(
-      GURL(kMultideviceSettingsUrl),
-      NewWindowDelegate::OpenUrlFrom::kUserInteraction);
-}
-
-void NotificationOptInView::DismissButtonPressed() {
-  // Dismiss this view if user chose to opt out and update the bubble size.
-  LogNotificationOptInEvent(InterstitialScreenEvent::kDismiss);
-  SetVisible(false);
-  notification_access_manager_->DismissSetupRequiredUi();
-}
-
-void NotificationOptInView::OnNotificationAccessChanged() {
-  UpdateVisibility();
-}
-
-void NotificationOptInView::UpdateVisibility() {
-  DCHECK(notification_access_manager_);
-
-  // Can only request access if it is available but has not yet been granted.
-  bool can_request_access = notification_access_manager_->GetAccessStatus() ==
-                            phonehub::NotificationAccessManager::AccessStatus::
-                                kAvailableButNotGranted;
-  const bool should_show =
-      can_request_access &&
-      !notification_access_manager_->HasNotificationSetupUiBeenDismissed();
-  SetVisible(should_show);
-}
-
-BEGIN_METADATA(NotificationOptInView, views::View)
-END_METADATA
-
-}  // namespace ash
diff --git a/ash/system/phonehub/notification_opt_in_view.h b/ash/system/phonehub/notification_opt_in_view.h
deleted file mode 100644
index 8f39c75..0000000
--- a/ash/system/phonehub/notification_opt_in_view.h
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef ASH_SYSTEM_PHONEHUB_NOTIFICATION_OPT_IN_VIEW_H_
-#define ASH_SYSTEM_PHONEHUB_NOTIFICATION_OPT_IN_VIEW_H_
-
-#include "ash/ash_export.h"
-#include "ash/components/phonehub/notification_access_manager.h"
-#include "ash/system/phonehub/sub_feature_opt_in_view.h"
-#include "base/scoped_observation.h"
-#include "ui/base/metadata/metadata_header_macros.h"
-
-namespace ash {
-
-// An additional entry point shown on the Phone Hub bubble for the user to grant
-// access or opt out for notifications from the phone.
-class ASH_EXPORT NotificationOptInView
-    : public SubFeatureOptInView,
-      public phonehub::NotificationAccessManager::Observer {
- public:
-  METADATA_HEADER(NotificationOptInView);
-
-  explicit NotificationOptInView(
-      phonehub::NotificationAccessManager* notification_access_manager);
-  NotificationOptInView(const NotificationOptInView&) = delete;
-  NotificationOptInView& operator=(const NotificationOptInView&) = delete;
-  ~NotificationOptInView() override;
-
-  // phonehub::NotificationAccessManager::Observer:
-  void OnNotificationAccessChanged() override;
- private:
-  void SetUpButtonPressed() override;
-  void DismissButtonPressed() override;
-
-  // Calculates whether this view should be visible and updates its visibility
-  // accordingly.
-  void UpdateVisibility();
-
-  phonehub::NotificationAccessManager* notification_access_manager_;
-
-  base::ScopedObservation<phonehub::NotificationAccessManager,
-                          phonehub::NotificationAccessManager::Observer>
-      access_manager_observation_{this};
-};
-
-}  // namespace ash
-
-#endif  // ASH_SYSTEM_PHONEHUB_NOTIFICATION_OPT_IN_VIEW_H_
diff --git a/ash/system/phonehub/phone_connected_view.cc b/ash/system/phonehub/phone_connected_view.cc
index fe7e9e0..0681233 100644
--- a/ash/system/phonehub/phone_connected_view.cc
+++ b/ash/system/phonehub/phone_connected_view.cc
@@ -6,12 +6,12 @@
 
 #include <memory>
 
-#include "ash/components/phonehub/notification_access_manager.h"
+#include "ash/components/phonehub/multidevice_feature_access_manager.h"
 #include "ash/components/phonehub/phone_hub_manager.h"
 #include "ash/constants/ash_features.h"
 #include "ash/style/ash_color_provider.h"
 #include "ash/system/phonehub/camera_roll_view.h"
-#include "ash/system/phonehub/notification_opt_in_view.h"
+#include "ash/system/phonehub/multidevice_feature_opt_in_view.h"
 #include "ash/system/phonehub/phone_hub_recent_apps_view.h"
 #include "ash/system/phonehub/phone_hub_view_ids.h"
 #include "ash/system/phonehub/phone_status_view.h"
@@ -44,8 +44,8 @@
       gfx::Insets(0, kBubbleHorizontalSidePaddingDip)));
   layout->SetDefaultFlex(1);
 
-  AddChildView(std::make_unique<NotificationOptInView>(
-      phone_hub_manager->GetNotificationAccessManager()));
+  AddChildView(std::make_unique<MultideviceFeatureOptInView>(
+      phone_hub_manager->GetMultideviceFeatureAccessManager()));
 
   setup_layered_view(
       AddChildView(std::make_unique<QuickActionsView>(phone_hub_manager)));
diff --git a/ash/system/phonehub/phone_hub_tray_unittest.cc b/ash/system/phonehub/phone_hub_tray_unittest.cc
index 61b3e56..16c132e 100644
--- a/ash/system/phonehub/phone_hub_tray_unittest.cc
+++ b/ash/system/phonehub/phone_hub_tray_unittest.cc
@@ -5,13 +5,13 @@
 #include "ash/system/phonehub/phone_hub_tray.h"
 
 #include "ash/components/phonehub/fake_connection_scheduler.h"
-#include "ash/components/phonehub/fake_notification_access_manager.h"
+#include "ash/components/phonehub/fake_multidevice_feature_access_manager.h"
 #include "ash/components/phonehub/fake_phone_hub_manager.h"
 #include "ash/components/phonehub/phone_model_test_util.h"
 #include "ash/constants/ash_features.h"
 #include "ash/public/cpp/test/test_new_window_delegate.h"
 #include "ash/shell.h"
-#include "ash/system/phonehub/notification_opt_in_view.h"
+#include "ash/system/phonehub/multidevice_feature_opt_in_view.h"
 #include "ash/system/phonehub/phone_hub_ui_controller.h"
 #include "ash/system/phonehub/phone_hub_view_ids.h"
 #include "ash/system/status_area_widget.h"
@@ -48,7 +48,10 @@
 
   // AshTestBase:
   void SetUp() override {
-    feature_list_.InitAndEnableFeature(chromeos::features::kPhoneHub);
+    feature_list_.InitWithFeatures(
+        /*enabled_features=*/{chromeos::features::kPhoneHub,
+                              chromeos::features::kPhoneHubCameraRoll},
+        /*disabled_features=*/{});
     auto delegate = std::make_unique<MockNewWindowDelegate>();
     new_window_delegate_ = delegate.get();
     delegate_provider_ =
@@ -70,8 +73,9 @@
     return phone_hub_manager_.fake_feature_status_provider();
   }
 
-  phonehub::FakeNotificationAccessManager* GetNotificationAccessManager() {
-    return phone_hub_manager_.fake_notification_access_manager();
+  phonehub::FakeMultideviceFeatureAccessManager*
+  GetMultideviceFeatureAccessManager() {
+    return phone_hub_manager_.fake_multidevice_feature_access_manager();
   }
 
   phonehub::FakeConnectionScheduler* GetConnectionScheduler() {
@@ -105,9 +109,9 @@
     return phone_hub_tray_->content_view_for_testing();
   }
 
-  NotificationOptInView* notification_opt_in_view() {
-    return static_cast<NotificationOptInView*>(
-        bubble_view()->GetViewByID(PhoneHubViewID::kNotificationOptInView));
+  MultideviceFeatureOptInView* multidevice_feature_opt_in_view() {
+    return static_cast<MultideviceFeatureOptInView*>(bubble_view()->GetViewByID(
+        PhoneHubViewID::kMultideviceFeatureOptInView));
   }
 
   views::View* onboarding_main_view() {
@@ -218,54 +222,87 @@
   EXPECT_TRUE(phone_hub_tray_->GetBubbleView()->GetWidget()->IsActive());
 }
 
-TEST_F(PhoneHubTrayTest, ShowNotificationOptInViewWhenAccessNotGranted) {
-  GetNotificationAccessManager()->SetAccessStatusInternal(
-      phonehub::NotificationAccessManager::AccessStatus::
+TEST_F(PhoneHubTrayTest, ShowOptInViewWhenNotificationAccessNotGranted) {
+  GetMultideviceFeatureAccessManager()->SetNotificationAccessStatusInternal(
+      phonehub::MultideviceFeatureAccessManager::AccessStatus::
           kAvailableButNotGranted);
+  GetMultideviceFeatureAccessManager()->SetCameraRollAccessStatusInternal(
+      phonehub::MultideviceFeatureAccessManager::AccessStatus::kAccessGranted);
 
   ClickTrayButton();
 
-  EXPECT_TRUE(notification_opt_in_view());
-  EXPECT_TRUE(notification_opt_in_view()->GetVisible());
+  EXPECT_TRUE(multidevice_feature_opt_in_view());
+  EXPECT_TRUE(multidevice_feature_opt_in_view()->GetVisible());
 
   // Simulate a click on "Dismiss" button.
   LeftClickOn(notification_opt_in_dismiss_button());
 
   // Clicking on "Dismiss" should hide the view and also disable the ability to
   // show it again.
-  EXPECT_FALSE(notification_opt_in_view()->GetVisible());
-  EXPECT_TRUE(
-      GetNotificationAccessManager()->HasNotificationSetupUiBeenDismissed());
+  EXPECT_FALSE(multidevice_feature_opt_in_view()->GetVisible());
+  EXPECT_TRUE(GetMultideviceFeatureAccessManager()
+                  ->HasMultideviceFeatureSetupUiBeenDismissed());
 }
 
-TEST_F(PhoneHubTrayTest, HideNotificationOptInViewWhenAccessHasBeenGranted) {
-  GetNotificationAccessManager()->SetAccessStatusInternal(
-      phonehub::NotificationAccessManager::AccessStatus::kAccessGranted);
-
-  ClickTrayButton();
-
-  EXPECT_TRUE(notification_opt_in_view());
-  EXPECT_FALSE(notification_opt_in_view()->GetVisible());
-}
-
-TEST_F(PhoneHubTrayTest, HideNotificationOptInViewWhenAccessIsProhibited) {
-  GetNotificationAccessManager()->SetAccessStatusInternal(
-      phonehub::NotificationAccessManager::AccessStatus::kProhibited);
-
-  ClickTrayButton();
-
-  EXPECT_TRUE(notification_opt_in_view());
-  EXPECT_FALSE(notification_opt_in_view()->GetVisible());
-}
-
-TEST_F(PhoneHubTrayTest, StartNotificationSetUpFlow) {
-  GetNotificationAccessManager()->SetAccessStatusInternal(
-      phonehub::NotificationAccessManager::AccessStatus::
+TEST_F(PhoneHubTrayTest, ShowOptInViewWhenCameraRollAccessNotGranted) {
+  GetMultideviceFeatureAccessManager()->SetNotificationAccessStatusInternal(
+      phonehub::MultideviceFeatureAccessManager::AccessStatus::kAccessGranted);
+  GetMultideviceFeatureAccessManager()->SetCameraRollAccessStatusInternal(
+      phonehub::MultideviceFeatureAccessManager::AccessStatus::
           kAvailableButNotGranted);
 
   ClickTrayButton();
-  EXPECT_TRUE(notification_opt_in_view());
-  EXPECT_TRUE(notification_opt_in_view()->GetVisible());
+
+  EXPECT_TRUE(multidevice_feature_opt_in_view());
+  EXPECT_TRUE(multidevice_feature_opt_in_view()->GetVisible());
+
+  // Simulate a click on "Dismiss" button.
+  LeftClickOn(notification_opt_in_dismiss_button());
+
+  // Clicking on "Dismiss" should hide the view and also disable the ability to
+  // show it again.
+  EXPECT_FALSE(multidevice_feature_opt_in_view()->GetVisible());
+  EXPECT_TRUE(GetMultideviceFeatureAccessManager()
+                  ->HasMultideviceFeatureSetupUiBeenDismissed());
+}
+
+TEST_F(PhoneHubTrayTest, HideOptInViewWhenAllFeatureAccessHasBeenGranted) {
+  GetMultideviceFeatureAccessManager()->SetNotificationAccessStatusInternal(
+      phonehub::MultideviceFeatureAccessManager::AccessStatus::kAccessGranted);
+  GetMultideviceFeatureAccessManager()->SetCameraRollAccessStatusInternal(
+      phonehub::MultideviceFeatureAccessManager::AccessStatus::kAccessGranted);
+
+  ClickTrayButton();
+
+  EXPECT_TRUE(multidevice_feature_opt_in_view());
+  EXPECT_FALSE(multidevice_feature_opt_in_view()->GetVisible());
+}
+
+TEST_F(
+    PhoneHubTrayTest,
+    HideOptInViewWhenNotificationAccessIsProhibitedAndCameraRollAccessIsGranted) {
+  GetMultideviceFeatureAccessManager()->SetNotificationAccessStatusInternal(
+      phonehub::MultideviceFeatureAccessManager::AccessStatus::kProhibited);
+  GetMultideviceFeatureAccessManager()->SetCameraRollAccessStatusInternal(
+      phonehub::MultideviceFeatureAccessManager::AccessStatus::kAccessGranted);
+
+  ClickTrayButton();
+
+  EXPECT_TRUE(multidevice_feature_opt_in_view());
+  EXPECT_FALSE(multidevice_feature_opt_in_view()->GetVisible());
+}
+
+TEST_F(PhoneHubTrayTest, StartMultideviceFeatureSetUpFlow) {
+  GetMultideviceFeatureAccessManager()->SetNotificationAccessStatusInternal(
+      phonehub::MultideviceFeatureAccessManager::AccessStatus::
+          kAvailableButNotGranted);
+  GetMultideviceFeatureAccessManager()->SetCameraRollAccessStatusInternal(
+      phonehub::MultideviceFeatureAccessManager::AccessStatus::
+          kAvailableButNotGranted);
+
+  ClickTrayButton();
+  EXPECT_TRUE(multidevice_feature_opt_in_view());
+  EXPECT_TRUE(multidevice_feature_opt_in_view()->GetVisible());
 
   // Clicking on the set up button should open the corresponding settings page
   // for the notification set up flow.
@@ -277,19 +314,22 @@
   LeftClickOn(notification_opt_in_set_up_button());
 
   // Simulate that notification access has been granted.
-  GetNotificationAccessManager()->SetAccessStatusInternal(
-      phonehub::NotificationAccessManager::AccessStatus::kAccessGranted);
+  GetMultideviceFeatureAccessManager()->SetNotificationAccessStatusInternal(
+      phonehub::MultideviceFeatureAccessManager::AccessStatus::kAccessGranted);
+  // Simulate that camera roll access has been granted.
+  GetMultideviceFeatureAccessManager()->SetCameraRollAccessStatusInternal(
+      phonehub::MultideviceFeatureAccessManager::AccessStatus::kAccessGranted);
 
   // This view should be dismissed.
-  EXPECT_FALSE(notification_opt_in_view()->GetVisible());
+  EXPECT_FALSE(multidevice_feature_opt_in_view()->GetVisible());
 
   // Simulate that notification access has been revoked by the phone.
-  GetNotificationAccessManager()->SetAccessStatusInternal(
-      phonehub::NotificationAccessManager::AccessStatus::
+  GetMultideviceFeatureAccessManager()->SetNotificationAccessStatusInternal(
+      phonehub::MultideviceFeatureAccessManager::AccessStatus::
           kAvailableButNotGranted);
 
   // This view should show up again.
-  EXPECT_TRUE(notification_opt_in_view()->GetVisible());
+  EXPECT_TRUE(multidevice_feature_opt_in_view()->GetVisible());
 }
 
 TEST_F(PhoneHubTrayTest, HideTrayItemOnUiStateChange) {
diff --git a/ash/system/phonehub/phone_hub_view_ids.h b/ash/system/phonehub/phone_hub_view_ids.h
index 04b9a7e3..425dea1d 100644
--- a/ash/system/phonehub/phone_hub_view_ids.h
+++ b/ash/system/phonehub/phone_hub_view_ids.h
@@ -18,10 +18,7 @@
   kTaskContinuationView,
 
   // Notification opt in view and its components.
-  kNotificationOptInView,
-
-  // Camera Roll opt in view and its components.
-  kCameraRollOptInView,
+  kMultideviceFeatureOptInView,
 
   // Sub feature opt in view buttons
   kSubFeatureOptInConfirmButton,
diff --git a/ash/utility/lottie-util b/ash/utility/lottie-util
deleted file mode 100644
index e69de29..0000000
--- a/ash/utility/lottie-util
+++ /dev/null
diff --git a/ash/wallpaper/wallpaper_controller_impl.cc b/ash/wallpaper/wallpaper_controller_impl.cc
index 27abff1..bb58c026d 100644
--- a/ash/wallpaper/wallpaper_controller_impl.cc
+++ b/ash/wallpaper/wallpaper_controller_impl.cc
@@ -1095,7 +1095,7 @@
     const base::FilePath& file_path,
     WallpaperLayout layout,
     bool preview_mode,
-    SetCustomWallpaperCallback callback) {
+    SetWallpaperCallback callback) {
   DCHECK(Shell::Get()->session_controller()->IsActiveUserSessionStarted());
   if (!CanSetUserWallpaper(account_id)) {
     // Return early to skip the work of decoding.
@@ -1150,7 +1150,7 @@
 
 void WallpaperControllerImpl::SetOnlineWallpaper(
     const OnlineWallpaperParams& params,
-    SetOnlineWallpaperCallback callback) {
+    SetWallpaperCallback callback) {
   DCHECK(callback);
   DCHECK(Shell::Get()->session_controller()->IsActiveUserSessionStarted());
   if (!CanSetUserWallpaper(params.account_id)) {
@@ -1168,7 +1168,7 @@
 
 void WallpaperControllerImpl::SetOnlineWallpaperIfExists(
     const OnlineWallpaperParams& params,
-    SetOnlineWallpaperCallback callback) {
+    SetWallpaperCallback callback) {
   DCHECK(callback);
   DCHECK(Shell::Get()->session_controller()->IsActiveUserSessionStarted());
   DVLOG(1) << __func__ << " params=" << params;
@@ -1217,7 +1217,7 @@
 void WallpaperControllerImpl::SetOnlineWallpaperFromData(
     const OnlineWallpaperParams& params,
     const std::string& image_data,
-    SetOnlineWallpaperCallback callback) {
+    SetWallpaperCallback callback) {
   if (!Shell::Get()->session_controller()->IsActiveUserSessionStarted() ||
       !CanSetUserWallpaper(params.account_id)) {
     std::move(callback).Run(/*success=*/false);
@@ -1245,7 +1245,7 @@
 
 void WallpaperControllerImpl::SetGooglePhotosWallpaper(
     const GooglePhotosWallpaperParams& params,
-    WallpaperController::SetGooglePhotosWallpaperCallback callback) {
+    WallpaperController::SetWallpaperCallback callback) {
   if (!features::IsWallpaperGooglePhotosIntegrationEnabled()) {
     std::move(callback).Run(false);
     return;
@@ -2068,7 +2068,7 @@
 }
 
 void WallpaperControllerImpl::SetOnlineWallpaperFromPath(
-    SetOnlineWallpaperCallback callback,
+    SetWallpaperCallback callback,
     const OnlineWallpaperParams& params,
     const base::FilePath& file_path) {
   bool file_exists = !file_path.empty();
@@ -2085,7 +2085,7 @@
 }
 
 void WallpaperControllerImpl::SetOnlineWallpaperFromVariantPaths(
-    SetOnlineWallpaperCallback callback,
+    SetWallpaperCallback callback,
     const OnlineWallpaperParams& params,
     const base::flat_map<std::string, base::FilePath>& url_to_file_path_map) {
   if (url_to_file_path_map.empty()) {
@@ -2103,7 +2103,7 @@
 void WallpaperControllerImpl::OnOnlineWallpaperDecoded(
     const OnlineWallpaperParams& params,
     bool save_file,
-    SetOnlineWallpaperCallback callback,
+    SetWallpaperCallback callback,
     const gfx::ImageSkia& image) {
   bool success = !image.isNull();
   if (callback)
@@ -2158,7 +2158,7 @@
 
 void WallpaperControllerImpl::OnGooglePhotosMetadataFetched(
     const GooglePhotosWallpaperParams& params,
-    SetGooglePhotosWallpaperCallback callback,
+    SetWallpaperCallback callback,
     const std::string& metadata) {
   // TODO(angusmclean): Verify that the image is still valid/not deleted, then
   // check the cache for it, only downloading if necessary.
@@ -2175,7 +2175,7 @@
 
 void WallpaperControllerImpl::OnGooglePhotosWallpaperDownloaded(
     const GooglePhotosWallpaperParams& params,
-    SetGooglePhotosWallpaperCallback callback,
+    SetWallpaperCallback callback,
     const gfx::ImageSkia& image) {
   // TODO(angusmclean): Replace this DCHECK with actual logic to handle an image
   // coming back empty.
@@ -2372,7 +2372,7 @@
     const base::FilePath& path,
     WallpaperLayout layout,
     bool preview_mode,
-    SetCustomWallpaperCallback callback,
+    SetWallpaperCallback callback,
     const gfx::ImageSkia& image) {
   bool success = !image.isNull();
   // Run callback before finishing setting the image. This is the same timing of
@@ -2674,7 +2674,7 @@
 
 void WallpaperControllerImpl::OnAttemptSetOnlineWallpaper(
     const OnlineWallpaperParams& params,
-    SetOnlineWallpaperCallback callback,
+    SetWallpaperCallback callback,
     bool success) {
   if (success) {
     std::move(callback).Run(true);
@@ -2739,7 +2739,7 @@
 
 void WallpaperControllerImpl::OnAllOnlineWallpaperVariantsDownloaded(
     const OnlineWallpaperParams& params,
-    SetOnlineWallpaperCallback callback) {
+    SetWallpaperCallback callback) {
   bool success = url_to_image_map_.size() == params.variants.size() &&
                  !url_to_image_map_.at(params.url.spec()).isNull();
   if (!success) {
diff --git a/ash/wallpaper/wallpaper_controller_impl.h b/ash/wallpaper/wallpaper_controller_impl.h
index cf0be88..ee289dd 100644
--- a/ash/wallpaper/wallpaper_controller_impl.h
+++ b/ash/wallpaper/wallpaper_controller_impl.h
@@ -255,22 +255,21 @@
                           const base::FilePath& file_path,
                           WallpaperLayout layout,
                           bool preview_mode,
-                          SetCustomWallpaperCallback callback) override;
+                          SetWallpaperCallback callback) override;
   void SetCustomWallpaper(const AccountId& account_id,
                           const std::string& file_name,
                           WallpaperLayout layout,
                           const gfx::ImageSkia& image,
                           bool preview_mode) override;
   void SetOnlineWallpaper(const OnlineWallpaperParams& params,
-                          SetOnlineWallpaperCallback callback) override;
+                          SetWallpaperCallback callback) override;
   void SetOnlineWallpaperIfExists(const OnlineWallpaperParams& params,
-                                  SetOnlineWallpaperCallback callback) override;
+                                  SetWallpaperCallback callback) override;
   void SetOnlineWallpaperFromData(const OnlineWallpaperParams& params,
                                   const std::string& image_data,
-                                  SetOnlineWallpaperCallback callback) override;
-  void SetGooglePhotosWallpaper(
-      const GooglePhotosWallpaperParams& params,
-      SetGooglePhotosWallpaperCallback callback) override;
+                                  SetWallpaperCallback callback) override;
+  void SetGooglePhotosWallpaper(const GooglePhotosWallpaperParams& params,
+                                SetWallpaperCallback callback) override;
   void SetDefaultWallpaper(const AccountId& account_id,
                            bool show_wallpaper) override;
   void SetCustomizedDefaultWallpaperPaths(
@@ -450,14 +449,14 @@
   // Used as the callback of checking `WallpaperType::kOnline` wallpaper
   // existence in `SetOnlineWallpaperIfExists`. Initiates reading and decoding
   // the wallpaper if `file_path` is not empty.
-  void SetOnlineWallpaperFromPath(SetOnlineWallpaperCallback callback,
+  void SetOnlineWallpaperFromPath(SetWallpaperCallback callback,
                                   const OnlineWallpaperParams& params,
                                   const base::FilePath& file_path);
 
   // Used as the callback of checking that all the wallpaper variants' paths
   // exist. If they do, set the online wallpaper from the given |params.url|.
   void SetOnlineWallpaperFromVariantPaths(
-      SetOnlineWallpaperCallback callback,
+      SetWallpaperCallback callback,
       const OnlineWallpaperParams& params,
       const base::flat_map<std::string, base::FilePath>& url_to_file_path_map);
 
@@ -467,13 +466,13 @@
   // active user.
   void OnOnlineWallpaperDecoded(const OnlineWallpaperParams& params,
                                 bool save_file,
-                                SetOnlineWallpaperCallback callback,
+                                SetWallpaperCallback callback,
                                 const gfx::ImageSkia& image);
 
   // Used as the callback of fetching the metadata for a Google Photos photo
   // from the unique id. Currently metadata is simply the image URL (stubbed).
   void OnGooglePhotosMetadataFetched(const GooglePhotosWallpaperParams& params,
-                                     SetGooglePhotosWallpaperCallback callback,
+                                     SetWallpaperCallback callback,
                                      const std::string& metadata);
 
   // Used as the callback of downloading wallpapers of type
@@ -481,7 +480,7 @@
   // `params.account_id` is the active user.
   void OnGooglePhotosWallpaperDownloaded(
       const GooglePhotosWallpaperParams& params,
-      SetGooglePhotosWallpaperCallback callback,
+      SetWallpaperCallback callback,
       const gfx::ImageSkia& image);
 
   // Implementation of |SetOnlineWallpaper|. Shows the wallpaper on screen if
@@ -539,7 +538,7 @@
                                 const base::FilePath& path,
                                 WallpaperLayout layout,
                                 bool preview_mode,
-                                SetCustomWallpaperCallback callback,
+                                SetWallpaperCallback callback,
                                 const gfx::ImageSkia& image);
 
   // Used as the callback of wallpaper decoding. (Wallpapers of type
@@ -632,7 +631,7 @@
   void HandleWallpaperInfoSyncedIn(const AccountId& account_id,
                                    WallpaperInfo info);
   void OnAttemptSetOnlineWallpaper(const OnlineWallpaperParams& params,
-                                   SetOnlineWallpaperCallback callback,
+                                   SetWallpaperCallback callback,
                                    bool success);
 
   // Save the downloaded |params.variants| at |current_index|.
@@ -645,7 +644,7 @@
   // from |params.url|.
   void OnAllOnlineWallpaperVariantsDownloaded(
       const OnlineWallpaperParams& params,
-      SetOnlineWallpaperCallback callback);
+      SetWallpaperCallback callback);
 
   constexpr bool IsWallpaperTypeSyncable(WallpaperType type);
 
diff --git a/ash/wallpaper/wallpaper_controller_unittest.cc b/ash/wallpaper/wallpaper_controller_unittest.cc
index 64f51399..e4cddc13 100644
--- a/ash/wallpaper/wallpaper_controller_unittest.cc
+++ b/ash/wallpaper/wallpaper_controller_unittest.cc
@@ -627,7 +627,7 @@
       bool from_user,
       const absl::optional<uint64_t>& unit_id,
       const std::vector<OnlineWallpaperVariant>& variants,
-      WallpaperControllerImpl::SetOnlineWallpaperCallback callback) {
+      WallpaperController::SetWallpaperCallback callback) {
     const OnlineWallpaperParams params = {
         account_id, asset_id,     GURL(url), collection_id,
         layout,     preview_mode, from_user, /*daily_refresh_enabled=*/false,
@@ -690,7 +690,7 @@
             /*daily_refresh_enabled=*/false, /*unit_id=*/absl::nullopt,
             /*variants=*/std::vector<OnlineWallpaperVariant>()),
         /*image_data=*/std::string(),
-        WallpaperControllerImpl::SetOnlineWallpaperCallback());
+        WallpaperController::SetWallpaperCallback());
     RunAllTasksUntilIdle();
 
     // Change the on-screen wallpaper to a different one. (Otherwise the
@@ -1099,7 +1099,7 @@
   controller_->SetOnlineWallpaperFromData(
       params,
       /*image_data=*/std::string(),
-      WallpaperControllerImpl::SetOnlineWallpaperCallback());
+      WallpaperController::SetWallpaperCallback());
   RunAllTasksUntilIdle();
   EXPECT_EQ(1, GetWallpaperCount());
   EXPECT_EQ(controller_->GetWallpaperType(), WallpaperType::kOnline);
@@ -1159,7 +1159,7 @@
           /*daily_refresh_enabled=*/false, kUnitId,
           /*variants=*/std::vector<OnlineWallpaperVariant>()),
       /*image_data=*/std::string(),
-      WallpaperControllerImpl::SetOnlineWallpaperCallback());
+      WallpaperController::SetWallpaperCallback());
   RunAllTasksUntilIdle();
 
   // Verify that the wallpaper with |url| is available offline, and the returned
@@ -1191,7 +1191,7 @@
           /*daily_refresh_enabled=*/false, /*unit_id=*/absl::nullopt,
           /*variants=*/std::vector<OnlineWallpaperVariant>()),
       /*image_data=*/std::string(),
-      WallpaperControllerImpl::SetOnlineWallpaperCallback());
+      WallpaperController::SetWallpaperCallback());
   RunAllTasksUntilIdle();
   // Verify that the user wallpaper info is updated.
   EXPECT_TRUE(controller_->GetUserWallpaperInfo(account_id_1, &wallpaper_info));
@@ -1213,7 +1213,7 @@
           /*daily_refresh_enabled=*/false, /*unit_id=*/absl::nullopt,
           /*variants=*/std::vector<OnlineWallpaperVariant>()),
       /*image_data=*/std::string(),
-      WallpaperControllerImpl::SetOnlineWallpaperCallback());
+      WallpaperController::SetWallpaperCallback());
   RunAllTasksUntilIdle();
   EXPECT_EQ(0, GetWallpaperCount());
   EXPECT_TRUE(controller_->GetUserWallpaperInfo(account_id_1, &wallpaper_info));
@@ -1852,7 +1852,7 @@
           /*daily_refresh_enabled=*/false, kUnitId,
           /*variants=*/std::vector<OnlineWallpaperVariant>()),
       /*image_data=*/std::string(),
-      WallpaperControllerImpl::SetOnlineWallpaperCallback());
+      WallpaperController::SetWallpaperCallback());
   RunAllTasksUntilIdle();
   EXPECT_TRUE(
       controller_->GetWallpaperFromCache(account_id_1, &cached_wallpaper));
@@ -2089,7 +2089,7 @@
           /*daily_refresh_enabled=*/false, /*unit_id=*/absl::nullopt,
           /*variants=*/std::vector<OnlineWallpaperVariant>()),
       /*image_data=*/std::string(),
-      WallpaperControllerImpl::SetOnlineWallpaperCallback());
+      WallpaperController::SetWallpaperCallback());
   RunAllTasksUntilIdle();
   EXPECT_EQ(1, GetWallpaperCount());
   EXPECT_EQ(controller_->GetWallpaperType(), WallpaperType::kOnline);
@@ -2746,7 +2746,7 @@
       layout,
       /*save_file=*/false, /*preview_mode=*/true, /*from_user=*/true, kUnitId,
       /*variants=*/std::vector<OnlineWallpaperVariant>(),
-      WallpaperControllerImpl::SetOnlineWallpaperCallback());
+      WallpaperController::SetWallpaperCallback());
   RunAllTasksUntilIdle();
   EXPECT_EQ(1, GetWallpaperCount());
   EXPECT_EQ(online_wallpaper_color, GetWallpaperColor());
@@ -2849,7 +2849,7 @@
       layout,
       /*save_file=*/false, /*preview_mode=*/true, /*from_user=*/true, kUnitId,
       /*variants=*/std::vector<OnlineWallpaperVariant>(),
-      WallpaperControllerImpl::SetOnlineWallpaperCallback());
+      WallpaperController::SetWallpaperCallback());
   RunAllTasksUntilIdle();
   EXPECT_EQ(1, GetWallpaperCount());
   EXPECT_EQ(kWallpaperColor, GetWallpaperColor());
@@ -2869,7 +2869,7 @@
       kDummyCollectionId, layout, /*save_file=*/false, /*preview_mode=*/false,
       /*from_user=*/true, kUnitId,
       /*variants=*/std::vector<OnlineWallpaperVariant>(),
-      WallpaperControllerImpl::SetOnlineWallpaperCallback());
+      WallpaperController::SetWallpaperCallback());
   RunAllTasksUntilIdle();
   EXPECT_EQ(0, GetWallpaperCount());
   EXPECT_EQ(kWallpaperColor, GetWallpaperColor());
@@ -4023,7 +4023,7 @@
   controller_->SetOnlineWallpaperFromData(
       params,
       /*image_data=*/std::string(),
-      WallpaperControllerImpl::SetOnlineWallpaperCallback());
+      WallpaperController::SetWallpaperCallback());
   RunAllTasksUntilIdle();
   EXPECT_EQ(1, GetWallpaperCount());
   EXPECT_EQ(controller_->GetWallpaperType(), WallpaperType::kOnline);
diff --git a/ash/wm/desks/templates/desks_templates_item_view.cc b/ash/wm/desks/templates/desks_templates_item_view.cc
index 9b81f5f..8d4b158 100644
--- a/ash/wm/desks/templates/desks_templates_item_view.cc
+++ b/ash/wm/desks/templates/desks_templates_item_view.cc
@@ -15,6 +15,7 @@
 #include "ash/style/close_button.h"
 #include "ash/style/pill_button.h"
 #include "ash/style/style_util.h"
+#include "ash/wm/desks/desk.h"
 #include "ash/wm/desks/desks_textfield.h"
 #include "ash/wm/desks/templates/desks_templates_dialog_controller.h"
 #include "ash/wm/desks/templates/desks_templates_grid_view.h"
@@ -255,6 +256,21 @@
   return name_view_->HasFocus();
 }
 
+void DesksTemplatesItemView::MaybeRemoveNameNumber() {
+  // When there are existing matched Desk name and Template name (ie.
+  // "Desk 1"), creating a new template from "Desk 1" will get auto generated
+  // template name from backend as "Desk 1 (1)", to prevent template
+  // duplication, we show the template view name to be "Desk 1" by removing name
+  // number, save template under such name will call out template replace
+  // dialog.
+  if (FindOtherTemplateWithName(
+          DesksController::Get()->active_desk()->name())) {
+    // Replace the name number.
+    name_view_->SetTemporaryName(DesksController::Get()->active_desk()->name());
+    name_view_->SetViewName(DesksController::Get()->active_desk()->name());
+  }
+}
+
 void DesksTemplatesItemView::ReplaceTemplate(const std::string& uuid) {
   // Make sure we delete the template we are replacing first, so that we don't
   // get template name collisions.
@@ -266,7 +282,7 @@
 void DesksTemplatesItemView::RevertTemplateName() {
   views::FocusManager* focus_manager = GetFocusManager();
   focus_manager->SetFocusedView(name_view_);
-  name_view_->SetText(desk_template_->template_name());
+  name_view_->SetViewName(name_view_->GetAccessibleName());
   name_view_->SelectAll(true);
 
   name_view_->OnContentsChanged();
@@ -346,10 +362,6 @@
   hover_container_->SetVisible(false);
   icon_container_view_->SetVisible(true);
 
-  // Set the unelided template name so that the full name shows up for the user
-  // to be able to change it.
-  name_view_->SetText(desk_template_->template_name());
-
   // Set the Overview highlight to move focus with the `name_view_`.
   auto* highlight_controller = Shell::Get()
                                    ->overview_controller()
@@ -387,6 +399,9 @@
   defer_select_all_ = false;
   name_view_->UpdateViewAppearance();
 
+  if (MaybeShowReplaceDialog())
+    return;
+
   // Collapse the whitespace for the text first before comparing it or trying to
   // commit the name in order to prevent duplicate name issues.
   const std::u16string user_entered_name =
@@ -404,34 +419,27 @@
     return;
   }
 
+  UpdateTemplateName();
+}
+
+bool DesksTemplatesItemView::MaybeShowReplaceDialog() {
   // Check if template name exist, replace existing template if confirmed by
   // user.
+  const std::u16string new_name = name_view_->GetText();
+  auto* template_item = FindOtherTemplateWithName(new_name);
+  if (!template_item)
+    return false;
+  // Show replace template dialog. If accepted, replace old template and commit
+  // name change.
   aura::Window* root_window = GetWidget()->GetNativeWindow()->GetRootWindow();
-  OverviewGrid* overview_grid = Shell::Get()
-                                    ->overview_controller()
-                                    ->overview_session()
-                                    ->GetGridWithRootWindow(root_window);
-  auto* templates_grid_view = static_cast<DesksTemplatesGridView*>(
-      overview_grid->desks_templates_grid_widget()->GetContentsView());
-  for (DesksTemplatesItemView* template_item :
-       templates_grid_view->grid_items()) {
-    auto new_name = name_view_->GetText();
-    if (template_item != this &&
-        template_item->desk_template_->template_name() == new_name) {
-      // Show replace template dialog.
-      // If accepted, replace old template and commit name change.
-      DesksTemplatesDialogController::Get()->ShowReplaceDialog(
-          root_window, new_name,
-          base::BindOnce(
-              &DesksTemplatesItemView::ReplaceTemplate,
-              weak_ptr_factory_.GetWeakPtr(),
-              template_item->desk_template_->uuid().AsLowercaseString()),
-          base::BindOnce(&DesksTemplatesItemView::RevertTemplateName,
-                         weak_ptr_factory_.GetWeakPtr()));
-      return;
-    }
-  }
-  UpdateTemplateName();
+  DesksTemplatesDialogController::Get()->ShowReplaceDialog(
+      root_window, new_name,
+      base::BindOnce(&DesksTemplatesItemView::ReplaceTemplate,
+                     weak_ptr_factory_.GetWeakPtr(),
+                     template_item->desk_template_->uuid().AsLowercaseString()),
+      base::BindOnce(&DesksTemplatesItemView::RevertTemplateName,
+                     weak_ptr_factory_.GetWeakPtr()));
+  return true;
 }
 
 views::Button::KeyClickAction DesksTemplatesItemView::GetKeyClickActionForEvent(
@@ -558,6 +566,19 @@
   return views::ViewTargeterDelegate::TargetForRect(root, rect);
 }
 
+DesksTemplatesItemView* DesksTemplatesItemView::FindOtherTemplateWithName(
+    const std::u16string& name) const {
+  const auto templates_grid_view_items =
+      static_cast<const DesksTemplatesGridView*>(parent())->grid_items();
+
+  auto iter = std::find_if(
+      templates_grid_view_items.begin(), templates_grid_view_items.end(),
+      [this, name](const DesksTemplatesItemView* d) {
+        return (d != this && d->desk_template()->template_name() == name);
+      });
+  return iter == templates_grid_view_items.end() ? nullptr : *iter;
+}
+
 void DesksTemplatesItemView::OnDeleteTemplate() {
   DesksTemplatesPresenter::Get()->DeleteEntry(
       desk_template_->uuid().AsLowercaseString());
diff --git a/ash/wm/desks/templates/desks_templates_item_view.h b/ash/wm/desks/templates/desks_templates_item_view.h
index d7bbfa0..ac06b6e 100644
--- a/ash/wm/desks/templates/desks_templates_item_view.h
+++ b/ash/wm/desks/templates/desks_templates_item_view.h
@@ -85,6 +85,12 @@
   // `DesksTemplatesNameView` has the focus).
   bool IsTemplateNameBeingModified() const;
 
+  // To prevent duplications when creating template from the same desk, check if
+  // there's a existing template shares the same name as current active desk, if
+  // so, remove auto added number.
+  void MaybeRemoveNameNumber();
+  // Show replace dialog when found a name duplication.
+  bool MaybeShowReplaceDialog();
   // Rename current template with new name, delete old template with same name
   // by uuid. Used for callback functions for Replace Dialog.
   void ReplaceTemplate(const std::string& uuid);
@@ -118,6 +124,11 @@
  private:
   friend class DesksTemplatesItemViewTestApi;
 
+  // Return the duplicated template item if there is a name duplication in saved
+  // templates.
+  DesksTemplatesItemView* FindOtherTemplateWithName(
+      const std::u16string& name) const;
+
   void OnDeleteTemplate();
   void OnDeleteButtonPressed();
 
diff --git a/ash/wm/desks/templates/desks_templates_name_view.cc b/ash/wm/desks/templates/desks_templates_name_view.cc
index d6bd343..f8f9c03 100644
--- a/ash/wm/desks/templates/desks_templates_name_view.cc
+++ b/ash/wm/desks/templates/desks_templates_name_view.cc
@@ -78,6 +78,7 @@
 
 void DesksTemplatesNameView::OnContentsChanged() {
   PreferredSizeChanged();
+  temporary_name_.reset();
 }
 
 gfx::Size DesksTemplatesNameView::CalculatePreferredSize() const {
@@ -95,6 +96,11 @@
   event->StopPropagation();
 }
 
+void DesksTemplatesNameView::SetViewName(const std::u16string& name) {
+  SetText(temporary_name_.value_or(name));
+  PreferredSizeChanged();
+}
+
 int DesksTemplatesNameView::GetAvailableWidth() const {
   auto* parent_view = static_cast<const views::BoxLayoutView*>(parent());
   int available_width = parent_view->width() -
diff --git a/ash/wm/desks/templates/desks_templates_name_view.h b/ash/wm/desks/templates/desks_templates_name_view.h
index 80d1861..ffbf7ac 100644
--- a/ash/wm/desks/templates/desks_templates_name_view.h
+++ b/ash/wm/desks/templates/desks_templates_name_view.h
@@ -5,7 +5,10 @@
 #ifndef ASH_WM_DESKS_TEMPLATES_DESKS_TEMPLATES_NAME_VIEW_H_
 #define ASH_WM_DESKS_TEMPLATES_DESKS_TEMPLATES_NAME_VIEW_H_
 
+#include <string>
+
 #include "ash/wm/desks/desks_textfield.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "ui/base/metadata/metadata_header_macros.h"
 
 namespace ash {
@@ -28,6 +31,11 @@
   // grid widget.
   static void CommitChanges(views::Widget* widget);
 
+  void SetViewName(const std::u16string& name);
+  void SetTemporaryName(const std::u16string& new_name) {
+    temporary_name_ = new_name;
+  }
+
   // Called when the contents in the textfield change. Updates the preferred
   // size of `this`, which invalidates the layout.
   void OnContentsChanged();
@@ -41,6 +49,9 @@
   // based on the parent's and visible sibling's preferred sizes. Will always
   // return a value greater than or equal to one.
   int GetAvailableWidth() const;
+
+  // Store the modified text view name if name nudge is removed.
+  absl::optional<std::u16string> temporary_name_;
 };
 
 BEGIN_VIEW_BUILDER(/* no export */, DesksTemplatesNameView, DesksTextfield)
diff --git a/ash/wm/desks/templates/desks_templates_presenter.cc b/ash/wm/desks/templates/desks_templates_presenter.cc
index 6493d2e4..3cd2471 100644
--- a/ash/wm/desks/templates/desks_templates_presenter.cc
+++ b/ash/wm/desks/templates/desks_templates_presenter.cc
@@ -251,6 +251,8 @@
             // name view.
             DCHECK(!item_view->name_view()->GetReadOnly());
             item_view->name_view()->RequestFocus();
+            // Check if name nudge needs to be removed.
+            item_view->MaybeRemoveNameNumber();
             cached_saved_template_uuid_.reset();
             break;
           }
diff --git a/ash/wm/desks/templates/desks_templates_unittest.cc b/ash/wm/desks/templates/desks_templates_unittest.cc
index 26ea337a..abb0e35 100644
--- a/ash/wm/desks/templates/desks_templates_unittest.cc
+++ b/ash/wm/desks/templates/desks_templates_unittest.cc
@@ -2782,4 +2782,60 @@
   EXPECT_TRUE(GetOverviewItemForWindow(window.get()));
 }
 
+// Test save same desk as template won't create name with number on the template
+// view for the second template.
+TEST_F(DesksTemplatesTest, NoDuplicateDisplayedName) {
+  // There are no saved template entries and one test window initially.
+  auto test_window = CreateAppWindow();
+  ToggleOverview();
+  WaitForDesksTemplatesUI();
+
+  // The `save_desk_as_template_widget` is visible when at least one window is
+  // open.
+  views::Widget* save_desk_as_template_widget =
+      GetSaveDeskAsTemplateButtonForRoot(Shell::GetPrimaryRootWindow());
+  ASSERT_TRUE(save_desk_as_template_widget);
+  EXPECT_TRUE(save_desk_as_template_widget->GetContentsView()->GetVisible());
+
+  // Click on `save_desk_as_template_widget` button.
+  ClickOnView(save_desk_as_template_widget->GetContentsView());
+  ASSERT_EQ(1ul, GetAllEntries().size());
+  WaitForDesksTemplatesUI();
+  ASSERT_EQ(u"Desk 1", DesksController::Get()->active_desk()->name());
+  EXPECT_EQ(u"Desk 1", GetItemViewFromTemplatesGrid(0)->name_view()->GetText());
+  // The new template name still have name nudge to maintain it's uniqueness.
+  EXPECT_EQ(u"Desk 1", GetAllEntries().back()->template_name());
+
+  // Exit overview and save the same desk again.
+  ToggleOverview();
+  ASSERT_FALSE(InOverviewSession());
+  ToggleOverview();
+  WaitForDesksTemplatesUI();
+
+  save_desk_as_template_widget =
+      GetSaveDeskAsTemplateButtonForRoot(Shell::GetPrimaryRootWindow());
+  ASSERT_TRUE(save_desk_as_template_widget);
+  EXPECT_TRUE(save_desk_as_template_widget->GetContentsView()->GetVisible());
+
+  // Click on `save_desk_as_template_widget` button. At this point the template
+  // name matches the desk name.
+  ClickOnView(save_desk_as_template_widget->GetContentsView());
+  ASSERT_EQ(2ul, GetAllEntries().size());
+  WaitForDesksTemplatesUI();
+  // Newly created template name_view.
+  DesksTemplatesNameView* name_view =
+      GetItemViewFromTemplatesGrid(1)->name_view();
+  EXPECT_TRUE(name_view->HasFocus());
+  OverviewGrid* overview_grid = GetOverviewGridList()[0].get();
+  DeskNameView* desk_name_view =
+      overview_grid->desks_bar_view()->mini_views().back()->desk_name_view();
+  // Check newly created template doesn't have name nudge.
+  EXPECT_EQ(desk_name_view->GetText(), name_view->GetText());
+  ASSERT_EQ(u"Desk 1", DesksController::Get()->active_desk()->name());
+  EXPECT_EQ(u"Desk 1", name_view->GetText());
+  // The new template name still have name nudge to maintain it's uniqueness.
+  EXPECT_EQ(u"Desk 1 (1)",
+            GetItemViewFromTemplatesGrid(1)->desk_template()->template_name());
+}
+
 }  // namespace ash
diff --git a/base/allocator/partition_allocator/partition_root.h b/base/allocator/partition_allocator/partition_root.h
index afc122b..6e71c47 100644
--- a/base/allocator/partition_allocator/partition_root.h
+++ b/base/allocator/partition_allocator/partition_root.h
@@ -61,6 +61,7 @@
 #include "base/allocator/partition_allocator/starscan/state_bitmap.h"
 #include "base/allocator/partition_allocator/tagging.h"
 #include "base/allocator/partition_allocator/thread_cache.h"
+#include "base/bits.h"
 #include "base/compiler_specific.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
diff --git a/base/allocator/partition_allocator/spinning_mutex.cc b/base/allocator/partition_allocator/spinning_mutex.cc
index 7091fa0..8a1b9a4 100644
--- a/base/allocator/partition_allocator/spinning_mutex.cc
+++ b/base/allocator/partition_allocator/spinning_mutex.cc
@@ -59,6 +59,37 @@
 #endif  // BUILDFLAG(IS_APPLE)
 }
 
+void SpinningMutex::AcquireSpinThenBlock() {
+  int tries = 0;
+  int backoff = 1;
+  do {
+    if (LIKELY(Try()))
+      return;
+    // Note: Per the intel optimization manual
+    // (https://software.intel.com/content/dam/develop/public/us/en/documents/64-ia-32-architectures-optimization-manual.pdf),
+    // the "pause" instruction is more costly on Skylake Client than on previous
+    // architectures. The latency is found to be 141 cycles
+    // there (from ~10 on previous ones, nice 14x).
+    //
+    // According to Agner Fog's instruction tables, the latency is still >100
+    // cycles on Ice Lake, and from other sources, seems to be high as well on
+    // Adler Lake. Separately, it is (from
+    // https://agner.org/optimize/instruction_tables.pdf) also high on AMD Zen 3
+    // (~65). So just assume that it's this way for most x86_64 architectures.
+    //
+    // Also, loop several times here, following the guidelines in section 2.3.4
+    // of the manual, "Pause latency in Skylake Client Microarchitecture".
+    for (int yields = 0; yields < backoff; yields++) {
+      PA_YIELD_PROCESSOR;
+      tries++;
+    }
+    constexpr int kMaxBackoff = 16;
+    backoff = std::min(kMaxBackoff, backoff << 1);
+  } while (tries < kSpinCount);
+
+  LockSlow();
+}
+
 #if defined(PA_HAS_FAST_MUTEX)
 
 #if defined(PA_HAS_LINUX_KERNEL)
diff --git a/base/allocator/partition_allocator/spinning_mutex.h b/base/allocator/partition_allocator/spinning_mutex.h
index 42ca63b..325f379 100644
--- a/base/allocator/partition_allocator/spinning_mutex.h
+++ b/base/allocator/partition_allocator/spinning_mutex.h
@@ -101,6 +101,7 @@
   void Reinit() UNLOCK_FUNCTION();
 
  private:
+  NOINLINE void AcquireSpinThenBlock() EXCLUSIVE_LOCK_FUNCTION();
   void LockSlow() EXCLUSIVE_LOCK_FUNCTION();
 
   // See below, the latency of PA_YIELD_PROCESSOR can be as high as ~150
@@ -151,36 +152,14 @@
 };
 
 ALWAYS_INLINE void SpinningMutex::Acquire() {
-  int tries = 0;
-  int backoff = 1;
-  // Busy-waiting is inlined, which is fine as long as we have few callers. This
-  // is only used for the partition lock, so this is the case.
-  do {
-    if (LIKELY(Try()))
-      return;
-    // Note: Per the intel optimization manual
-    // (https://software.intel.com/content/dam/develop/public/us/en/documents/64-ia-32-architectures-optimization-manual.pdf),
-    // the "pause" instruction is more costly on Skylake Client than on previous
-    // architectures. The latency is found to be 141 cycles
-    // there (from ~10 on previous ones, nice 14x).
-    //
-    // According to Agner Fog's instruction tables, the latency is still >100
-    // cycles on Ice Lake, and from other sources, seems to be high as well on
-    // Adler Lake. Separately, it is (from
-    // https://agner.org/optimize/instruction_tables.pdf) also high on AMD Zen 3
-    // (~65). So just assume that it's this way for most x86_64 architectures.
-    //
-    // Also, loop several times here, following the guidelines in section 2.3.4
-    // of the manual, "Pause latency in Skylake Client Microarchitecture".
-    for (int yields = 0; yields < backoff; yields++) {
-      PA_YIELD_PROCESSOR;
-      tries++;
-    }
-    constexpr int kMaxBackoff = 16;
-    backoff = std::min(kMaxBackoff, backoff << 1);
-  } while (tries < kSpinCount);
+  // Not marked LIKELY(), as:
+  // 1. We don't know how much contention the lock would experience
+  // 2. This may lead to weird-looking code layout when inlined into a caller
+  // with (UN)LIKELY() annotations.
+  if (Try())
+    return;
 
-  LockSlow();
+  return AcquireSpinThenBlock();
 }
 
 inline constexpr SpinningMutex::SpinningMutex() = default;
diff --git a/base/android/jni_generator/golden/testInnerClassNativesBothInnerAndOuterRegistrations.golden b/base/android/jni_generator/golden/testInnerClassNativesBothInnerAndOuterRegistrations.golden
index c4702b3..996c08a 100644
--- a/base/android/jni_generator/golden/testInnerClassNativesBothInnerAndOuterRegistrations.golden
+++ b/base/android/jni_generator/golden/testInnerClassNativesBothInnerAndOuterRegistrations.golden
@@ -12,9 +12,10 @@
 
 #include <jni.h>
 
+#include <iterator>
+
 #include "base/android/jni_generator/jni_generator_helper.h"
 #include "base/android/jni_int_wrapper.h"
-#include "base/cxx17_backports.h"  // For base::size().
 
 
 // Step 1: Forward declarations (classes).
@@ -65,7 +66,7 @@
 
 JNI_REGISTRATION_EXPORT bool RegisterNative_org_chromium_TestJni(JNIEnv* env) {
   const int kMethods_org_chromium_TestJniSize =
-      base::size(kMethods_org_chromium_TestJni);
+      std::size(kMethods_org_chromium_TestJni);
   if (env->RegisterNatives(
       org_chromium_TestJni_clazz(env),
       kMethods_org_chromium_TestJni,
@@ -78,7 +79,7 @@
 
 
   const int kMethods_org_chromium_TestJni_00024MyOtherInnerClassSize =
-      base::size(kMethods_org_chromium_TestJni_00024MyOtherInnerClass);
+      std::size(kMethods_org_chromium_TestJni_00024MyOtherInnerClass);
   if (env->RegisterNatives(
       org_chromium_TestJni_00024MyOtherInnerClass_clazz(env),
       kMethods_org_chromium_TestJni_00024MyOtherInnerClass,
diff --git a/base/android/jni_generator/golden/testNativesRegistrations.golden b/base/android/jni_generator/golden/testNativesRegistrations.golden
index 4e3531e3..3ac19474 100644
--- a/base/android/jni_generator/golden/testNativesRegistrations.golden
+++ b/base/android/jni_generator/golden/testNativesRegistrations.golden
@@ -12,9 +12,10 @@
 
 #include <jni.h>
 
+#include <iterator>
+
 #include "base/android/jni_generator/jni_generator_helper.h"
 #include "base/android/jni_int_wrapper.h"
-#include "base/cxx17_backports.h"  // For base::size().
 
 
 // Step 1: Forward declarations (classes).
@@ -144,7 +145,7 @@
 
 JNI_REGISTRATION_EXPORT bool RegisterNative_org_chromium_TestJni(JNIEnv* env) {
   const int kMethods_org_chromium_TestJniSize =
-      base::size(kMethods_org_chromium_TestJni);
+      std::size(kMethods_org_chromium_TestJni);
   if (env->RegisterNatives(
       org_chromium_TestJni_clazz(env),
       kMethods_org_chromium_TestJni,
diff --git a/base/android/jni_generator/golden/testProxyNativesMainDex.golden b/base/android/jni_generator/golden/testProxyNativesMainDex.golden
index d1f9a9d..8516e86 100644
--- a/base/android/jni_generator/golden/testProxyNativesMainDex.golden
+++ b/base/android/jni_generator/golden/testProxyNativesMainDex.golden
@@ -12,9 +12,10 @@
 
 #include <jni.h>
 
+#include <iterator>
+
 #include "base/android/jni_generator/jni_generator_helper.h"
 #include "base/android/jni_int_wrapper.h"
-#include "base/cxx17_backports.h"  // For base::size().
 
 
 // Step 1: Forward declarations (classes).
@@ -40,7 +41,7 @@
 namespace {
 
 JNI_REGISTRATION_EXPORT bool RegisterNative_org_chromium_base_natives_GEN_1JNIMAIN_DEX(JNIEnv* env) {
-  const int number_of_methods = base::size(kMethods_org_chromium_base_natives_GEN_1JNIMAIN_DEX);
+  const int number_of_methods = std::size(kMethods_org_chromium_base_natives_GEN_1JNIMAIN_DEX);
 
   base::android::ScopedJavaLocalRef<jclass> native_clazz =
       base::android::GetClass(env, "org/chromium/base/natives/GEN_JNI");
diff --git a/base/android/jni_generator/golden/testProxyNativesMainDexAndNonMainDex.golden b/base/android/jni_generator/golden/testProxyNativesMainDexAndNonMainDex.golden
index 0443c781..8595f4d 100644
--- a/base/android/jni_generator/golden/testProxyNativesMainDexAndNonMainDex.golden
+++ b/base/android/jni_generator/golden/testProxyNativesMainDexAndNonMainDex.golden
@@ -12,9 +12,10 @@
 
 #include <jni.h>
 
+#include <iterator>
+
 #include "base/android/jni_generator/jni_generator_helper.h"
 #include "base/android/jni_int_wrapper.h"
-#include "base/cxx17_backports.h"  // For base::size().
 
 
 // Step 1: Forward declarations (classes).
@@ -47,7 +48,7 @@
 namespace {
 
 JNI_REGISTRATION_EXPORT bool RegisterNative_org_chromium_base_natives_GEN_1JNI(JNIEnv* env) {
-  const int number_of_methods = base::size(kMethods_org_chromium_base_natives_GEN_1JNI);
+  const int number_of_methods = std::size(kMethods_org_chromium_base_natives_GEN_1JNI);
 
   base::android::ScopedJavaLocalRef<jclass> native_clazz =
       base::android::GetClass(env, "org/chromium/base/natives/GEN_JNI");
@@ -75,7 +76,7 @@
 namespace {
 
 JNI_REGISTRATION_EXPORT bool RegisterNative_org_chromium_base_natives_GEN_1JNIMAIN_DEX(JNIEnv* env) {
-  const int number_of_methods = base::size(kMethods_org_chromium_base_natives_GEN_1JNIMAIN_DEX);
+  const int number_of_methods = std::size(kMethods_org_chromium_base_natives_GEN_1JNIMAIN_DEX);
 
   base::android::ScopedJavaLocalRef<jclass> native_clazz =
       base::android::GetClass(env, "org/chromium/base/natives/GEN_JNI");
diff --git a/base/android/jni_generator/golden/testProxyNativesRegistrations.golden b/base/android/jni_generator/golden/testProxyNativesRegistrations.golden
index bf1d1a7..d320451 100644
--- a/base/android/jni_generator/golden/testProxyNativesRegistrations.golden
+++ b/base/android/jni_generator/golden/testProxyNativesRegistrations.golden
@@ -12,9 +12,10 @@
 
 #include <jni.h>
 
+#include <iterator>
+
 #include "base/android/jni_generator/jni_generator_helper.h"
 #include "base/android/jni_int_wrapper.h"
-#include "base/cxx17_backports.h"  // For base::size().
 
 
 // Step 1: Forward declarations (classes).
@@ -61,7 +62,7 @@
 namespace {
 
 JNI_REGISTRATION_EXPORT bool RegisterNative_org_chromium_base_natives_GEN_1JNI(JNIEnv* env) {
-  const int number_of_methods = base::size(kMethods_org_chromium_base_natives_GEN_1JNI);
+  const int number_of_methods = std::size(kMethods_org_chromium_base_natives_GEN_1JNI);
 
   base::android::ScopedJavaLocalRef<jclass> native_clazz =
       base::android::GetClass(env, "org/chromium/base/natives/GEN_JNI");
diff --git a/base/android/jni_generator/jni_registration_generator.py b/base/android/jni_generator/jni_registration_generator.py
index 528eada..158d2ad 100755
--- a/base/android/jni_generator/jni_registration_generator.py
+++ b/base/android/jni_generator/jni_registration_generator.py
@@ -137,7 +137,7 @@
 namespace {
 
 JNI_REGISTRATION_EXPORT bool ${REGISTRATION_NAME}(JNIEnv* env) {
-  const int number_of_methods = base::size(kMethods_${ESCAPED_PROXY_CLASS});
+  const int number_of_methods = std::size(kMethods_${ESCAPED_PROXY_CLASS});
 
   base::android::ScopedJavaLocalRef<jclass> native_clazz =
       base::android::GetClass(env, "${PROXY_CLASS}");
@@ -263,9 +263,10 @@
 
 #include <jni.h>
 
+#include <iterator>
+
 #include "base/android/jni_generator/jni_generator_helper.h"
 #include "base/android/jni_int_wrapper.h"
-#include "base/cxx17_backports.h"  // For base::size().
 
 
 // Step 1: Forward declarations (classes).
@@ -517,7 +518,7 @@
     """Returns the shared implementation for RegisterNatives."""
     template = string.Template("""\
   const int kMethods_${JAVA_CLASS}Size =
-      base::size(${NAMESPACE}kMethods_${JAVA_CLASS});
+      std::size(${NAMESPACE}kMethods_${JAVA_CLASS});
   if (env->RegisterNatives(
       ${JAVA_CLASS}_clazz(env),
       ${NAMESPACE}kMethods_${JAVA_CLASS},
diff --git a/base/system/sys_info.h b/base/system/sys_info.h
index 304134f..87d6931 100644
--- a/base/system/sys_info.h
+++ b/base/system/sys_info.h
@@ -24,7 +24,6 @@
 FORWARD_DECLARE_TEST(SystemMetricsTest, ParseMeminfo);
 }
 
-class CommandLine;
 class FilePath;
 struct SystemMemoryInfoKB;
 
@@ -69,21 +68,6 @@
   // on failure.
   static int64_t AmountOfTotalDiskSpace(const FilePath& path);
 
-#if BUILDFLAG(IS_CHROMEOS)
-  // On ChromeOS, spaced is the central source-of-truth for disk space
-  // information. Spaced takes into account the available extents on the
-  // underlying thinpool to make sure that thinly provisioned filesystems
-  // return only the available physical extents as the free space.
-  //
-  // Return the available disk space in bytes on the volume containing |path|,
-  // or -1 on failure.
-  static int64_t GetFreeDiskSpaceFromSpaced(const FilePath& path);
-
-  // Return the total disk space in bytes on the volume containing |path|, or -1
-  // on failure.
-  static int64_t GetTotalDiskSpaceFromSpaced(const FilePath& path);
-#endif
-
 #if BUILDFLAG(IS_FUCHSIA)
   // Sets the total amount of disk space to report under the specified |path|.
   // If |bytes| is -ve then any existing entry for |path| is removed.
@@ -185,14 +169,6 @@
   // Undoes the function above.
   static void ResetChromeOSVersionInfoForTest();
 
-  // Overrides the command runner for running commands. Overrides cannot be
-  // nested. Users must call SetChromeOSGetAppOutputForTest(nullptr) to revert
-  // the test function.
-  using GetAppOutputCallback =
-      RepeatingCallback<bool(const CommandLine&, std::string*)>;
-
-  static void SetChromeOSGetAppOutputForTest(GetAppOutputCallback* callback);
-
   // Returns the kernel version of the host operating system.
   static std::string KernelVersion();
 
diff --git a/base/system/sys_info_chromeos.cc b/base/system/sys_info_chromeos.cc
index 5c1cfa87..188552e 100644
--- a/base/system/sys_info_chromeos.cc
+++ b/base/system/sys_info_chromeos.cc
@@ -8,8 +8,6 @@
 #include <stdint.h>
 #include <sys/utsname.h>
 
-#include "base/callback.h"
-#include "base/command_line.h"
 #include "base/cxx17_backports.h"
 #include "base/environment.h"
 #include "base/files/file.h"
@@ -17,7 +15,6 @@
 #include "base/files/file_util.h"
 #include "base/no_destructor.h"
 #include "base/notreached.h"
-#include "base/process/launch.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_piece.h"
 #include "base/strings/string_split.h"
@@ -49,10 +46,6 @@
 const char kLsbReleaseSourceEnv[] = "env";
 const char kLsbReleaseSourceFile[] = "file";
 
-const char kSpacedCliPath[] = "/usr/sbin/spaced_cli";
-const char kSpacedGetFreeDiskSpaceAction[] = "get_free_disk_space";
-const char kSpacedGetTotalDiskSpaceAction[] = "get_total_disk_space";
-
 class ChromeOSVersionInfo {
  public:
   ChromeOSVersionInfo() {
@@ -175,28 +168,6 @@
   return *version_info;
 }
 
-SysInfo::GetAppOutputCallback* g_chromeos_get_app_output_for_test = nullptr;
-
-int64_t GetInfoFromSpaced(StringPiece action, const base::FilePath& path) {
-  CommandLine command((base::FilePath(kSpacedCliPath)));
-  command.AppendSwitchPath(action, path);
-
-  std::string output;
-  bool ret;
-  if (g_chromeos_get_app_output_for_test) {
-    ret = g_chromeos_get_app_output_for_test->Run(command, &output);
-  } else {
-    ret = GetAppOutput(command, &output);
-  }
-
-  int64_t result;
-  if (!ret || !StringToInt64(output, &result) || result < 0) {
-    return -1;
-  }
-
-  return result;
-}
-
 }  // namespace
 
 // static
@@ -278,11 +249,6 @@
 }
 
 // static
-void SysInfo::SetChromeOSGetAppOutputForTest(GetAppOutputCallback* callback) {
-  g_chromeos_get_app_output_for_test = callback;
-}
-
-// static
 void SysInfo::CrashIfChromeOSNonTestImage() {
   if (!IsRunningOnChromeOS())
     return;
@@ -299,14 +265,4 @@
   CHECK_NE(track.find(kTestImageRelease), std::string::npos);
 }
 
-// static
-int64_t SysInfo::GetFreeDiskSpaceFromSpaced(const base::FilePath& path) {
-  return GetInfoFromSpaced(kSpacedGetFreeDiskSpaceAction, path);
-}
-
-// static
-int64_t SysInfo::GetTotalDiskSpaceFromSpaced(const base::FilePath& path) {
-  return GetInfoFromSpaced(kSpacedGetTotalDiskSpaceAction, path);
-}
-
 }  // namespace base
diff --git a/base/system/sys_info_posix.cc b/base/system/sys_info_posix.cc
index de2d323..4eff878 100644
--- a/base/system/sys_info_posix.cc
+++ b/base/system/sys_info_posix.cc
@@ -158,16 +158,10 @@
 int64_t SysInfo::AmountOfFreeDiskSpace(const FilePath& path) {
   base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
                                                 base::BlockingType::MAY_BLOCK);
-#if BUILDFLAG(IS_CHROMEOS)
-  int64_t ret = GetFreeDiskSpaceFromSpaced(path);
-  if (ret != -1)
-    return ret;
-#endif
 
   int64_t available;
   if (!GetDiskSpaceInfo(path, &available, nullptr))
     return -1;
-
   return available;
 }
 
@@ -176,16 +170,9 @@
   base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
                                                 base::BlockingType::MAY_BLOCK);
 
-#if BUILDFLAG(IS_CHROMEOS)
-  int64_t ret = GetTotalDiskSpaceFromSpaced(path);
-  if (ret != -1)
-    return ret;
-#endif
-
   int64_t total;
   if (!GetDiskSpaceInfo(path, nullptr, &total))
     return -1;
-
   return total;
 }
 
diff --git a/base/system/sys_info_unittest.cc b/base/system/sys_info_unittest.cc
index 160e46f4..9ee252e 100644
--- a/base/system/sys_info_unittest.cc
+++ b/base/system/sys_info_unittest.cc
@@ -429,54 +429,6 @@
   EXPECT_EQ(was_running, SysInfo::IsRunningOnChromeOS());
 }
 
-SysInfo::GetAppOutputCallback MockGetAppOutputTestCallback(
-    const std::string& mock_output,
-    bool mock_ret) {
-  return BindRepeating(
-      [](const std::string& expected_output, bool return_value,
-         const CommandLine& cl, std::string* out) -> bool {
-        *out = expected_output;
-        return return_value;
-      },
-      mock_output, mock_ret);
-}
-
-TEST_F(SysInfoTest, SpacedValidQuery) {
-  FilePath dummy_path("/a/b/c");
-  auto mock_get_app_output = MockGetAppOutputTestCallback("1234", true);
-  SysInfo::SetChromeOSGetAppOutputForTest(&mock_get_app_output);
-  EXPECT_EQ(SysInfo::GetTotalDiskSpaceFromSpaced(dummy_path), 1234);
-  EXPECT_EQ(SysInfo::GetFreeDiskSpaceFromSpaced(dummy_path), 1234);
-  SysInfo::SetChromeOSGetAppOutputForTest(nullptr);
-}
-
-TEST_F(SysInfoTest, SpacedInternalFailure) {
-  FilePath dummy_path("/a/b/c");
-  auto mock_get_app_output = MockGetAppOutputTestCallback("-1", true);
-  SysInfo::SetChromeOSGetAppOutputForTest(&mock_get_app_output);
-  EXPECT_EQ(SysInfo::GetTotalDiskSpaceFromSpaced(dummy_path), -1);
-  EXPECT_EQ(SysInfo::GetFreeDiskSpaceFromSpaced(dummy_path), -1);
-  SysInfo::SetChromeOSGetAppOutputForTest(nullptr);
-}
-
-TEST_F(SysInfoTest, SpacedFailedInvocation) {
-  FilePath dummy_path("/a/b/c");
-  auto mock_get_app_output = MockGetAppOutputTestCallback("5", false);
-  SysInfo::SetChromeOSGetAppOutputForTest(&mock_get_app_output);
-  EXPECT_EQ(SysInfo::GetTotalDiskSpaceFromSpaced(dummy_path), -1);
-  EXPECT_EQ(SysInfo::GetFreeDiskSpaceFromSpaced(dummy_path), -1);
-  SysInfo::SetChromeOSGetAppOutputForTest(nullptr);
-}
-
-TEST_F(SysInfoTest, SpacedInvalidOutput) {
-  FilePath dummy_path("/a/b/c");
-  auto mock_get_app_output = MockGetAppOutputTestCallback("foo", true);
-  SysInfo::SetChromeOSGetAppOutputForTest(&mock_get_app_output);
-  EXPECT_EQ(SysInfo::GetTotalDiskSpaceFromSpaced(dummy_path), -1);
-  EXPECT_EQ(SysInfo::GetFreeDiskSpaceFromSpaced(dummy_path), -1);
-  SysInfo::SetChromeOSGetAppOutputForTest(nullptr);
-}
-
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
 
 }  // namespace base
diff --git a/build/rust/mixed_shared_library.gni b/build/rust/mixed_shared_library.gni
new file mode 100644
index 0000000..dc45d54
--- /dev/null
+++ b/build/rust/mixed_shared_library.gni
@@ -0,0 +1,29 @@
+# Copyright 2022 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/rust.gni")
+import("//build/rust/mixed_target.gni")
+
+# Defines a shared_library containing both Rust and C++ code.
+# See mixed_target.gni for documentation.
+#
+# Note that there is not currently any way for other Rust code
+# to depend on Rust APIs exposed by this shared_library. We simply haven't
+# set up the infrastructure to enable Rust symbols to be exported by
+# a shared library yet. This likely requires a fix to:
+# https://github.com/rust-lang/rust/issues/73295
+
+template("mixed_shared_library") {
+  mixed_target(target_name) {
+    target_type = "shared_library"
+    forward_variables_from(invoker,
+                           "*",
+                           TESTONLY_AND_VISIBILITY + [ "rs_visibility" ])
+    forward_variables_from(invoker, TESTONLY_AND_VISIBILITY)
+  }
+}
+
+set_defaults("mixed_shared_library") {
+  configs = default_shared_library_configs
+}
diff --git a/build/rust/mixed_target.gni b/build/rust/mixed_target.gni
index 20b96810..e9d936e6 100644
--- a/build/rust/mixed_target.gni
+++ b/build/rust/mixed_target.gni
@@ -45,6 +45,7 @@
 #   rs_test_deps
 #   rs_skip_unit_tests
 #   rs_unit_test_target
+#   rs_crate_name
 #   rs_crate_root
 #   rs_features
 #   rs_cxx_bindings
@@ -88,6 +89,7 @@
     "rs_sources",
     "rs_features",
     "rs_cxx_bindings",
+    "rs_crate_name",
     "rs_crate_root",
     "rs_skip_unit_tests",
     "rs_unit_test_target",
@@ -135,6 +137,9 @@
       if (defined(invoker.rs_crate_root)) {
         crate_root = invoker.rs_crate_root
       }
+      if (defined(invoker.rs_crate_name)) {
+        crate_name = invoker.rs_crate_name
+      }
       if (defined(invoker.rs_skip_unit_tests)) {
         skip_unit_tests = invoker.rs_skip_unit_tests
       }
diff --git a/build/rust/rust_target.gni b/build/rust/rust_target.gni
index 203ff059..988e3e0 100644
--- a/build/rust/rust_target.gni
+++ b/build/rust/rust_target.gni
@@ -47,7 +47,6 @@
   } else {
     _crate_root = "src/lib.rs"
   }
-  crate_root = _crate_root
 
   _rustflags = []
   if (defined(invoker.rustflags)) {
@@ -76,11 +75,18 @@
       _configs += invoker.library_configs
     }
   }
+  _forward_to_host_toolchain = false
   if (invoker.target_type == "rust_proc_macro") {
+    # TODO(crbug.com/gn/104): GN rust_proc_macro targets are missing this
+    # command line flag, for the proc_macro crate which is provided by rustc for
+    # compiling proc-macros.
     _rustflags += [
       "--extern",
       "proc_macro",
     ]
+
+    _forward_to_host_toolchain = true
+    _forward_to_target_name = "${target_name}_proc_macro"
   }
 
   _deps_for_rust_targets = []
@@ -174,49 +180,88 @@
     assert(main_target_suffix == "")
   }
 
-  target(invoker.target_type, "${target_name}${main_target_suffix}") {
-    crate_name = _crate_name
-    crate_root = _crate_root
-    configs = []
-    configs = _configs
-    deps = _deps
-    if (defined(_cxx_bindings)) {
-      deps += [ "//build/rust:cxx_rustdeps" ]
-    }
-    rustflags = _rustflags
-    rustflags += [ string_join("",
-                               [
-                                 "-Cmetadata=",
-                                 _metadata,
-                               ]) ]
-    forward_variables_from(invoker,
-                           "*",
-                           [
-                             "features",
-                             "deps",
-                             "rustflags",
-                             "rustenv",
-                             "configs",
-                             "output_name",
-                             "crate_root",
-                             "unit_test_target",
-                             "visibility",
-                           ])
-    rustenv = [ "OUT_DIR=" + rebase_path(target_out_dir) ]
-    if (defined(invoker.rustenv)) {
-      rustenv += invoker.rustenv
-    }
-    if (_create_cpp_groups) {
-      visibility = [
-        ":${_target_name}",
-        ":${_target_name}_cpp_bindings",
-      ]
+  _generate_target = true
+  if (_forward_to_host_toolchain) {
+    if (current_toolchain != host_toolchain) {
+      # Redirect to the host toolchain.
+      group(target_name) {
+        forward_variables_from(invoker, TESTONLY_AND_VISIBILITY)
+        public_deps = [ ":${_forward_to_target_name}($host_toolchain)" ]
+      }
+
+      not_needed(invoker, "*")
+      not_needed([ "proc_macro_target" ])
+      _generate_target = false
     } else {
-      forward_variables_from(invoker, [ "visibility" ])
+      # We're in the host toolchain, so just generate an alias.
+      group(target_name) {
+        forward_variables_from(invoker, TESTONLY_AND_VISIBILITY)
+        public_deps = [ ":${_forward_to_target_name}" ]
+      }
     }
+
+    # Since we're forwarding through a group which uses `target_name`, we rename
+    # the actual target to what the group points at.
+    target_name = _forward_to_target_name
   }
 
-  if (_build_unit_tests) {
+  if (_generate_target) {
+    target(invoker.target_type, "${target_name}${main_target_suffix}") {
+      crate_name = _crate_name
+      crate_root = _crate_root
+      configs = []
+      configs = _configs
+      deps = _deps
+      if (defined(_cxx_bindings)) {
+        deps += [ "//build/rust:cxx_rustdeps" ]
+      }
+      rustflags = _rustflags
+      rustflags += [ string_join("",
+                                 [
+                                   "-Cmetadata=",
+                                   _metadata,
+                                 ]) ]
+      forward_variables_from(invoker,
+                             "*",
+                             [
+                               "features",
+                               "deps",
+                               "rustflags",
+                               "rustenv",
+                               "configs",
+                               "output_name",
+                               "unit_test_target",
+                               "visibility",
+                             ])
+      rustenv = [ "OUT_DIR=" + rebase_path(target_out_dir) ]
+      if (defined(invoker.rustenv)) {
+        rustenv += invoker.rustenv
+      }
+      if (_create_cpp_groups) {
+        visibility = [
+          ":${_target_name}",
+          ":${_target_name}_cpp_bindings",
+        ]
+      } else {
+        forward_variables_from(invoker, [ "visibility" ])
+      }
+    }
+
+    if (defined(_cxx_bindings)) {
+      rust_cxx("${_target_name}_cxx") {
+        inputs = _cxx_bindings
+        native_header_deps = _deps
+      }
+    }
+  } else {
+    not_needed([
+                 "_metadata",
+                 "_crate_root",
+                 "_crate_name",
+               ])
+  }
+
+  if (_generate_target && _build_unit_tests) {
     _unit_test_target = "${_target_name}_unittests"
     if (defined(invoker.unit_test_target)) {
       _unit_test_target = invoker.unit_test_target
@@ -234,14 +279,10 @@
       deps += _deps_for_rust_targets
     }
   } else {
-    not_needed([ "_test_deps" ])
-  }
-
-  if (defined(_cxx_bindings)) {
-    rust_cxx("${_target_name}_cxx") {
-      inputs = _cxx_bindings
-      native_header_deps = _deps
-    }
+    not_needed([
+                 "_test_deps",
+                 "_build_unit_tests",
+               ])
   }
 }
 
diff --git a/cc/paint/paint_op_buffer.cc b/cc/paint/paint_op_buffer.cc
index 6d84ce09..3b07bf1 100644
--- a/cc/paint/paint_op_buffer.cc
+++ b/cc/paint/paint_op_buffer.cc
@@ -348,7 +348,8 @@
     : image_provider(image_provider),
       original_ctm(original_ctm),
       custom_callback(custom_callback),
-      did_draw_op_callback(did_draw_op_callback) {}
+      did_draw_op_callback(did_draw_op_callback),
+      raw_draw_analysis(false) {}
 
 PlaybackParams::~PlaybackParams() {}
 
@@ -782,10 +783,12 @@
   helper.AlignMemory(alignof(SkScalar));
   helper.Write(op->x);
   helper.Write(op->y);
-  helper.Write(op->blob);
   helper.Write(options.raw_draw);
-  if (options.raw_draw)
-    helper.Write(current_ctm.asM33());
+  if (options.raw_draw) {
+    helper.Write(op->slug);
+  } else {
+    helper.Write(op->blob);
+  }
   return helper.size();
 }
 
@@ -1350,13 +1353,12 @@
   deserializer.AlignMemory(alignof(SkScalar));
   deserializer.Read(&deserializer->x);
   deserializer.Read(&deserializer->y);
-  deserializer.Read(&deserializer->blob);
   bool raw_draw = false;
   deserializer.Read(&raw_draw);
   if (raw_draw) {
-    SkMatrix hint;
-    deserializer.Read(&hint);
-    deserializer->hint = hint;
+    deserializer.Read(&deserializer->slug);
+  } else {
+    deserializer.Read(&deserializer->blob);
   }
   return deserializer.FinalizeOp();
 }
@@ -1806,18 +1808,15 @@
                                      const PlaybackParams& params) {
   if (op->node_id)
     SkPDF::SetNodeId(canvas, op->node_id);
-  flags->DrawToSk(canvas, [op](SkCanvas* c, const SkPaint& p) {
-    if (op->hint) {
-      sk_sp<GrSlug> slug;
-      {
-        SkAutoCanvasRestore auto_save(c, true);
-        c->setMatrix(*op->hint);
-        slug = GrSlug::ConvertBlob(c, *op->blob, {op->x, op->y}, p);
-      }
-      if (slug)
-        slug->draw(c);
-    } else {
+  flags->DrawToSk(canvas, [op, &params](SkCanvas* c, const SkPaint& p) {
+    if (op->blob) {
       c->drawTextBlob(op->blob.get(), op->x, op->y, p);
+      if (params.raw_draw_analysis) {
+        const_cast<DrawTextBlobOp*>(op)->slug =
+            GrSlug::ConvertBlob(c, *op->blob, {op->x, op->y}, p);
+      }
+    } else if (op->slug) {
+      op->slug->draw(c);
     }
   });
   if (op->node_id)
diff --git a/cc/paint/paint_op_buffer.h b/cc/paint/paint_op_buffer.h
index 8dadc8c..3c7ded2b 100644
--- a/cc/paint/paint_op_buffer.h
+++ b/cc/paint/paint_op_buffer.h
@@ -151,6 +151,7 @@
   CustomDataRasterCallback custom_callback;
   DidDrawOpCallback did_draw_op_callback;
   absl::optional<bool> save_layer_alpha_should_preserve_lcd_text;
+  bool raw_draw_analysis;
 };
 
 class CC_PAINT_EXPORT PaintOp {
@@ -884,11 +885,11 @@
   HAS_SERIALIZATION_FUNCTIONS();
 
   sk_sp<SkTextBlob> blob;
+  sk_sp<GrSlug> slug;
   SkScalar x;
   SkScalar y;
   // This field isn't serialized.
   NodeId node_id = kInvalidNodeId;
-  absl::optional<SkMatrix> hint;
 
  private:
   DrawTextBlobOp();
diff --git a/cc/paint/paint_op_buffer_serializer.cc b/cc/paint/paint_op_buffer_serializer.cc
index 1f6f4912..72e9dc2 100644
--- a/cc/paint/paint_op_buffer_serializer.cc
+++ b/cc/paint/paint_op_buffer_serializer.cc
@@ -19,11 +19,12 @@
 namespace cc {
 namespace {
 
-
-PlaybackParams MakeParams(const SkCanvas* canvas) {
+PlaybackParams MakeParams(const SkCanvas* canvas, bool raw_draw) {
   // We don't use an ImageProvider here since the ops are played onto a no-draw
   // canvas for state tracking and don't need decoded images.
-  return PlaybackParams(nullptr, canvas->getLocalToDevice());
+  PlaybackParams params(nullptr, canvas->getLocalToDevice());
+  params.raw_draw_analysis = raw_draw;
+  return params;
 }
 
 std::unique_ptr<SkCanvas> MakeAnalysisCanvas(
@@ -62,7 +63,7 @@
   // only used for serializing the preamble and the initial save / final restore
   // SerializeBuffer will create its own PlaybackParams based on the
   // post-preamble canvas.
-  PlaybackParams params = MakeParams(canvas.get());
+  PlaybackParams params = MakeParams(canvas.get(), options_.raw_draw);
 
   int saveCount = canvas->getSaveCount();
   Save(canvas.get(), params);
@@ -81,7 +82,7 @@
   // only used for serializing the preamble and the initial save / final restore
   // SerializeBuffer will create its own PlaybackParams based on the
   // post-preamble canvas.
-  PlaybackParams params = MakeParams(canvas.get());
+  PlaybackParams params = MakeParams(canvas.get(), options_.raw_draw);
 
   int saveCount = canvas->getSaveCount();
   Save(canvas.get(), params);
@@ -100,7 +101,7 @@
                                         const gfx::SizeF& post_scale) {
   std::unique_ptr<SkCanvas> canvas = MakeAnalysisCanvas(options_);
 
-  PlaybackParams params = MakeParams(canvas.get());
+  PlaybackParams params = MakeParams(canvas.get(), options_.raw_draw);
 
   // TODO(khushalsagar): remove this clip rect if it's not needed.
   if (!playback_rect.IsEmpty()) {
@@ -275,7 +276,7 @@
   DCHECK(buffer);
   // This updates the original_ctm to reflect the canvas transformation at
   // start of this call to SerializeBuffer.
-  PlaybackParams params = MakeParams(canvas);
+  PlaybackParams params = MakeParams(canvas, options_.raw_draw);
 
   for (PaintOpBuffer::PlaybackFoldingIterator iter(buffer, offsets); iter;
        ++iter) {
@@ -293,7 +294,7 @@
   DCHECK(buffer);
   // This updates the original_ctm to reflect the canvas transformation at
   // start of this call to SerializeBuffer.
-  PlaybackParams params = MakeParams(canvas);
+  PlaybackParams params = MakeParams(canvas, options_.raw_draw);
   bool destroy_op_only = false;
 
   for (PaintOpBuffer::PlaybackFoldingIterator iter(buffer, offsets); iter;
diff --git a/cc/paint/paint_op_reader.cc b/cc/paint/paint_op_reader.cc
index c56e2ea..f7ea17c 100644
--- a/cc/paint/paint_op_reader.cc
+++ b/cc/paint/paint_op_reader.cc
@@ -34,6 +34,7 @@
 #include "third_party/skia/include/core/SkRRect.h"
 #include "third_party/skia/include/core/SkSerialProcs.h"
 #include "third_party/skia/include/core/SkTextBlob.h"
+#include "third_party/skia/include/private/chromium/GrSlug.h"
 #include "third_party/skia/include/private/chromium/SkChromeRemoteGlyphCache.h"
 
 namespace cc {
@@ -473,6 +474,27 @@
   memory_ += size;
   remaining_bytes_ -= size;
 }
+void PaintOpReader::Read(sk_sp<GrSlug>* slug) {
+  AlignMemory(4);
+
+  size_t data_bytes = 0u;
+  ReadSize(&data_bytes);
+
+  if (data_bytes == 0) {
+    *slug = nullptr;
+    return;
+  }
+  if (remaining_bytes_ < data_bytes)
+    SetInvalid(
+        DeserializationError::kInsufficientRemainingBytes_Read_SkTextBlob);
+  if (!valid_)
+    return;
+
+  *slug = GrSlug::Deserialize(const_cast<const char*>(memory_), data_bytes,
+                              options_.strike_client);
+  memory_ += data_bytes;
+  remaining_bytes_ -= data_bytes;
+}
 
 void PaintOpReader::Read(sk_sp<SkTextBlob>* blob) {
   AlignMemory(4);
diff --git a/cc/paint/paint_op_reader.h b/cc/paint/paint_op_reader.h
index 201cdfde..0317f6d 100644
--- a/cc/paint/paint_op_reader.h
+++ b/cc/paint/paint_op_reader.h
@@ -66,6 +66,7 @@
   void Read(PaintImage* image);
   void Read(sk_sp<SkData>* data);
   void Read(sk_sp<SkTextBlob>* blob);
+  void Read(sk_sp<GrSlug>* slug);
   void Read(sk_sp<PaintFilter>* filter);
   void Read(sk_sp<PaintShader>* shader);
   void Read(SkMatrix* matrix);
diff --git a/cc/paint/paint_op_writer.cc b/cc/paint/paint_op_writer.cc
index 102a126..816e9aee 100644
--- a/cc/paint/paint_op_writer.cc
+++ b/cc/paint/paint_op_writer.cc
@@ -20,6 +20,7 @@
 #include "gpu/command_buffer/common/mailbox.h"
 #include "third_party/skia/include/core/SkSerialProcs.h"
 #include "third_party/skia/include/core/SkTextBlob.h"
+#include "third_party/skia/include/private/chromium/GrSlug.h"
 #include "third_party/skia/include/private/chromium/SkChromeRemoteGlyphCache.h"
 #include "ui/gfx/geometry/rect_conversions.h"
 #include "ui/gfx/geometry/skia_conversions.h"
@@ -385,6 +386,28 @@
   remaining_bytes_ -= written;
 }
 
+void PaintOpWriter::Write(const sk_sp<GrSlug>& slug) {
+  if (!valid_)
+    return;
+
+  AlignMemory(4);
+  uint64_t* size_memory = WriteSize(0u);
+  size_t bytes_written = 0;
+  if (slug) {
+    // TODO(penghuang): should we use a unique id to avoid sending the same
+    // slug?
+    bytes_written = slug->serialize(
+        memory_, base::bits::AlignDown(remaining_bytes_, kSkiaAlignment));
+    if (bytes_written == 0u) {
+      valid_ = false;
+      return;
+    }
+  }
+  *size_memory = bytes_written;
+  memory_ += bytes_written;
+  remaining_bytes_ -= bytes_written;
+}
+
 void PaintOpWriter::Write(const sk_sp<SkTextBlob>& blob) {
   DCHECK(blob);
   if (!valid_)
diff --git a/cc/paint/paint_op_writer.h b/cc/paint/paint_op_writer.h
index 520eaca..0c35f8e7 100644
--- a/cc/paint/paint_op_writer.h
+++ b/cc/paint/paint_op_writer.h
@@ -65,6 +65,7 @@
   void Write(const SkColorSpace* data);
   void Write(const SkSamplingOptions&);
   void Write(const sk_sp<SkTextBlob>& blob);
+  void Write(const sk_sp<GrSlug>& slug);
   void Write(SkYUVColorSpace yuv_color_space);
   void Write(SkYUVAInfo::PlaneConfig plane_config);
   void Write(SkYUVAInfo::Subsampling subsampling);
diff --git a/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceTabSwitcherTest.java b/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceTabSwitcherTest.java
index acafc0ec..6364bbb 100644
--- a/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceTabSwitcherTest.java
+++ b/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceTabSwitcherTest.java
@@ -19,6 +19,7 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
+import static org.chromium.chrome.browser.feed.FeedPlaceholderLayout.DISABLE_ANIMATION_SWITCH;
 import static org.chromium.chrome.features.start_surface.StartSurfaceTestUtils.START_SURFACE_TEST_BASE_PARAMS;
 import static org.chromium.chrome.features.start_surface.StartSurfaceTestUtils.sClassParamsForStartSurfaceTest;
 import static org.chromium.ui.test.util.ViewUtils.onViewWaiting;
@@ -369,7 +370,8 @@
     @EnableFeatures(ChromeFeatureList.TAB_GROUPS_ANDROID)
     // clang-format off
     @CommandLineFlags.Add({START_SURFACE_TEST_BASE_PARAMS +
-        "/show_tabs_in_mru_order/true/show_last_active_tab_only/true"})
+        "/show_tabs_in_mru_order/true/show_last_active_tab_only/true",
+        DISABLE_ANIMATION_SWITCH})
     public void testShowV2_GridTabSwitcher_AlwaysShowTabsInCreationOrder() {
         // clang-format on
         tabSwitcher_AlwaysShowTabsInGridTabSwitcherInCreationOrderImpl();
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/pseudotab/PseudoTab.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/pseudotab/PseudoTab.java
index 1c3f0c2..70a9739 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/pseudotab/PseudoTab.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/pseudotab/PseudoTab.java
@@ -136,7 +136,10 @@
         if (tabList != null) {
             pseudoTabs = new ArrayList<>();
             for (int i = 0; i < tabList.getCount(); i++) {
-                pseudoTabs.add(fromTab(tabList.getTabAt(i)));
+                Tab tab = tabList.getTabAt(i);
+                if (tab != null) {
+                    pseudoTabs.add(fromTab(tab));
+                }
             }
         }
         return pseudoTabs;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/share/ShareHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/share/ShareHelper.java
index 243e7f6b..b669248 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/share/ShareHelper.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/share/ShareHelper.java
@@ -196,7 +196,7 @@
         SharedPreferencesManager.getInstance().writeString(
                 ChromePreferenceKeys.SHARING_LAST_SHARED_COMPONENT_NAME,
                 component.flattenToString());
-        if (ChromeFeatureList.isEnabled(ChromeFeatureList.SHARE_USAGE_RANKING) && profile != null) {
+        if (profile != null) {
             ShareHistoryBridge.addShareEntry(profile, component.flattenToString());
         }
     }
diff --git a/chrome/app/BUILD.gn b/chrome/app/BUILD.gn
index 0f93e18..bd66d118 100644
--- a/chrome/app/BUILD.gn
+++ b/chrome/app/BUILD.gn
@@ -250,13 +250,6 @@
 if (is_fuchsia) {
   # Generate a packaged component, and an installer script to aid development.
 
-  # TODO(crbug.com/1211758): Remove this once bringup on CFv2 is complete.
-  fuchsia_component("chrome_component_v1") {
-    manifest = "chrome_v1.cmx"
-    data_deps = [ "//chrome:chrome_initial" ]
-    visibility = [ ":*" ]
-  }
-
   fuchsia_component("chrome_component") {
     manifest = "chrome.cml"
     data_deps = [ "//chrome:chrome_initial" ]
@@ -265,10 +258,7 @@
 
   fuchsia_package("chrome_pkg") {
     package_name = "chrome"
-    deps = [
-      ":chrome_component",
-      ":chrome_component_v1",
-    ]
+    deps = [ ":chrome_component" ]
 
     excluded_dirs = FUCHSIA_PACKAGED_CONTENT_EMBEDDER_EXCLUDED_DIRS
   }
@@ -286,7 +276,7 @@
     executable = rebase_path("//build/fuchsia/run_ffx_command.py")
     executable_args = [
       "--command",
-      "session add fuchsia-pkg://fuchsia.com/chrome#meta/chrome_v1.cmx -- " +
+      "session add fuchsia-pkg://fuchsia.com/chrome#meta/chrome.cm -- " +
           "%args%",
     ]
     include_fuchsia_build_dir = true
diff --git a/chrome/app/chrome.cml b/chrome/app/chrome.cml
index fbf680c..9323ba55 100644
--- a/chrome/app/chrome.cml
+++ b/chrome/app/chrome.cml
@@ -7,7 +7,6 @@
         runner: "elf",
         binary: "chrome",
         args: [
-            "--enable-cfv2",
             "--enable-features=Vulkan,CanvasOopRasterization",
         ],
 
diff --git a/chrome/app/chrome_v1.cmx b/chrome/app/chrome_v1.cmx
deleted file mode 100644
index a45fa78..0000000
--- a/chrome/app/chrome_v1.cmx
+++ /dev/null
@@ -1,40 +0,0 @@
-{
-  "program": {
-    "binary": "chrome",
-    "args": [ "--enable-features=Vulkan,CanvasOopRasterization" ]
-  },
-  "sandbox": {
-    "features": [
-      "config-data",
-      "deprecated-ambient-replace-as-executable",
-      "isolated-cache-storage",
-      "isolated-persistent-storage",
-      "isolated-temp",
-      "root-ssl-certificates",
-      "vulkan"
-    ],
-    "services": [
-      "fuchsia.buildinfo.Provider",
-      "fuchsia.device.NameProvider",
-      "fuchsia.element.GraphicalPresenter",
-      "fuchsia.fonts.Provider",
-      "fuchsia.input.virtualkeyboard.ControllerCreator",
-      "fuchsia.intl.PropertyProvider",
-      "fuchsia.logger.LogSink",
-      "fuchsia.media.Audio",
-      "fuchsia.media.AudioDeviceEnumerator",
-      "fuchsia.media.ProfileProvider",
-      "fuchsia.memorypressure.Provider",
-      "fuchsia.net.interfaces.State",
-      "fuchsia.net.name.Lookup",
-      "fuchsia.posix.socket.Provider",
-      "fuchsia.process.Launcher",
-      "fuchsia.sysmem.Allocator",
-      "fuchsia.ui.composition.Allocator",
-      "fuchsia.ui.composition.Flatland",
-      "fuchsia.ui.input3.Keyboard",
-      "fuchsia.ui.scenic.Scenic",
-      "fuchsia.vulkan.loader.Loader"
-    ]
-  }
-}
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index ef320764..c84e127 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -5947,8 +5947,6 @@
       "fuchsia/chrome_browser_main_parts_fuchsia.h",
       "fuchsia/element_manager_impl.cc",
       "fuchsia/element_manager_impl.h",
-      "fuchsia/switches.cc",
-      "fuchsia/switches.h",
       "fullscreen_fuchsia.cc",
       "icon_loader_fuchsia.cc",
       "importer/firefox_profile_lock_fuchsia.cc",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 7d18dcd..189acbd 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -2360,14 +2360,9 @@
 #if BUILDFLAG(IS_ANDROID)
 const FeatureEntry::FeatureParam kFedCmVariationAutoSignin[] = {
     {features::kFedCmAutoSigninFieldTrialParamName, "true"}};
-const FeatureEntry::FeatureParam kFedCmVariationInterception[] = {
-    {features::kFedCmInterceptionFieldTrialParamName, "true"}};
 const FeatureEntry::FeatureVariation kFedCmFeatureVariations[] = {
     {"- with FedCM auto-signin", kFedCmVariationAutoSignin,
-     base::size(kFedCmVariationAutoSignin), nullptr},
-    {"- with FedCM HTTP filtering (very experimental)",
-     kFedCmVariationInterception, base::size(kFedCmVariationInterception),
-     nullptr}};
+     base::size(kFedCmVariationAutoSignin), nullptr}};
 #endif
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
@@ -4400,11 +4395,6 @@
      kOsCrOS,
      FEATURE_VALUE_TYPE(
          features::kExperimentalAccessibilityDictationExtension)},
-    {"enable-experimental-accessibility-dictation-offline",
-     flag_descriptions::kExperimentalAccessibilityDictationOfflineName,
-     flag_descriptions::kExperimentalAccessibilityDictationOfflineDescription,
-     kOsCrOS,
-     FEATURE_VALUE_TYPE(features::kExperimentalAccessibilityDictationOffline)},
     {"enable-experimental-accessibility-dictation-commands",
      flag_descriptions::kExperimentalAccessibilityDictationCommandsName,
      flag_descriptions::kExperimentalAccessibilityDictationCommandsDescription,
@@ -7405,14 +7395,6 @@
      FEATURE_VALUE_TYPE(
          autofill::features::kAutofillParseMerchantPromoCodeFields)},
 
-    {"autofill-enable-offer-notification-cross-tab-tracking",
-     flag_descriptions::kAutofillEnableOfferNotificationCrossTabTrackingName,
-     flag_descriptions::
-         kAutofillEnableOfferNotificationCrossTabTrackingDescription,
-     kOsAll,
-     FEATURE_VALUE_TYPE(
-         autofill::features::kAutofillEnableOfferNotificationCrossTabTracking)},
-
     {"autofill-enable-virtual-cards-risk-based-authentication",
      flag_descriptions::kAutofillEnableVirtualCardsRiskBasedAuthenticationName,
      flag_descriptions::
@@ -7422,11 +7404,6 @@
          autofill::features::
              kAutofillEnableVirtualCardsRiskBasedAuthentication)},
 
-    {"autofill-fix-offer-in-incognito",
-     flag_descriptions::kAutofillFixOfferInIncognitoName,
-     flag_descriptions::kAutofillFixOfferInIncognitoDescription, kOsAll,
-     FEATURE_VALUE_TYPE(autofill::features::kAutofillFixOfferInIncognito)},
-
     {"autofill-highlight-only-changed-value-in-preview-mode",
      flag_descriptions::kAutofillHighlightOnlyChangedValuesInPreviewModeName,
      flag_descriptions::
@@ -7706,13 +7683,6 @@
      FEATURE_VALUE_TYPE(omnibox::kUpdatedConnectionSecurityIndicators)},
 
 #if BUILDFLAG(IS_ANDROID)
-    {"share-usage-ranking", flag_descriptions::kShareUsageRankingName,
-     flag_descriptions::kShareUsageRankingDescription, kOsAndroid,
-     FEATURE_VALUE_TYPE(features::kShareUsageRanking)},
-    {"share-usage-ranking-fixed-more",
-     flag_descriptions::kShareUsageRankingFixedMoreName,
-     flag_descriptions::kShareUsageRankingFixedMoreDescription, kOsAndroid,
-     FEATURE_VALUE_TYPE(features::kShareUsageRankingFixedMore)},
     {"swap-android-share-hub-rows",
      flag_descriptions::kSwapAndroidShareHubRowsName,
      flag_descriptions::kSwapAndroidShareHubRowsDescription, kOsAndroid,
diff --git a/chrome/browser/apps/app_discovery_service/recommended_arc_app_fetcher_unittest.cc b/chrome/browser/apps/app_discovery_service/recommended_arc_app_fetcher_unittest.cc
index a3d5515..1c76d2c 100644
--- a/chrome/browser/apps/app_discovery_service/recommended_arc_app_fetcher_unittest.cc
+++ b/chrome/browser/apps/app_discovery_service/recommended_arc_app_fetcher_unittest.cc
@@ -106,13 +106,13 @@
 
 TEST_F(RecommendedArcAppFetcherTest, OnLoadError) {
   arc_app_fetcher()->SetCallbackForTesting(base::BindLambdaForTesting(
-      [](std::vector<Result> results) { ASSERT_EQ(results.size(), 0); }));
+      [](std::vector<Result> results) { ASSERT_EQ(results.size(), 0u); }));
   arc_app_fetcher()->OnLoadError();
 }
 
 TEST_F(RecommendedArcAppFetcherTest, OnParseResponseError) {
   arc_app_fetcher()->SetCallbackForTesting(base::BindLambdaForTesting(
-      [](std::vector<Result> results) { ASSERT_EQ(results.size(), 0); }));
+      [](std::vector<Result> results) { ASSERT_EQ(results.size(), 0u); }));
   arc_app_fetcher()->OnParseResponseError();
 }
 
diff --git a/chrome/browser/apps/app_service/browser_app_instance_tracker_browsertest.cc b/chrome/browser/apps/app_service/browser_app_instance_tracker_browsertest.cc
index 07c7cf9c..195510f 100644
--- a/chrome/browser/apps/app_service/browser_app_instance_tracker_browsertest.cc
+++ b/chrome/browser/apps/app_service/browser_app_instance_tracker_browsertest.cc
@@ -370,8 +370,8 @@
     browser = CreateBrowser();
     window = browser->window()->GetNativeWindow();
     tab_app1 = InsertForegroundTab(browser, "https://a.example.org");
-    EXPECT_EQ(GetId(browser), 1);
-    EXPECT_EQ(GetId(tab_app1), 2);
+    EXPECT_EQ(GetId(browser), 1u);
+    EXPECT_EQ(GetId(tab_app1), 2u);
     recorder.Verify({
         {"added", 1, kChromeWindow, "", window, "", kActive, false},
         {"added", 2, kAppTab, kAppId_A, window, "", kActive, kActive},
@@ -386,7 +386,7 @@
     Recorder recorder(*tracker_);
 
     tab_app2 = InsertForegroundTab(browser, "https://b.example.org");
-    EXPECT_EQ(GetId(tab_app2), 3);
+    EXPECT_EQ(GetId(tab_app2), 3u);
     recorder.Verify({
         {"updated", 2, kAppTab, kAppId_A, window, kTitle_A, kActive, kInactive},
         {"added", 3, kAppTab, kAppId_B, window, "", kActive, kActive},
@@ -412,9 +412,9 @@
     Recorder recorder(*tracker_);
 
     auto* tab_app3 = InsertForegroundTab(browser, "https://a.example.org");
-    EXPECT_EQ(GetId(tab_app3), 4);
+    EXPECT_EQ(GetId(tab_app3), 4u);
     auto* tab_app4 = InsertForegroundTab(browser, "https://b.example.org");
-    EXPECT_EQ(GetId(tab_app4), 5);
+    EXPECT_EQ(GetId(tab_app4), 5u);
     // Close in reverse order.
     int i = browser->tab_strip_model()->GetIndexOfWebContents(tab_app4);
     browser->tab_strip_model()->CloseWebContentsAt(
@@ -562,8 +562,8 @@
   auto* browser = CreateBrowser();
   auto* tab = InsertForegroundTab(browser, "https://c.example.org");
   auto* window = browser->window()->GetNativeWindow();
-  EXPECT_EQ(GetId(browser), 1);
-  EXPECT_EQ(GetId(tab), 0);
+  EXPECT_EQ(GetId(browser), 1u);
+  EXPECT_EQ(GetId(tab), 0u);
 
   // Navigate the foreground tab to app A.
   {
@@ -571,7 +571,7 @@
     Recorder recorder(*tracker_);
 
     NavigateActiveTab(browser, "https://a.example.org");
-    EXPECT_EQ(GetId(tab), 2);
+    EXPECT_EQ(GetId(tab), 2u);
     recorder.Verify({
         {"added", 2, kAppTab, kAppId_A, window, kURL_A, kActive, kActive},
         {"updated", 2, kAppTab, kAppId_A, window, kTitle_A, kActive, kActive},
@@ -584,7 +584,7 @@
     Recorder recorder(*tracker_);
 
     NavigateActiveTab(browser, "https://b.example.org");
-    EXPECT_EQ(GetId(tab), 3);
+    EXPECT_EQ(GetId(tab), 3u);
     recorder.Verify({
         {"removed", 2, kAppTab, kAppId_A, window, kTitle_A, kActive, kActive},
         {"added", 3, kAppTab, kAppId_B, window, kURL_B, kActive, kActive},
@@ -598,7 +598,7 @@
     Recorder recorder(*tracker_);
 
     NavigateActiveTab(browser, "https://c.example.org");
-    EXPECT_EQ(GetId(tab), 0);
+    EXPECT_EQ(GetId(tab), 0u);
     recorder.Verify({
         {"removed", 3, kAppTab, kAppId_B, window, kTitle_B, kActive, kActive},
     });
@@ -610,7 +610,7 @@
     Recorder recorder(*tracker_);
 
     NavigateActiveTab(browser, "https://b.example.org");
-    EXPECT_EQ(GetId(tab), 4);
+    EXPECT_EQ(GetId(tab), 4u);
     recorder.Verify({
         {"added", 4, kAppTab, kAppId_B, window, kURL_B, kActive, kActive},
         {"updated", 4, kAppTab, kAppId_B, window, kTitle_B, kActive, kActive},
@@ -623,7 +623,7 @@
     Recorder recorder(*tracker_);
 
     NavigateActiveTab(browser, "https://example.com");
-    EXPECT_EQ(GetId(tab), 0);
+    EXPECT_EQ(GetId(tab), 0u);
     recorder.Verify({
         {"removed", 4, kAppTab, kAppId_B, window, kTitle_B, kActive, kActive},
     });
@@ -635,7 +635,7 @@
     Recorder recorder(*tracker_);
 
     NavigateActiveTab(browser, "https://b.example.org");
-    EXPECT_EQ(GetId(tab), 5);
+    EXPECT_EQ(GetId(tab), 5u);
     recorder.Verify({
         {"added", 5, kAppTab, kAppId_B, window, kURL_B, kActive, kActive},
         {"updated", 5, kAppTab, kAppId_B, window, kTitle_B, kActive, kActive},
@@ -657,8 +657,8 @@
 
     browser = CreateAppBrowser(app_id);
     tab = InsertForegroundTab(browser, "https://d.example.org");
-    EXPECT_EQ(GetId(browser), 1);
-    EXPECT_EQ(GetId(tab), 1);
+    EXPECT_EQ(GetId(browser), 1u);
+    EXPECT_EQ(GetId(tab), 1u);
     window = browser->window()->GetNativeWindow();
     recorder.Verify({
         {"added", 1, kAppWindow, app_id, window, "", kActive, kActive},
@@ -688,8 +688,8 @@
 
     browser = CreateAppBrowser(kAppId_A);
     tab = InsertForegroundTab(browser, "https://a.example.org");
-    EXPECT_EQ(GetId(browser), 2);
-    EXPECT_EQ(GetId(tab), 2);
+    EXPECT_EQ(GetId(browser), 2u);
+    EXPECT_EQ(GetId(tab), 2u);
     window = browser->window()->GetNativeWindow();
     // When open in a window it's still an app, even if configured to open in a
     // tab.
@@ -741,8 +741,8 @@
 
     // A window is added, both the window and the tab map to the same app
     // instance.
-    EXPECT_EQ(GetId(browser), 1);
-    EXPECT_EQ(GetId(tab), 1);
+    EXPECT_EQ(GetId(browser), 1u);
+    EXPECT_EQ(GetId(tab), 1u);
     window = browser->window()->GetNativeWindow();
     recorder.Verify({
         {"added", 1, kAppWindow, app_id, window, "", kActive, kActive},
@@ -762,7 +762,7 @@
                              u"crosh2");
 
     // Only title of the existing app instance should be updated.
-    EXPECT_EQ(GetId(tab), 1);
+    EXPECT_EQ(GetId(tab), 1u);
     recorder.Verify({
         {"updated", 1, kAppWindow, app_id, window, "", kActive, kActive},
         {"updated", 1, kAppWindow, app_id, window, "crosh2", kActive, kActive},
@@ -787,10 +787,10 @@
   auto* browser = CreateBrowser();
   auto* window = browser->window()->GetNativeWindow();
   auto* tab0 = InsertForegroundTab(browser, "https://a.example.org");
-  EXPECT_EQ(GetId(browser), 1);
-  EXPECT_EQ(GetId(tab0), 2);
+  EXPECT_EQ(GetId(browser), 1u);
+  EXPECT_EQ(GetId(tab0), 2u);
   auto* tab1 = InsertForegroundTab(browser, "https://b.example.org");
-  EXPECT_EQ(GetId(tab1), 3);
+  EXPECT_EQ(GetId(tab1), 3u);
   InsertForegroundTab(browser, "https://c.example.org");
 
   // Switch tabs: no app -> app A
@@ -835,20 +835,20 @@
   auto* b1_tab1 = InsertForegroundTab(browser1, "https://a.example.org");
   auto* b1_tab2 = InsertForegroundTab(browser1, "https://c.example.org");
   auto* b1_tab3 = InsertForegroundTab(browser1, "https://b.example.org");
-  EXPECT_EQ(GetId(browser1), 1);
-  EXPECT_EQ(GetId(b1_tab1), 2);
-  EXPECT_EQ(GetId(b1_tab2), 0);
-  EXPECT_EQ(GetId(b1_tab3), 3);
+  EXPECT_EQ(GetId(browser1), 1u);
+  EXPECT_EQ(GetId(b1_tab1), 2u);
+  EXPECT_EQ(GetId(b1_tab2), 0u);
+  EXPECT_EQ(GetId(b1_tab3), 3u);
 
   auto* browser2 = CreateBrowser();
   auto* window2 = browser2->window()->GetNativeWindow();
   auto* b2_tab1 = InsertForegroundTab(browser2, "https://a.example.org");
   auto* b2_tab2 = InsertForegroundTab(browser2, "https://c.example.org");
   auto* b2_tab3 = InsertForegroundTab(browser2, "https://b.example.org");
-  EXPECT_EQ(GetId(browser2), 4);
-  EXPECT_EQ(GetId(b2_tab1), 5);
-  EXPECT_EQ(GetId(b2_tab2), 0);
-  EXPECT_EQ(GetId(b2_tab3), 6);
+  EXPECT_EQ(GetId(browser2), 4u);
+  EXPECT_EQ(GetId(b2_tab1), 5u);
+  EXPECT_EQ(GetId(b2_tab2), 0u);
+  EXPECT_EQ(GetId(b2_tab3), 6u);
 
   ASSERT_FALSE(browser1->window()->IsActive());
   ASSERT_TRUE(browser2->window()->IsActive());
@@ -902,19 +902,19 @@
   auto* window1 = browser1->window()->GetNativeWindow();
   auto* b1_tab1 = InsertForegroundTab(browser1, "https://a.example.org");
   auto* b1_tab2 = InsertForegroundTab(browser1, "https://b.example.org");
-  EXPECT_EQ(GetId(browser1), 1);
-  EXPECT_EQ(GetId(b1_tab1), 2);
-  EXPECT_EQ(GetId(b1_tab2), 3);
+  EXPECT_EQ(GetId(browser1), 1u);
+  EXPECT_EQ(GetId(b1_tab1), 2u);
+  EXPECT_EQ(GetId(b1_tab2), 3u);
 
   auto* browser2 = CreateBrowser();
   auto* window2 = browser2->window()->GetNativeWindow();
   auto* b2_tab1 = InsertForegroundTab(browser2, "https://a.example.org");
-  EXPECT_EQ(GetId(browser2), 4);
-  EXPECT_EQ(GetId(b2_tab1), 5);
+  EXPECT_EQ(GetId(browser2), 4u);
+  EXPECT_EQ(GetId(b2_tab1), 5u);
   auto* b2_tab2 = InsertForegroundTab(browser2, "https://a.example.org");
-  EXPECT_EQ(GetId(b2_tab2), 6);
+  EXPECT_EQ(GetId(b2_tab2), 6u);
   auto* b2_tab3 = InsertForegroundTab(browser2, "https://b.example.org");
-  EXPECT_EQ(GetId(b2_tab3), 7);
+  EXPECT_EQ(GetId(b2_tab3), 7u);
 
   ASSERT_FALSE(browser1->window()->IsActive());
   ASSERT_TRUE(browser2->window()->IsActive());
@@ -969,8 +969,8 @@
   auto* window1 = browser1->window()->GetNativeWindow();
   InsertForegroundTab(browser1, "https://c.example.org");
   auto* tab = InsertForegroundTab(browser1, "https://d.example.org");
-  EXPECT_EQ(GetId(browser1), 1);
-  EXPECT_EQ(GetId(tab), 0);
+  EXPECT_EQ(GetId(browser1), 1u);
+  EXPECT_EQ(GetId(tab), 0u);
   ASSERT_TRUE(browser1->window()->IsActive());
 
   // Move the tab from the browser to the newly created app browser. This
@@ -979,7 +979,7 @@
   Recorder recorder(*tracker_);
 
   auto* browser2 = CreateAppBrowser(app_id);
-  EXPECT_EQ(GetId(browser2), 0);
+  EXPECT_EQ(GetId(browser2), 0u);
   auto* window2 = browser2->window()->GetNativeWindow();
   // Target app browser goes into foreground.
   browser2->window()->Activate();
@@ -1009,23 +1009,23 @@
   auto* browser1 = CreateBrowser();
   auto* window1 = browser1->window()->GetNativeWindow();
   auto* b1_tab1 = InsertForegroundTab(browser1, "https://a.example.org");
-  EXPECT_EQ(GetId(browser1), 1);
-  EXPECT_EQ(GetId(b1_tab1), 2);
+  EXPECT_EQ(GetId(browser1), 1u);
+  EXPECT_EQ(GetId(b1_tab1), 2u);
   auto* b1_tab2 = InsertForegroundTab(browser1, "https://c.example.org");
   auto* b1_tab3 = InsertForegroundTab(browser1, "https://b.example.org");
-  EXPECT_EQ(GetId(b1_tab3), 3);
+  EXPECT_EQ(GetId(b1_tab3), 3u);
 
   auto* browser2 = CreateBrowser();
   auto* window2 = browser2->window()->GetNativeWindow();
   auto* b2_tab1 = InsertForegroundTab(browser2, "https://c.example.org");
   auto* b2_tab2 = InsertForegroundTab(browser2, "https://b.example.org");
-  EXPECT_EQ(GetId(browser2), 4);
-  EXPECT_EQ(GetId(b2_tab2), 5);
+  EXPECT_EQ(GetId(browser2), 4u);
+  EXPECT_EQ(GetId(b2_tab2), 5u);
 
   auto* browser3 = CreateAppBrowser(kAppId_B);
   auto* window3 = browser3->window()->GetNativeWindow();
   auto* b3_tab1 = InsertForegroundTab(browser3, "https://b.example.org");
-  EXPECT_EQ(GetId(b3_tab1), 6);
+  EXPECT_EQ(GetId(b3_tab1), 6u);
 
   ASSERT_FALSE(browser1->window()->IsActive());
   ASSERT_FALSE(browser2->window()->IsActive());
@@ -1085,11 +1085,11 @@
     SCOPED_TRACE("install app opening in a tab");
     Recorder recorder(*tracker_);
 
-    EXPECT_EQ(GetId(tab1), 0);
-    EXPECT_EQ(GetId(tab3), 0);
+    EXPECT_EQ(GetId(tab1), 0u);
+    EXPECT_EQ(GetId(tab3), 0u);
     app_id = InstallWebAppOpeningAsTab("https://c.example.org");
-    EXPECT_EQ(GetId(tab1), 2);
-    EXPECT_EQ(GetId(tab3), 3);
+    EXPECT_EQ(GetId(tab1), 2u);
+    EXPECT_EQ(GetId(tab3), 3u);
     recorder.Verify({
         {"added", 2, kAppTab, app_id, window1, title, kActive, kInactive},
         {"added", 3, kAppTab, app_id, window1, title, kActive, kActive},
@@ -1101,8 +1101,8 @@
     Recorder recorder(*tracker_);
 
     UninstallWebApp(app_id);
-    EXPECT_EQ(GetId(tab1), 0);
-    EXPECT_EQ(GetId(tab3), 0);
+    EXPECT_EQ(GetId(tab1), 0u);
+    EXPECT_EQ(GetId(tab3), 0u);
     recorder.Verify({
         {"removed", 2, kAppTab, app_id, window1, title, kActive, kInactive},
         {"removed", 3, kAppTab, app_id, window1, title, kActive, kActive},
@@ -1113,13 +1113,13 @@
     SCOPED_TRACE("install app opening in a window");
     Recorder recorder(*tracker_);
 
-    EXPECT_EQ(GetId(tab1), 0);
-    EXPECT_EQ(GetId(tab3), 0);
+    EXPECT_EQ(GetId(tab1), 0u);
+    EXPECT_EQ(GetId(tab3), 0u);
     app_id = InstallWebAppOpeningAsWindow("https://c.example.org");
     // This has no effect: apps configured to open in a window aren't counted as
     // apps when opened in a tab.
-    EXPECT_EQ(GetId(tab1), 0);
-    EXPECT_EQ(GetId(tab3), 0);
+    EXPECT_EQ(GetId(tab1), 0u);
+    EXPECT_EQ(GetId(tab3), 0u);
     recorder.Verify({});
   }
 }
@@ -1159,11 +1159,11 @@
   window1 = browser1->window()->GetNativeWindow();
   content::WebContents* tab;
   tab = InsertForegroundTab(browser1, "https://a.example.org");
-  EXPECT_EQ(GetId(tab), 2);
+  EXPECT_EQ(GetId(tab), 2u);
   tab = InsertForegroundTab(browser1, "https://a.example.org");
-  EXPECT_EQ(GetId(tab), 3);
+  EXPECT_EQ(GetId(tab), 3u);
   tab = InsertForegroundTab(browser1, "https://b.example.org");
-  EXPECT_EQ(GetId(tab), 4);
+  EXPECT_EQ(GetId(tab), 4u);
 
   // Open a fourth tab with no app.
   InsertForegroundTab(browser1, "https://c.example.org");
@@ -1173,7 +1173,7 @@
   browser2 = CreateAppBrowser(app_d_id);
   window2 = browser2->window()->GetNativeWindow();
   tab = InsertForegroundTab(browser2, "https://d.example.org");
-  EXPECT_EQ(GetId(tab), 5);
+  EXPECT_EQ(GetId(tab), 5u);
 
   // Stop app A.
   {
diff --git a/chrome/browser/apps/app_service/intent_util_unittest.cc b/chrome/browser/apps/app_service/intent_util_unittest.cc
index 8c10ed3..d0316ec4 100644
--- a/chrome/browser/apps/app_service/intent_util_unittest.cc
+++ b/chrome/browser/apps/app_service/intent_util_unittest.cc
@@ -188,7 +188,7 @@
       web_app->app_id(), /*is_note_taking_web_app*/ false, scope,
       /*app_share_target*/ nullptr, /*enabled_file_handlers*/ nullptr);
 
-  ASSERT_EQ(filters.size(), 1);
+  ASSERT_EQ(filters.size(), 1u);
   IntentFilterPtr& filter = filters[0];
   EXPECT_FALSE(filter->activity_name.has_value());
   EXPECT_FALSE(filter->activity_label.has_value());
@@ -244,7 +244,7 @@
           web_app->app_id(), /*is_note_taking_web_app*/ false, scope,
           /*app_share_target*/ nullptr, /*enabled_file_handlers*/ nullptr);
 
-  ASSERT_EQ(filters.size(), 1);
+  ASSERT_EQ(filters.size(), 1u);
   apps::mojom::IntentFilterPtr& filter = filters[0];
   EXPECT_FALSE(filter->activity_name.has_value());
   EXPECT_FALSE(filter->activity_label.has_value());
@@ -314,21 +314,21 @@
       web_app->app_id(), /*is_note_taking_web_app*/ false, scope,
       /*app_share_target*/ nullptr, &file_handlers);
 
-  ASSERT_EQ(filters.size(), 2);
+  ASSERT_EQ(filters.size(), 2u);
   // 1st filter is URL filter.
 
   // File filter - View action
   const IntentFilterPtr& file_filter = filters[1];
-  ASSERT_EQ(file_filter->conditions.size(), 2);
+  ASSERT_EQ(file_filter->conditions.size(), 2u);
   const Condition& view_cond = *file_filter->conditions[0];
   EXPECT_EQ(view_cond.condition_type, ConditionType::kAction);
-  ASSERT_EQ(view_cond.condition_values.size(), 1);
+  ASSERT_EQ(view_cond.condition_values.size(), 1u);
   EXPECT_EQ(view_cond.condition_values[0]->value, apps_util::kIntentActionView);
 
   // File filter - mime & file extension match
   const Condition& file_cond = *file_filter->conditions[1];
   EXPECT_EQ(file_cond.condition_type, ConditionType::kFile);
-  ASSERT_EQ(file_cond.condition_values.size(), 2);
+  ASSERT_EQ(file_cond.condition_values.size(), 2u);
   EXPECT_EQ(file_cond.condition_values[0]->match_type,
             PatternMatchType::kMimeType);
   EXPECT_EQ(file_cond.condition_values[0]->value, "text/plain");
@@ -359,21 +359,21 @@
           web_app->app_id(), /*is_note_taking_web_app*/ false, scope,
           /*app_share_target*/ nullptr, &file_handlers);
 
-  ASSERT_EQ(filters.size(), 2);
+  ASSERT_EQ(filters.size(), 2u);
   // 1st filter is URL filter.
 
   // File filter - View action
   const apps::mojom::IntentFilterPtr& file_filter = filters[1];
-  ASSERT_EQ(file_filter->conditions.size(), 2);
+  ASSERT_EQ(file_filter->conditions.size(), 2u);
   const apps::mojom::Condition& view_cond = *file_filter->conditions[0];
   EXPECT_EQ(view_cond.condition_type, apps::mojom::ConditionType::kAction);
-  ASSERT_EQ(view_cond.condition_values.size(), 1);
+  ASSERT_EQ(view_cond.condition_values.size(), 1u);
   EXPECT_EQ(view_cond.condition_values[0]->value, apps_util::kIntentActionView);
 
   // File filter - mime & file extension match
   const apps::mojom::Condition& file_cond = *file_filter->conditions[1];
   EXPECT_EQ(file_cond.condition_type, apps::mojom::ConditionType::kFile);
-  ASSERT_EQ(file_cond.condition_values.size(), 2);
+  ASSERT_EQ(file_cond.condition_values.size(), 2u);
   EXPECT_EQ(file_cond.condition_values[0]->match_type,
             apps::mojom::PatternMatchType::kMimeType);
   EXPECT_EQ(file_cond.condition_values[0]->value, "text/plain");
@@ -394,13 +394,13 @@
       web_app->app_id(), /*is_note_taking_web_app*/ true, scope,
       /*app_share_target*/ nullptr, /*enabled_file_handlers*/ nullptr);
 
-  ASSERT_EQ(filters.size(), 2);
+  ASSERT_EQ(filters.size(), 2u);
 
   // 2nd filter is note-taking filter.
-  ASSERT_EQ(filters[1]->conditions.size(), 1);
+  ASSERT_EQ(filters[1]->conditions.size(), 1u);
   const Condition& condition = *filters[1]->conditions[0];
   EXPECT_EQ(condition.condition_type, ConditionType::kAction);
-  ASSERT_EQ(condition.condition_values.size(), 1);
+  ASSERT_EQ(condition.condition_values.size(), 1u);
   EXPECT_EQ(condition.condition_values[0]->value,
             apps_util::kIntentActionCreateNote);
 }
@@ -419,17 +419,17 @@
           web_app->app_id(), /*is_note_taking_web_app*/ true, scope,
           /*app_share_target*/ nullptr, /*enabled_file_handlers*/ nullptr);
 
-  ASSERT_EQ(filters.size(), 2);
+  ASSERT_EQ(filters.size(), 2u);
 
   // 1st filter is URL filter.
   EXPECT_TRUE(apps_util::IntentMatchesFilter(
       apps_util::CreateIntentFromUrl(scope), filters[0]));
 
   // 2nd filter is note-taking filter.
-  ASSERT_EQ(filters[1]->conditions.size(), 1);
+  ASSERT_EQ(filters[1]->conditions.size(), 1u);
   const apps::mojom::Condition& condition = *filters[1]->conditions[0];
   EXPECT_EQ(condition.condition_type, apps::mojom::ConditionType::kAction);
-  ASSERT_EQ(condition.condition_values.size(), 1);
+  ASSERT_EQ(condition.condition_values.size(), 1u);
   EXPECT_EQ(condition.condition_values[0]->value,
             apps_util::kIntentActionCreateNote);
   EXPECT_TRUE(apps_util::IntentMatchesFilter(
@@ -476,37 +476,37 @@
 
   IntentFilters filters = apps_util::CreateIntentFiltersForChromeApp(foo.get());
 
-  ASSERT_EQ(filters.size(), 2);
+  ASSERT_EQ(filters.size(), 2u);
 
   // "any" filter - View action
   const IntentFilterPtr& mime_filter = filters[0];
-  ASSERT_EQ(mime_filter->conditions.size(), 2);
+  ASSERT_EQ(mime_filter->conditions.size(), 2u);
   const Condition& view_cond = *mime_filter->conditions[0];
   EXPECT_EQ(view_cond.condition_type, ConditionType::kAction);
-  ASSERT_EQ(view_cond.condition_values.size(), 1);
+  ASSERT_EQ(view_cond.condition_values.size(), 1u);
   EXPECT_EQ(view_cond.condition_values[0]->value, apps_util::kIntentActionView);
 
   // "any" filter - mime type match
   const Condition& file_cond = *mime_filter->conditions[1];
   EXPECT_EQ(file_cond.condition_type, ConditionType::kFile);
-  ASSERT_EQ(file_cond.condition_values.size(), 1);
+  ASSERT_EQ(file_cond.condition_values.size(), 1u);
   EXPECT_EQ(file_cond.condition_values[0]->match_type,
             PatternMatchType::kMimeType);
   EXPECT_EQ(file_cond.condition_values[0]->value, "*/*");
 
   // Text filter - View action
   const IntentFilterPtr& mime_filter2 = filters[1];
-  ASSERT_EQ(mime_filter2->conditions.size(), 2);
+  ASSERT_EQ(mime_filter2->conditions.size(), 2u);
   const Condition& view_cond2 = *mime_filter2->conditions[0];
   EXPECT_EQ(view_cond2.condition_type, ConditionType::kAction);
-  ASSERT_EQ(view_cond2.condition_values.size(), 1);
+  ASSERT_EQ(view_cond2.condition_values.size(), 1u);
   EXPECT_EQ(view_cond2.condition_values[0]->value,
             apps_util::kIntentActionView);
 
   // Text filter - mime type match
   const Condition& file_cond2 = *mime_filter2->conditions[1];
   EXPECT_EQ(file_cond2.condition_type, ConditionType::kFile);
-  ASSERT_EQ(file_cond2.condition_values.size(), 2);
+  ASSERT_EQ(file_cond2.condition_values.size(), 2u);
   EXPECT_EQ(file_cond2.condition_values[0]->match_type,
             PatternMatchType::kMimeType);
   EXPECT_EQ(file_cond2.condition_values[0]->value, "text/plain");
@@ -558,37 +558,37 @@
   std::vector<apps::mojom::IntentFilterPtr> filters =
       apps_util::CreateChromeAppIntentFilters(foo.get());
 
-  ASSERT_EQ(filters.size(), 2);
+  ASSERT_EQ(filters.size(), 2u);
 
   // "any" filter - View action
   const apps::mojom::IntentFilterPtr& mime_filter = filters[0];
-  ASSERT_EQ(mime_filter->conditions.size(), 2);
+  ASSERT_EQ(mime_filter->conditions.size(), 2u);
   const apps::mojom::Condition& view_cond = *mime_filter->conditions[0];
   EXPECT_EQ(view_cond.condition_type, apps::mojom::ConditionType::kAction);
-  ASSERT_EQ(view_cond.condition_values.size(), 1);
+  ASSERT_EQ(view_cond.condition_values.size(), 1u);
   EXPECT_EQ(view_cond.condition_values[0]->value, apps_util::kIntentActionView);
 
   // "any" filter - mime type match
   const apps::mojom::Condition& file_cond = *mime_filter->conditions[1];
   EXPECT_EQ(file_cond.condition_type, apps::mojom::ConditionType::kFile);
-  ASSERT_EQ(file_cond.condition_values.size(), 1);
+  ASSERT_EQ(file_cond.condition_values.size(), 1u);
   EXPECT_EQ(file_cond.condition_values[0]->match_type,
             apps::mojom::PatternMatchType::kMimeType);
   EXPECT_EQ(file_cond.condition_values[0]->value, "*/*");
 
   // Text filter - View action
   const apps::mojom::IntentFilterPtr& mime_filter2 = filters[1];
-  ASSERT_EQ(mime_filter2->conditions.size(), 2);
+  ASSERT_EQ(mime_filter2->conditions.size(), 2u);
   const apps::mojom::Condition& view_cond2 = *mime_filter2->conditions[0];
   EXPECT_EQ(view_cond2.condition_type, apps::mojom::ConditionType::kAction);
-  ASSERT_EQ(view_cond2.condition_values.size(), 1);
+  ASSERT_EQ(view_cond2.condition_values.size(), 1u);
   EXPECT_EQ(view_cond2.condition_values[0]->value,
             apps_util::kIntentActionView);
 
   // Text filter - mime type match
   const apps::mojom::Condition& file_cond2 = *mime_filter2->conditions[1];
   EXPECT_EQ(file_cond2.condition_type, apps::mojom::ConditionType::kFile);
-  ASSERT_EQ(file_cond2.condition_values.size(), 2);
+  ASSERT_EQ(file_cond2.condition_values.size(), 2u);
   EXPECT_EQ(file_cond2.condition_values[0]->match_type,
             apps::mojom::PatternMatchType::kMimeType);
   EXPECT_EQ(file_cond2.condition_values[0]->value, "text/plain");
@@ -642,37 +642,37 @@
 
   IntentFilters filters = apps_util::CreateIntentFiltersForExtension(foo.get());
 
-  ASSERT_EQ(filters.size(), 2);
+  ASSERT_EQ(filters.size(), 2u);
 
   // "html" filter - View action
   const IntentFilterPtr& mime_filter = filters[0];
-  ASSERT_EQ(mime_filter->conditions.size(), 2);
+  ASSERT_EQ(mime_filter->conditions.size(), 2u);
   const Condition& view_cond = *mime_filter->conditions[0];
   EXPECT_EQ(view_cond.condition_type, ConditionType::kAction);
-  ASSERT_EQ(view_cond.condition_values.size(), 1);
+  ASSERT_EQ(view_cond.condition_values.size(), 1u);
   EXPECT_EQ(view_cond.condition_values[0]->value, apps_util::kIntentActionView);
 
   // "html" filter - glob match
   const Condition& file_cond = *mime_filter->conditions[1];
   EXPECT_EQ(file_cond.condition_type, ConditionType::kFile);
-  ASSERT_EQ(file_cond.condition_values.size(), 1);
+  ASSERT_EQ(file_cond.condition_values.size(), 1u);
   EXPECT_EQ(file_cond.condition_values[0]->match_type, PatternMatchType::kGlob);
   EXPECT_EQ(file_cond.condition_values[0]->value,
             R"(filesystem:chrome-extension://.*/.*\.html)");
 
   // "any" filter - View action
   const IntentFilterPtr& mime_filter2 = filters[1];
-  ASSERT_EQ(mime_filter2->conditions.size(), 2);
+  ASSERT_EQ(mime_filter2->conditions.size(), 2u);
   const Condition& view_cond2 = *mime_filter2->conditions[0];
   EXPECT_EQ(view_cond2.condition_type, ConditionType::kAction);
-  ASSERT_EQ(view_cond2.condition_values.size(), 1);
+  ASSERT_EQ(view_cond2.condition_values.size(), 1u);
   EXPECT_EQ(view_cond2.condition_values[0]->value,
             apps_util::kIntentActionView);
 
   // "any" filter - glob match
   const Condition& file_cond2 = *mime_filter2->conditions[1];
   EXPECT_EQ(file_cond2.condition_type, ConditionType::kFile);
-  ASSERT_EQ(file_cond2.condition_values.size(), 1);
+  ASSERT_EQ(file_cond2.condition_values.size(), 1u);
   EXPECT_EQ(file_cond2.condition_values[0]->match_type,
             PatternMatchType::kGlob);
   EXPECT_EQ(file_cond2.condition_values[0]->value,
@@ -725,20 +725,20 @@
   std::vector<apps::mojom::IntentFilterPtr> filters =
       apps_util::CreateExtensionIntentFilters(foo.get());
 
-  ASSERT_EQ(filters.size(), 2);
+  ASSERT_EQ(filters.size(), 2u);
 
   // "html" filter - View action
   const apps::mojom::IntentFilterPtr& mime_filter = filters[0];
-  ASSERT_EQ(mime_filter->conditions.size(), 2);
+  ASSERT_EQ(mime_filter->conditions.size(), 2u);
   const apps::mojom::Condition& view_cond = *mime_filter->conditions[0];
   EXPECT_EQ(view_cond.condition_type, apps::mojom::ConditionType::kAction);
-  ASSERT_EQ(view_cond.condition_values.size(), 1);
+  ASSERT_EQ(view_cond.condition_values.size(), 1u);
   EXPECT_EQ(view_cond.condition_values[0]->value, apps_util::kIntentActionView);
 
   // "html" filter - glob match
   const apps::mojom::Condition& file_cond = *mime_filter->conditions[1];
   EXPECT_EQ(file_cond.condition_type, apps::mojom::ConditionType::kFile);
-  ASSERT_EQ(file_cond.condition_values.size(), 1);
+  ASSERT_EQ(file_cond.condition_values.size(), 1u);
   EXPECT_EQ(file_cond.condition_values[0]->match_type,
             apps::mojom::PatternMatchType::kGlob);
   EXPECT_EQ(file_cond.condition_values[0]->value,
@@ -746,17 +746,17 @@
 
   // "any" filter - View action
   const apps::mojom::IntentFilterPtr& mime_filter2 = filters[1];
-  ASSERT_EQ(mime_filter2->conditions.size(), 2);
+  ASSERT_EQ(mime_filter2->conditions.size(), 2u);
   const apps::mojom::Condition& view_cond2 = *mime_filter2->conditions[0];
   EXPECT_EQ(view_cond2.condition_type, apps::mojom::ConditionType::kAction);
-  ASSERT_EQ(view_cond2.condition_values.size(), 1);
+  ASSERT_EQ(view_cond2.condition_values.size(), 1u);
   EXPECT_EQ(view_cond2.condition_values[0]->value,
             apps_util::kIntentActionView);
 
   // "any" filter - glob match
   const apps::mojom::Condition& file_cond2 = *mime_filter2->conditions[1];
   EXPECT_EQ(file_cond2.condition_type, apps::mojom::ConditionType::kFile);
-  ASSERT_EQ(file_cond2.condition_values.size(), 1);
+  ASSERT_EQ(file_cond2.condition_values.size(), 1u);
   EXPECT_EQ(file_cond2.condition_values[0]->match_type,
             apps::mojom::PatternMatchType::kGlob);
   EXPECT_EQ(file_cond2.condition_values[0]->value,
diff --git a/chrome/browser/apps/app_service/webapk/webapk_install_task_unittest.cc b/chrome/browser/apps/app_service/webapk/webapk_install_task_unittest.cc
index e1efad35..4cd28c0 100644
--- a/chrome/browser/apps/app_service/webapk/webapk_install_task_unittest.cc
+++ b/chrome/browser/apps/app_service/webapk/webapk_install_task_unittest.cc
@@ -210,7 +210,7 @@
   EXPECT_EQ(manifest.start_url(), kTestAppUrl);
   EXPECT_EQ(manifest.icons(0).src(), kTestAppIcon);
 
-  ASSERT_EQ(fake_webapk_instance()->handled_packages().size(), 1);
+  ASSERT_EQ(fake_webapk_instance()->handled_packages().size(), 1u);
   ASSERT_EQ(fake_webapk_instance()->handled_packages().count(
                 "org.chromium.webapk.some_package"),
             1);
@@ -258,9 +258,9 @@
   EXPECT_EQ(manifest.share_targets(0).params().url(), "share_url");
   EXPECT_FALSE(manifest.share_targets(0).params().has_title());
   EXPECT_EQ(manifest.share_targets(0).params().files(0).name(), "images");
-  EXPECT_EQ(manifest.share_targets(0).params().files(0).accept_size(), 1);
+  EXPECT_EQ(manifest.share_targets(0).params().files(0).accept_size(), 1u);
   EXPECT_EQ(manifest.share_targets(0).params().files(0).accept(0), "image/*");
-  EXPECT_EQ(manifest.share_targets(0).params().files(1).accept_size(), 2);
+  EXPECT_EQ(manifest.share_targets(0).params().files(1).accept_size(), 2u);
 }
 
 TEST_F(WebApkInstallTaskTest, NoIconInManifest) {
@@ -273,7 +273,7 @@
   base::HistogramTester histograms;
 
   ASSERT_FALSE(InstallWebApk(app_id));
-  ASSERT_EQ(apps::webapk_prefs::GetWebApkAppIds(profile()).size(), 0);
+  ASSERT_EQ(apps::webapk_prefs::GetWebApkAppIds(profile()).size(), 0u);
   histograms.ExpectBucketCount(apps::kWebApkInstallResultHistogram,
                                apps::WebApkInstallStatus::kAppInvalid, 1);
 }
@@ -287,8 +287,8 @@
 
   ASSERT_FALSE(InstallWebApk(app_id));
 
-  ASSERT_EQ(fake_webapk_instance()->handled_packages().size(), 0);
-  ASSERT_EQ(apps::webapk_prefs::GetWebApkAppIds(profile()).size(), 0);
+  ASSERT_EQ(fake_webapk_instance()->handled_packages().size(), 0u);
+  ASSERT_EQ(apps::webapk_prefs::GetWebApkAppIds(profile()).size(), 0u);
   histograms.ExpectBucketCount(apps::kWebApkInstallResultHistogram,
                                apps::WebApkInstallStatus::kNetworkError, 1);
   histograms.ExpectBucketCount(apps::kWebApkMinterErrorCodeHistogram,
@@ -308,7 +308,7 @@
   ASSERT_EQ(fake_webapk_instance()->handled_packages().count(
                 "org.chromium.webapk.some_package"),
             1);
-  ASSERT_EQ(apps::webapk_prefs::GetWebApkAppIds(profile()).size(), 0);
+  ASSERT_EQ(apps::webapk_prefs::GetWebApkAppIds(profile()).size(), 0u);
   histograms.ExpectBucketCount(apps::kWebApkInstallResultHistogram,
                                apps::WebApkInstallStatus::kGooglePlayError, 1);
   histograms.ExpectBucketCount(
@@ -409,7 +409,7 @@
               ::testing::ElementsAre(webapk::WebApk::SCOPE_DIFFERS));
 
   webapk::WebAppManifest manifest = last_webapk_request()->manifest();
-  EXPECT_EQ(last_webapk_request()->manifest().scopes_size(), 1);
+  EXPECT_EQ(last_webapk_request()->manifest().scopes_size(), 1u);
   EXPECT_EQ(last_webapk_request()->manifest().scopes(0),
             "https://www.differentexample.com/");
 
diff --git a/chrome/browser/ash/accessibility/accessibility_manager.cc b/chrome/browser/ash/accessibility/accessibility_manager.cc
index 9fb6c5d..573f9a8d85 100644
--- a/chrome/browser/ash/accessibility/accessibility_manager.cc
+++ b/chrome/browser/ash/accessibility/accessibility_manager.cc
@@ -896,16 +896,6 @@
   const bool enabled =
       pref_service->GetBoolean(prefs::kAccessibilityDictationEnabled);
 
-  // Only need to check SODA installation and locale preference if offline
-  // dictation is enabled.
-  if (!::features::IsExperimentalAccessibilityDictationOfflineEnabled()) {
-    // Show network dictation dialog if needed. Locale prefs are only used
-    // when this feature is enabled, so passing the empty string is OK.
-    if (enabled && triggered_by_user && ShouldShowNetworkDictationDialog(""))
-      ShowNetworkDictationDialog();
-    return;
-  }
-
   if (enabled &&
       pref_service->GetString(prefs::kAccessibilityDictationLocale).empty()) {
     // Dictation was turned on but the language pref isn't set yet. Determine if
@@ -923,7 +913,7 @@
   }
 
   // If SODA isn't available on the device, no need to try to install it.
-  if (!::features::IsDictationOfflineAvailableAndEnabled()) {
+  if (!::features::IsDictationOfflineAvailable()) {
     // Show network dictation dialog if needed. Locale doesn't matter as no
     // languages are supported by SODA.
     if (enabled && triggered_by_user && ShouldShowNetworkDictationDialog(""))
@@ -2034,7 +2024,7 @@
     return false;
   }
 
-  if (!::features::IsDictationOfflineAvailableAndEnabled())
+  if (!::features::IsDictationOfflineAvailable())
     return true;
 
   speech::SodaInstaller* soda_installer = speech::SodaInstaller::GetInstance();
@@ -2077,7 +2067,7 @@
 }
 
 void AccessibilityManager::MaybeInstallSoda(const std::string& locale) {
-  if (!::features::IsDictationOfflineAvailableAndEnabled())
+  if (!::features::IsDictationOfflineAvailable())
     return;
 
   speech::SodaInstaller* soda_installer = speech::SodaInstaller::GetInstance();
@@ -2122,7 +2112,7 @@
 }
 
 void AccessibilityManager::OnSodaInstallUpdated(int progress) {
-  if (!::features::IsDictationOfflineAvailableAndEnabled())
+  if (!::features::IsDictationOfflineAvailable())
     return;
 
   speech::SodaInstaller* soda_installer = speech::SodaInstaller::GetInstance();
@@ -2183,7 +2173,7 @@
 }
 
 bool AccessibilityManager::ShouldShowSodaSucceededNotificationForDictation() {
-  if (!::features::IsDictationOfflineAvailableAndEnabled() ||
+  if (!::features::IsDictationOfflineAvailable() ||
       !dictation_triggered_by_user_ || !IsDictationEnabled()) {
     return false;
   }
@@ -2204,7 +2194,7 @@
 
 bool AccessibilityManager::ShouldShowSodaFailedNotificationForDictation(
     speech::LanguageCode language_code) {
-  if (!::features::IsDictationOfflineAvailableAndEnabled() ||
+  if (!::features::IsDictationOfflineAvailable() ||
       !dictation_triggered_by_user_ || !IsDictationEnabled()) {
     return false;
   }
@@ -2228,7 +2218,7 @@
 
 void AccessibilityManager::ShowSodaDownloadNotificationForDictation(
     bool succeeded) {
-  if (!::features::IsDictationOfflineAvailableAndEnabled())
+  if (!::features::IsDictationOfflineAvailable())
     return;
 
   const std::string locale =
diff --git a/chrome/browser/ash/accessibility/accessibility_manager_browsertest.cc b/chrome/browser/ash/accessibility/accessibility_manager_browsertest.cc
index 999f1c4..b787a60 100644
--- a/chrome/browser/ash/accessibility/accessibility_manager_browsertest.cc
+++ b/chrome/browser/ash/accessibility/accessibility_manager_browsertest.cc
@@ -332,7 +332,7 @@
 void AssertMessageCenterEmpty() {
   message_center::NotificationList::Notifications notifications =
       message_center::MessageCenter::Get()->GetVisibleNotifications();
-  ASSERT_EQ(0, notifications.size());
+  ASSERT_EQ(0u, notifications.size());
 }
 
 void ClearMessageCenter() {
@@ -367,9 +367,7 @@
 
   void SetUpCommandLine(base::CommandLine* command_line) override {
     scoped_feature_list_.InitWithFeatures(
-        {::features::kExperimentalAccessibilityDictationOffline,
-         ash::features::kOnDeviceSpeechRecognition},
-        {});
+        {ash::features::kOnDeviceSpeechRecognition}, {});
     MixinBasedInProcessBrowserTest::SetUpCommandLine(command_line);
   }
 
@@ -1152,18 +1150,12 @@
     std::vector<base::Feature> enabled_features;
     std::vector<base::Feature> disabled_features;
     if (GetParam() == DictationDialogTestVariant::kOfflineEnabledAndAvailable) {
-      enabled_features.push_back(
-          ::features::kExperimentalAccessibilityDictationOffline);
       enabled_features.push_back(ash::features::kOnDeviceSpeechRecognition);
     } else if (GetParam() ==
                DictationDialogTestVariant::kOfflineEnabledAndUnavailable) {
-      // Offline dictation is enabled but SODA isn't available on this device.
-      enabled_features.push_back(
-          ::features::kExperimentalAccessibilityDictationOffline);
+      // SODA isn't available on this device.
       disabled_features.push_back(ash::features::kOnDeviceSpeechRecognition);
     } else {
-      disabled_features.push_back(
-          ::features::kExperimentalAccessibilityDictationOffline);
       disabled_features.push_back(ash::features::kOnDeviceSpeechRecognition);
     }
     scoped_feature_list_.InitWithFeatures(enabled_features, disabled_features);
diff --git a/chrome/browser/ash/accessibility/dictation.cc b/chrome/browser/ash/accessibility/dictation.cc
index aee9e7c..e18ea35 100644
--- a/chrome/browser/ash/accessibility/dictation.cc
+++ b/chrome/browser/ash/accessibility/dictation.cc
@@ -70,19 +70,13 @@
 }
 
 std::string GetUserLocale(Profile* profile) {
-  std::string locale;
-  if (features::IsExperimentalAccessibilityDictationOfflineEnabled()) {
-    // Get the user's chosen dictation locale from their preference in settings.
-    // This is guaranteed to be a supported locale and won't be empty, since
-    // the pref is set using DetermineDefaultSupportedLocale() as soon as
-    // Dictation is enabled, assuming that supported languages are never removed
-    // from this list.
-    locale =
-        profile->GetPrefs()->GetString(prefs::kAccessibilityDictationLocale);
-  } else {
-    locale = GetUserLangOrLocaleFromSystem(profile);
-  }
-
+  // Get the user's chosen dictation locale from their preference in settings.
+  // This is guaranteed to be a supported locale and won't be empty, since
+  // the pref is set using DetermineDefaultSupportedLocale() as soon as
+  // Dictation is enabled, assuming that supported languages are never removed
+  // from this list.
+  std::string locale =
+      profile->GetPrefs()->GetString(prefs::kAccessibilityDictationLocale);
   DCHECK(!locale.empty());
   return locale;
 }
@@ -210,7 +204,7 @@
     // By default these languages are not supported offline.
     supported_locales[locale] = LocaleData();
   }
-  if (features::IsDictationOfflineAvailableAndEnabled()) {
+  if (features::IsDictationOfflineAvailable()) {
     speech::SodaInstaller* soda_installer =
         speech::SodaInstaller::GetInstance();
     std::vector<std::string> offline_locales =
@@ -269,7 +263,7 @@
   base::UmaHistogramSparse("Accessibility.CrosDictation.Language",
                            base::HashMetricName(locale));
 
-  if (features::IsDictationOfflineAvailableAndEnabled() &&
+  if (features::IsDictationOfflineAvailable() &&
       speech::SodaInstaller::GetInstance()->IsSodaDownloading(
           speech::GetLanguageCode(locale))) {
     // Don't allow Dictation to be used while SODA is downloading.
@@ -278,7 +272,7 @@
     return false;
   }
 
-  if (features::IsDictationOfflineAvailableAndEnabled() &&
+  if (features::IsDictationOfflineAvailable() &&
       OnDeviceSpeechRecognizer::IsOnDeviceSpeechRecognizerAvailable(locale)) {
     // On-device recognition is behind a flag and then only available if
     // SODA is installed on-device.
diff --git a/chrome/browser/ash/accessibility/dictation_browsertest.cc b/chrome/browser/ash/accessibility/dictation_browsertest.cc
index 09ff5c2..41565507 100644
--- a/chrome/browser/ash/accessibility/dictation_browsertest.cc
+++ b/chrome/browser/ash/accessibility/dictation_browsertest.cc
@@ -279,13 +279,6 @@
         test_helper_.GetEnabledFeatures();
     std::vector<base::Feature> disabled_features =
         test_helper_.GetDisabledFeatures();
-    if (GetParam() == speech::SpeechRecognitionType::kOnDevice) {
-      enabled_features.push_back(
-          ::features::kExperimentalAccessibilityDictationOffline);
-    } else {
-      disabled_features.push_back(
-          ::features::kExperimentalAccessibilityDictationOffline);
-    }
     scoped_feature_list_.InitWithFeatures(enabled_features, disabled_features);
 
     InProcessBrowserTest::SetUpCommandLine(command_line);
@@ -680,7 +673,7 @@
   metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
 
   // Ensure that we recorded the correct locale.
-  const std::string locale = on_device ? "en-US" : "en";
+  const std::string locale = "en-US";
   histogram_tester_.ExpectUniqueSample(/*name=*/kLocaleMetric,
                                        /*sample=*/base::HashMetricName(locale),
                                        /*expected_bucket_count=*/1);
@@ -690,22 +683,22 @@
     histogram_tester_.ExpectUniqueSample(/*name=*/kOnDeviceSpeechMetric,
                                          /*sample=*/true,
                                          /*expected_bucket_count=*/1);
-    ASSERT_EQ(1,
+    ASSERT_EQ(1u,
               histogram_tester_.GetAllSamples(kOnDeviceListeningDurationMetric)
                   .size());
     // Ensure there are no metrics for the other type of speech recognition.
-    ASSERT_EQ(0,
+    ASSERT_EQ(0u,
               histogram_tester_.GetAllSamples(kNetworkListeningDurationMetric)
                   .size());
   } else {
     histogram_tester_.ExpectUniqueSample(/*name=*/kOnDeviceSpeechMetric,
                                          /*sample=*/false,
                                          /*expected_bucket_count=*/1);
-    ASSERT_EQ(1,
+    ASSERT_EQ(1u,
               histogram_tester_.GetAllSamples(kNetworkListeningDurationMetric)
                   .size());
     // Ensure there are no metrics for the other type of speech recognition.
-    ASSERT_EQ(0,
+    ASSERT_EQ(0u,
               histogram_tester_.GetAllSamples(kOnDeviceListeningDurationMetric)
                   .size());
   }
@@ -1072,22 +1065,22 @@
     histogram_tester_.ExpectUniqueSample(/*name=*/kOnDeviceSpeechMetric,
                                          /*sample=*/true,
                                          /*expected_bucket_count=*/1);
-    ASSERT_EQ(1,
+    ASSERT_EQ(1u,
               histogram_tester_.GetAllSamples(kOnDeviceListeningDurationMetric)
                   .size());
     // Ensure there are no metrics for the other type of speech recognition.
-    ASSERT_EQ(0,
+    ASSERT_EQ(0u,
               histogram_tester_.GetAllSamples(kNetworkListeningDurationMetric)
                   .size());
   } else {
     histogram_tester_.ExpectUniqueSample(/*name=*/kOnDeviceSpeechMetric,
                                          /*sample=*/false,
                                          /*expected_bucket_count=*/1);
-    ASSERT_EQ(1,
+    ASSERT_EQ(1u,
               histogram_tester_.GetAllSamples(kNetworkListeningDurationMetric)
                   .size());
     // Ensure there are no metrics for the other type of speech recognition.
-    ASSERT_EQ(0,
+    ASSERT_EQ(0u,
               histogram_tester_.GetAllSamples(kOnDeviceListeningDurationMetric)
                   .size());
   }
diff --git a/chrome/browser/ash/arc/accessibility/arc_accessibility_helper_bridge_browsertest.cc b/chrome/browser/ash/arc/accessibility/arc_accessibility_helper_bridge_browsertest.cc
index 2cbb3d0..092530f 100644
--- a/chrome/browser/ash/arc/accessibility/arc_accessibility_helper_bridge_browsertest.cc
+++ b/chrome/browser/ash/arc/accessibility/arc_accessibility_helper_bridge_browsertest.cc
@@ -344,7 +344,7 @@
   ArcAccessibilityHelperBridge* bridge =
       ArcAccessibilityHelperBridge::GetForBrowserContext(browser()->profile());
   auto& tree_map = bridge->trees_for_test();
-  ASSERT_EQ(1, tree_map.size());
+  ASSERT_EQ(1u, tree_map.size());
   AXTreeSourceArc* tree_source = tree_map.begin()->second.get();
   MockAutomationEventRouter router;
   tree_source->set_automation_event_router_for_test(&router);
@@ -384,7 +384,7 @@
   ArcAccessibilityHelperBridge* bridge =
       ArcAccessibilityHelperBridge::GetForBrowserContext(browser()->profile());
   auto& tree_map = bridge->trees_for_test();
-  ASSERT_EQ(1, tree_map.size());
+  ASSERT_EQ(1u, tree_map.size());
   AXTreeSourceArc* tree_source = tree_map.begin()->second.get();
   MockAutomationEventRouter router;
   tree_source->set_automation_event_router_for_test(&router);
diff --git a/chrome/browser/ash/arc/auth/arc_auth_service_browsertest.cc b/chrome/browser/ash/arc/auth/arc_auth_service_browsertest.cc
index 87a754cd..e0a7f52 100644
--- a/chrome/browser/ash/arc/auth/arc_auth_service_browsertest.cc
+++ b/chrome/browser/ash/arc/auth/arc_auth_service_browsertest.cc
@@ -901,7 +901,7 @@
   WaitForGoogleAccountsInArcCallback();
 
   EXPECT_TRUE(arc_google_accounts_callback_called());
-  ASSERT_EQ(1UL, arc_google_accounts().size());
+  ASSERT_EQ(1u, arc_google_accounts().size());
   EXPECT_EQ(kFakeUserName, arc_google_accounts()[0]->email);
   EXPECT_EQ(signin::GetTestGaiaIdForEmail(kFakeUserName),
             arc_google_accounts()[0]->gaia_id);
@@ -924,7 +924,7 @@
   WaitForInstanceReady(arc_bridge_service().auth());
 
   EXPECT_TRUE(arc_google_accounts_callback_called());
-  ASSERT_EQ(1UL, arc_google_accounts().size());
+  ASSERT_EQ(1u, arc_google_accounts().size());
   EXPECT_EQ(kFakeUserName, arc_google_accounts()[0]->email);
   EXPECT_EQ(signin::GetTestGaiaIdForEmail(kFakeUserName),
             arc_google_accounts()[0]->gaia_id);
@@ -944,7 +944,7 @@
   if (IsArcAccountRestrictionsEnabled()) {
     // 1 SetAccounts() call for the Primary account.
     EXPECT_EQ(1, initial_num_set_accounts_calls);
-    EXPECT_EQ(1, auth_instance().last_set_accounts_list()->size());
+    EXPECT_EQ(1u, auth_instance().last_set_accounts_list()->size());
     EXPECT_EQ(kFakeUserName,
               (*auth_instance().last_set_accounts_list())[0]->email);
     EXPECT_EQ(0, initial_num_account_upserted_calls);
@@ -968,7 +968,7 @@
   if (IsArcAccountRestrictionsEnabled()) {
     // 1 SetAccounts() call for the Primary account.
     EXPECT_EQ(1, auth_instance().num_set_accounts_calls());
-    EXPECT_EQ(1, auth_instance().last_set_accounts_list()->size());
+    EXPECT_EQ(1u, auth_instance().last_set_accounts_list()->size());
     EXPECT_EQ(kFakeUserName,
               (*auth_instance().last_set_accounts_list())[0]->email);
     // 1 call for the Secondary Account.
@@ -990,7 +990,7 @@
   if (IsArcAccountRestrictionsEnabled()) {
     // 1 SetAccounts() call for the Primary account.
     EXPECT_EQ(1, auth_instance().num_set_accounts_calls());
-    EXPECT_EQ(1, auth_instance().last_set_accounts_list()->size());
+    EXPECT_EQ(1u, auth_instance().last_set_accounts_list()->size());
     EXPECT_EQ(kFakeUserName,
               (*auth_instance().last_set_accounts_list())[0]->email);
     // 1 call for the Secondary Account.
@@ -1018,7 +1018,7 @@
   if (IsArcAccountRestrictionsEnabled()) {
     // 1 SetAccounts() call with empty list of accounts.
     EXPECT_EQ(1, auth_instance().num_set_accounts_calls());
-    EXPECT_EQ(0, auth_instance().last_set_accounts_list()->size());
+    EXPECT_EQ(0u, auth_instance().last_set_accounts_list()->size());
   }
 
   // 1 call for the Secondary Account.
@@ -1042,7 +1042,7 @@
   const int initial_num_calls = auth_instance().num_account_upserted_calls();
   // 1 SetAccounts() call for the Primary account.
   EXPECT_EQ(1, auth_instance().num_set_accounts_calls());
-  EXPECT_EQ(1, auth_instance().last_set_accounts_list()->size());
+  EXPECT_EQ(1u, auth_instance().last_set_accounts_list()->size());
   EXPECT_EQ(kFakeUserName,
             (*auth_instance().last_set_accounts_list())[0]->email);
   // 1 call for the Secondary Account.
@@ -1414,7 +1414,7 @@
   if (IsArcAccountRestrictionsEnabled()) {
     // 1 SetAccounts() call for the Primary account.
     EXPECT_EQ(1, auth_instance().num_set_accounts_calls());
-    EXPECT_EQ(1, auth_instance().last_set_accounts_list()->size());
+    EXPECT_EQ(1u, auth_instance().last_set_accounts_list()->size());
     EXPECT_EQ(kFakeUserName,
               (*auth_instance().last_set_accounts_list())[0]->email);
     // 1 call for the Secondary Account.
@@ -1434,7 +1434,7 @@
   if (IsArcAccountRestrictionsEnabled()) {
     // 1 SetAccounts() call for the Primary account.
     EXPECT_EQ(1, auth_instance().num_set_accounts_calls());
-    EXPECT_EQ(1, auth_instance().last_set_accounts_list()->size());
+    EXPECT_EQ(1u, auth_instance().last_set_accounts_list()->size());
     EXPECT_EQ(kFakeUserName,
               (*auth_instance().last_set_accounts_list())[0]->email);
     // 1 call for the Secondary Account.
diff --git a/chrome/browser/ash/borealis/borealis_installer_impl.cc b/chrome/browser/ash/borealis/borealis_installer_impl.cc
index f2659157..40b515f 100644
--- a/chrome/browser/ash/borealis/borealis_installer_impl.cc
+++ b/chrome/browser/ash/borealis/borealis_installer_impl.cc
@@ -22,6 +22,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chromeos/dbus/concierge/concierge_client.h"
 #include "chromeos/dbus/concierge/concierge_service.pb.h"
+#include "chromeos/dbus/dlcservice/dlcservice.pb.h"
 #include "chromeos/dbus/vm_applications/apps.pb.h"
 #include "components/prefs/pref_service.h"
 #include "content/public/browser/browser_thread.h"
@@ -84,8 +85,10 @@
       return;
     }
     SetState(InstallingState::kInstallingDlc);
+    dlcservice::InstallRequest install_request;
+    install_request.set_id(kBorealisDlcName);
     chromeos::DlcserviceClient::Get()->Install(
-        kBorealisDlcName,
+        install_request,
         base::BindOnce(&Installation::OnDlcInstallationCompleted,
                        weak_factory_.GetWeakPtr()),
         base::BindRepeating(&Installation::OnDlcInstallationProgressUpdated,
diff --git a/chrome/browser/ash/borealis/borealis_task.cc b/chrome/browser/ash/borealis/borealis_task.cc
index d6a308d..c801412 100644
--- a/chrome/browser/ash/borealis/borealis_task.cc
+++ b/chrome/browser/ash/borealis/borealis_task.cc
@@ -30,6 +30,7 @@
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chromeos/dbus/concierge/concierge_service.pb.h"
+#include "chromeos/dbus/dlcservice/dlcservice.pb.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace borealis {
@@ -84,8 +85,10 @@
 void MountDlc::RunInternal(BorealisContext* context) {
   // TODO(b/172279567): Ensure the DLC is present before trying to install,
   // otherwise we will silently download borealis here.
+  dlcservice::InstallRequest install_request;
+  install_request.set_id(kBorealisDlcName);
   chromeos::DlcserviceClient::Get()->Install(
-      kBorealisDlcName,
+      install_request,
       base::BindOnce(&MountDlc::OnMountDlc, weak_factory_.GetWeakPtr(),
                      context),
       base::DoNothing());
diff --git a/chrome/browser/ash/cert_provisioning/cert_provisioning_worker_unittest.cc b/chrome/browser/ash/cert_provisioning/cert_provisioning_worker_unittest.cc
index 7357b43..5f994a4 100644
--- a/chrome/browser/ash/cert_provisioning/cert_provisioning_worker_unittest.cc
+++ b/chrome/browser/ash/cert_provisioning/cert_provisioning_worker_unittest.cc
@@ -120,7 +120,7 @@
       chromeos::AttestationClient::Get()
           ->GetTestInterface()
           ->delete_keys_history();
-  EXPECT_EQ(delete_keys_history.size(), 1);
+  EXPECT_EQ(delete_keys_history.size(), 1u);
   EXPECT_EQ(delete_keys_history[0].username().empty(),
             cert_scope != CertScope::kUser);
   EXPECT_EQ(delete_keys_history[0].key_label_match(),
diff --git a/chrome/browser/ash/crosapi/network_settings_service_ash_browsertest.cc b/chrome/browser/ash/crosapi/network_settings_service_ash_browsertest.cc
index a2035414..7c09bd47 100644
--- a/chrome/browser/ash/crosapi/network_settings_service_ash_browsertest.cc
+++ b/chrome/browser/ash/crosapi/network_settings_service_ash_browsertest.cc
@@ -196,7 +196,7 @@
   ASSERT_TRUE(observer_->proxy_config_->proxy_settings->is_manual());
   crosapi::mojom::ProxySettingsManualPtr manual =
       std::move(observer_->proxy_config_->proxy_settings->get_manual());
-  ASSERT_EQ(manual->http_proxies.size(), 1);
+  ASSERT_EQ(manual->http_proxies.size(), 1u);
   EXPECT_EQ(manual->http_proxies[0]->host, "proxyhost");
   EXPECT_EQ(manual->http_proxies[0]->port, 3128);
 }
diff --git a/chrome/browser/ash/crostini/termina_installer.cc b/chrome/browser/ash/crostini/termina_installer.cc
index 065c455..7a815a4 100644
--- a/chrome/browser/ash/crostini/termina_installer.cc
+++ b/chrome/browser/ash/crostini/termina_installer.cc
@@ -20,6 +20,7 @@
 #include "chrome/browser/ash/crostini/crostini_util.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/browser_process_platform_part_chromeos.h"
+#include "chromeos/dbus/dlcservice/dlcservice.pb.h"
 #include "content/public/browser/network_service_instance.h"
 #include "services/network/public/cpp/network_connection_tracker.h"
 #include "third_party/cros_system_api/dbus/service_constants.h"
@@ -101,8 +102,10 @@
 void TerminaInstaller::InstallDlc(
     base::OnceCallback<void(InstallResult)> callback,
     bool is_initial_install) {
+  dlcservice::InstallRequest install_request;
+  install_request.set_id(kCrostiniDlcName);
   chromeos::DlcserviceClient::Get()->Install(
-      kCrostiniDlcName,
+      install_request,
       base::BindOnce(&TerminaInstaller::OnInstallDlc,
                      weak_ptr_factory_.GetWeakPtr(), std::move(callback),
                      is_initial_install),
diff --git a/chrome/browser/ash/exo/chrome_data_exchange_delegate.cc b/chrome/browser/ash/exo/chrome_data_exchange_delegate.cc
index bed6655..026ce60 100644
--- a/chrome/browser/ash/exo/chrome_data_exchange_delegate.cc
+++ b/chrome/browser/ash/exo/chrome_data_exchange_delegate.cc
@@ -44,7 +44,6 @@
 #include "ui/base/data_transfer_policy/data_transfer_endpoint.h"
 #include "ui/base/dragdrop/os_exchange_data.h"
 #include "url/gurl.h"
-#include "url/origin.h"
 
 namespace ash {
 
@@ -420,8 +419,8 @@
   std::vector<ui::FileInfo> file_info;
   // We only promote 'fs/sources' custom data pickle to be filenames which can
   // be shared and read by clients if it came from the trusted FilesApp.
-  if (!source || !source->IsSameOriginWith(ui::DataTransferEndpoint(
-                     file_manager::util::GetFilesAppOrigin()))) {
+  if (!source || !source->IsSameURLWith(ui::DataTransferEndpoint(
+                     file_manager::util::GetFileManagerURL()))) {
     return file_info;
   }
 
diff --git a/chrome/browser/ash/exo/chrome_data_exchange_delegate_unittest.cc b/chrome/browser/ash/exo/chrome_data_exchange_delegate_unittest.cc
index 83a1b7f..8c32895 100644
--- a/chrome/browser/ash/exo/chrome_data_exchange_delegate_unittest.cc
+++ b/chrome/browser/ash/exo/chrome_data_exchange_delegate_unittest.cc
@@ -48,7 +48,6 @@
 #include "ui/base/data_transfer_policy/data_transfer_endpoint.h"
 #include "ui/base/dragdrop/os_exchange_data.h"
 #include "ui/gfx/geometry/rect.h"
-#include "url/origin.h"
 #include "url/url_constants.h"
 
 namespace ash {
@@ -496,7 +495,7 @@
           {{u"fs/tag", u"exo"}, {u"fs/sources", urls}}),
       &pickle);
 
-  ui::DataTransferEndpoint files_app(url::Origin::Create(file_manager_url));
+  ui::DataTransferEndpoint files_app(file_manager_url);
   std::vector<ui::FileInfo> file_info =
       data_exchange_delegate.ParseFileSystemSources(&files_app, pickle);
   EXPECT_EQ(2u, file_info.size());
diff --git a/chrome/browser/ash/guest_os/guest_os_registry_service_icon_browsertest.cc b/chrome/browser/ash/guest_os/guest_os_registry_service_icon_browsertest.cc
index a34478b..9be5827f 100644
--- a/chrome/browser/ash/guest_os/guest_os_registry_service_icon_browsertest.cc
+++ b/chrome/browser/ash/guest_os/guest_os_registry_service_icon_browsertest.cc
@@ -66,7 +66,7 @@
               if (expect_loaded) {
                 EXPECT_FALSE(icon->is_placeholder_icon);
                 EXPECT_EQ(apps::IconType::kCompressed, icon->icon_type);
-                EXPECT_GT(icon->compressed.size(), 0);
+                EXPECT_GT(icon->compressed.size(), 0u);
               } else {
                 EXPECT_EQ(apps::IconType::kUnknown, icon->icon_type);
               }
diff --git a/chrome/browser/ash/login/login_browsertest.cc b/chrome/browser/ash/login/login_browsertest.cc
index 86b0bda..7e361fd9 100644
--- a/chrome/browser/ash/login/login_browsertest.cc
+++ b/chrome/browser/ash/login/login_browsertest.cc
@@ -413,7 +413,7 @@
   std::string email = managed_user_id_.GetUserEmail();
   std::vector<std::string> email_and_domain = base::SplitString(
       email, "@", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL);
-  ASSERT_EQ(email_and_domain.size(), 2);
+  ASSERT_EQ(email_and_domain.size(), 2u);
   std::string prefix = *(email_and_domain.begin());
 
   OobeScreenWaiter(GaiaView::kScreenId).Wait();
diff --git a/chrome/browser/ash/net/network_diagnostics/host_resolver_unittest.cc b/chrome/browser/ash/net/network_diagnostics/host_resolver_unittest.cc
index d23d5a2..f003e51 100644
--- a/chrome/browser/ash/net/network_diagnostics/host_resolver_unittest.cc
+++ b/chrome/browser/ash/net/network_diagnostics/host_resolver_unittest.cc
@@ -74,7 +74,7 @@
   EXPECT_EQ(resolution_result.result, net::OK);
   EXPECT_EQ(resolution_result.resolve_error_info,
             net::ResolveErrorInfo(net::OK));
-  EXPECT_EQ(resolution_result.resolved_addresses.value().size(), 1);
+  EXPECT_EQ(resolution_result.resolved_addresses.value().size(), 1u);
   EXPECT_EQ(resolution_result.resolved_addresses.value().front(),
             address_list.front());
 }
diff --git a/chrome/browser/ash/net/network_health/network_health_unittest.cc b/chrome/browser/ash/net/network_health/network_health_unittest.cc
index 8cee8d14..7d81f36 100644
--- a/chrome/browser/ash/net/network_health/network_health_unittest.cc
+++ b/chrome/browser/ash/net/network_health/network_health_unittest.cc
@@ -516,7 +516,7 @@
 
   auto& network_health = network_health_.GetNetworkHealthState();
   auto& networks = network_health->networks;
-  ASSERT_EQ(1, networks.size());
+  ASSERT_EQ(1u, networks.size());
   auto& stats = networks[0]->signal_strength_stats;
   ASSERT_TRUE(stats);
   EXPECT_FLOAT_EQ(75.0, stats->average);
@@ -555,10 +555,10 @@
 
   auto& network_health = network_health_.GetNetworkHealthState();
   auto& networks = network_health->networks;
-  ASSERT_EQ(1, networks.size());
+  ASSERT_EQ(1u, networks.size());
   auto& stats = networks[0]->signal_strength_stats;
   ASSERT_TRUE(stats);
-  ASSERT_EQ(1, stats->samples.size());
+  ASSERT_EQ(1u, stats->samples.size());
   ASSERT_EQ(75, stats->samples[0]);
 }
 
diff --git a/chrome/browser/ash/note_taking_helper_unittest.cc b/chrome/browser/ash/note_taking_helper_unittest.cc
index 37045460..b9c2071 100644
--- a/chrome/browser/ash/note_taking_helper_unittest.cc
+++ b/chrome/browser/ash/note_taking_helper_unittest.cc
@@ -872,7 +872,7 @@
   app_info->note_taking_new_note_url = new_note_url;
   std::string app_id =
       web_app::test::InstallWebApp(profile(), std::move(app_info));
-  ASSERT_EQ(helper()->GetAvailableApps(profile()).size(), 1);
+  ASSERT_EQ(helper()->GetAvailableApps(profile()).size(), 1u);
 
   // Fire a "Create Note" action and check the app is launched.
   HistogramTester histogram_tester;
diff --git a/chrome/browser/ash/phonehub/phone_hub_manager_factory.cc b/chrome/browser/ash/phonehub/phone_hub_manager_factory.cc
index ae8fe39..617c092 100644
--- a/chrome/browser/ash/phonehub/phone_hub_manager_factory.cc
+++ b/chrome/browser/ash/phonehub/phone_hub_manager_factory.cc
@@ -5,8 +5,8 @@
 #include "chrome/browser/ash/phonehub/phone_hub_manager_factory.h"
 
 #include "ash/components/phonehub/camera_roll_manager_impl.h"
+#include "ash/components/phonehub/multidevice_feature_access_manager_impl.h"
 #include "ash/components/phonehub/multidevice_setup_state_updater.h"
-#include "ash/components/phonehub/notification_access_manager_impl.h"
 #include "ash/components/phonehub/onboarding_ui_tracker_impl.h"
 #include "ash/components/phonehub/phone_hub_manager_impl.h"
 #include "ash/components/phonehub/recent_apps_interaction_handler_impl.h"
@@ -166,7 +166,7 @@
     user_prefs::PrefRegistrySyncable* registry) {
   MultideviceSetupStateUpdater::RegisterPrefs(registry);
   CameraRollManagerImpl::RegisterPrefs(registry);
-  NotificationAccessManagerImpl::RegisterPrefs(registry);
+  MultideviceFeatureAccessManagerImpl::RegisterPrefs(registry);
   OnboardingUiTrackerImpl::RegisterPrefs(registry);
   ScreenLockManagerImpl::RegisterPrefs(registry);
   RecentAppsInteractionHandlerImpl::RegisterPrefs(registry);
diff --git a/chrome/browser/ash/plugin_vm/plugin_vm_installer.cc b/chrome/browser/ash/plugin_vm/plugin_vm_installer.cc
index 840b6448..19d0478b 100644
--- a/chrome/browser/ash/plugin_vm/plugin_vm_installer.cc
+++ b/chrome/browser/ash/plugin_vm/plugin_vm_installer.cc
@@ -29,6 +29,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_key.h"
 #include "chromeos/dbus/debug_daemon/debug_daemon_client.h"
+#include "chromeos/dbus/dlcservice/dlcservice.pb.h"
 #include "components/download/public/background_service/background_download_service.h"
 #include "components/download/public/background_service/download_metadata.h"
 #include "components/prefs/pref_service.h"
@@ -467,8 +468,10 @@
     return;
   }
 
+  dlcservice::InstallRequest install_request;
+  install_request.set_id(kPitaDlc);
   chromeos::DlcserviceClient::Get()->Install(
-      "pita",
+      install_request,
       base::BindOnce(&PluginVmInstaller::OnDlcDownloadCompleted,
                      weak_ptr_factory_.GetWeakPtr()),
       base::BindRepeating(&PluginVmInstaller::OnDlcDownloadProgressUpdated,
diff --git a/chrome/browser/ash/plugin_vm/plugin_vm_manager_impl.cc b/chrome/browser/ash/plugin_vm/plugin_vm_manager_impl.cc
index 030a41fd..768a97ab 100644
--- a/chrome/browser/ash/plugin_vm/plugin_vm_manager_impl.cc
+++ b/chrome/browser/ash/plugin_vm/plugin_vm_manager_impl.cc
@@ -24,6 +24,7 @@
 #include "chrome/grit/generated_resources.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/debug_daemon/debug_daemon_client.h"
+#include "chromeos/dbus/dlcservice/dlcservice.pb.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
 #include "components/keyed_service/content/browser_context_keyed_service_factory.h"
 #include "components/prefs/pref_service.h"
@@ -416,8 +417,10 @@
     base::OnceCallback<void(bool default_vm_exists)> success_callback,
     base::OnceClosure error_callback) {
   LOG_FUNCTION_CALL();
+  dlcservice::InstallRequest install_request;
+  install_request.set_id(kPitaDlc);
   chromeos::DlcserviceClient::Get()->Install(
-      "pita",
+      install_request,
       base::BindOnce(&PluginVmManagerImpl::OnInstallPluginVmDlc,
                      weak_ptr_factory_.GetWeakPtr(),
                      std::move(success_callback), std::move(error_callback)),
diff --git a/chrome/browser/ash/plugin_vm/plugin_vm_util.cc b/chrome/browser/ash/plugin_vm/plugin_vm_util.cc
index 052ccb80..61a0736b 100644
--- a/chrome/browser/ash/plugin_vm/plugin_vm_util.cc
+++ b/chrome/browser/ash/plugin_vm/plugin_vm_util.cc
@@ -33,6 +33,7 @@
 
 namespace plugin_vm {
 
+const char kPitaDlc[] = "pita";
 const char kPluginVmShelfAppId[] = "lgjpclljbbmphhnalkeplcmnjpfmmaek";
 const char kPluginVmName[] = "PvmDefault";
 const char kChromeOSBaseDirectoryDisplayText[] = "Network \u203a ChromeOS";
diff --git a/chrome/browser/ash/plugin_vm/plugin_vm_util.h b/chrome/browser/ash/plugin_vm/plugin_vm_util.h
index d3489ee..1f865c2 100644
--- a/chrome/browser/ash/plugin_vm/plugin_vm_util.h
+++ b/chrome/browser/ash/plugin_vm/plugin_vm_util.h
@@ -26,6 +26,9 @@
 
 class PluginVmPolicySubscription;
 
+// Name of the pita DLC.
+extern const char kPitaDlc[];
+
 // This is used by both the Plugin VM app and its installer.
 // Generated as crx_file::id_util::GenerateId("org.chromium.plugin_vm");
 extern const char kPluginVmShelfAppId[];
diff --git a/chrome/browser/ash/policy/status_collector/device_status_collector.cc b/chrome/browser/ash/policy/status_collector/device_status_collector.cc
index 400c0d71..47e8f28 100644
--- a/chrome/browser/ash/policy/status_collector/device_status_collector.cc
+++ b/chrome/browser/ash/policy/status_collector/device_status_collector.cc
@@ -2974,7 +2974,7 @@
   return report_power_status_ || report_storage_status_ ||
          report_audio_status_ || report_board_status_ || report_memory_info_ ||
          report_cpu_info_ || report_backlight_info_ || report_bluetooth_info_ ||
-         report_fan_info_ || report_vpd_info_ || report_system_info_ ||
+         report_fan_info_ || report_vpd_info_ || report_system_info_ || report_boot_mode_ ||
          report_version_info_;
 }
 bool DeviceStatusCollector::IsReportingUsers() const {
diff --git a/chrome/browser/ash/scanning/scan_service_unittest.cc b/chrome/browser/ash/scanning/scan_service_unittest.cc
index bb4098aa..fc49c2a0 100644
--- a/chrome/browser/ash/scanning/scan_service_unittest.cc
+++ b/chrome/browser/ash/scanning/scan_service_unittest.cc
@@ -825,7 +825,7 @@
 
   mojo_ipc::ScanSettings settings = CreateScanSettings(
       scanned_files_mount_->GetRootPath(), mojo_ipc::FileType::kPdf);
-  int new_page_index = 0;
+  uint32_t new_page_index = 0;
 
   // Scan the first page without completing the scan.
   EXPECT_TRUE(StartMultiPageScan(scanners[0]->id, settings.Clone()));
@@ -880,7 +880,7 @@
   EXPECT_EQ(mojo_ipc::ScanResult::kUnknownError,
             fake_scan_job_observer_.multi_page_scan_result());
   EXPECT_TRUE(fake_scan_job_observer_.scanned_file_paths().empty());
-  EXPECT_EQ(0, fake_scan_job_observer_.new_page_index());
+  EXPECT_EQ(0u, fake_scan_job_observer_.new_page_index());
 
   // Set scan data to empty vector in FakeLorgnetteScannerManager so the next
   // scan will fail.
@@ -921,7 +921,7 @@
   EXPECT_EQ(mojo_ipc::ScanResult::kUnknownError,
             fake_scan_job_observer_.multi_page_scan_result());
   EXPECT_TRUE(fake_scan_job_observer_.scanned_file_paths().empty());
-  EXPECT_EQ(0, fake_scan_job_observer_.new_page_index());
+  EXPECT_EQ(0u, fake_scan_job_observer_.new_page_index());
 
   // The second attempt should fail.
   EXPECT_FALSE(StartMultiPageScan(scanners[0]->id, settings.Clone()));
@@ -941,7 +941,7 @@
 
   mojo_ipc::ScanSettings settings = CreateScanSettings(
       scanned_files_mount_->GetRootPath(), mojo_ipc::FileType::kPdf);
-  int new_page_index = 0;
+  uint32_t new_page_index = 0;
 
   const std::string first_scanned_image = CreatePng(/*alpha=*/1);
   const std::vector<std::string> first_scan_data = {first_scanned_image};
@@ -988,7 +988,7 @@
 
   mojo_ipc::ScanSettings settings = CreateScanSettings(
       scanned_files_mount_->GetRootPath(), mojo_ipc::FileType::kPdf);
-  int new_page_index = 0;
+  uint32_t new_page_index = 0;
 
   const std::string first_scanned_image = CreatePng(/*alpha=*/1);
   const std::vector<std::string> first_scan_data = {first_scanned_image};
@@ -1014,7 +1014,7 @@
 
   const std::vector<std::string> scanned_images =
       scan_service_->GetScannedImagesForTesting();
-  EXPECT_EQ(2, scanned_images.size());
+  EXPECT_EQ(2u, scanned_images.size());
   EXPECT_EQ(first_scanned_image, scanned_images[0]);
   EXPECT_EQ(third_scanned_image, scanned_images[1]);
 
@@ -1044,7 +1044,7 @@
 
   mojo_ipc::ScanSettings settings = CreateScanSettings(
       scanned_files_mount_->GetRootPath(), mojo_ipc::FileType::kPdf);
-  int new_page_index = 0;
+  uint32_t new_page_index = 0;
 
   EXPECT_TRUE(StartMultiPageScan(scanners[0]->id, settings.Clone()));
   EXPECT_EQ(new_page_index++, fake_scan_job_observer_.new_page_index());
@@ -1061,7 +1061,7 @@
 
   const std::vector<std::string> scanned_images =
       scan_service_->GetScannedImagesForTesting();
-  EXPECT_EQ(1, scanned_images.size());
+  EXPECT_EQ(1u, scanned_images.size());
 
   // Expect 1 record of the Scanning.NumPagesScanned metric in the 1 page
   // scanned bucket.
@@ -1087,7 +1087,7 @@
 
   mojo_ipc::ScanSettings settings = CreateScanSettings(
       scanned_files_mount_->GetRootPath(), mojo_ipc::FileType::kPdf);
-  int new_page_index = 0;
+  uint32_t new_page_index = 0;
 
   const std::string first_scanned_image = CreatePng(/*alpha=*/1);
   const std::vector<std::string> first_scan_data = {first_scanned_image};
@@ -1101,12 +1101,12 @@
       rescanned_scanned_image};
   fake_lorgnette_scanner_manager_.SetScanResponse(rescanned_scan_data);
   EXPECT_TRUE(RescanPage(scanners[0]->id, settings.Clone(), /*page_index=*/0));
-  EXPECT_EQ(0, fake_scan_job_observer_.new_page_index());
+  EXPECT_EQ(0u, fake_scan_job_observer_.new_page_index());
   CompleteMultiPageScan();
 
   const std::vector<std::string> scanned_images =
       scan_service_->GetScannedImagesForTesting();
-  EXPECT_EQ(1, scanned_images.size());
+  EXPECT_EQ(1u, scanned_images.size());
   EXPECT_EQ(rescanned_scanned_image, scanned_images[0]);
 
   // Expect 1 record of the Scanning.NumPagesScanned metric in the 1 pages
@@ -1133,7 +1133,7 @@
 
   mojo_ipc::ScanSettings settings = CreateScanSettings(
       scanned_files_mount_->GetRootPath(), mojo_ipc::FileType::kPdf);
-  int new_page_index = 0;
+  uint32_t new_page_index = 0;
 
   const std::string first_scanned_image = CreatePng(/*alpha=*/1);
   const std::vector<std::string> first_scan_data = {first_scanned_image};
@@ -1159,12 +1159,12 @@
       rescanned_scanned_image};
   fake_lorgnette_scanner_manager_.SetScanResponse(rescanned_scan_data);
   EXPECT_TRUE(RescanPage(scanners[0]->id, settings.Clone(), /*page_index=*/1));
-  EXPECT_EQ(1, fake_scan_job_observer_.new_page_index());
+  EXPECT_EQ(1u, fake_scan_job_observer_.new_page_index());
   CompleteMultiPageScan();
 
   const std::vector<std::string> scanned_images =
       scan_service_->GetScannedImagesForTesting();
-  EXPECT_EQ(3, scanned_images.size());
+  EXPECT_EQ(3u, scanned_images.size());
   EXPECT_EQ(first_scanned_image, scanned_images[0]);
   EXPECT_EQ(rescanned_scanned_image, scanned_images[1]);
   EXPECT_EQ(third_scanned_image, scanned_images[2]);
diff --git a/chrome/browser/ash/smb_client/smbfs_share_unittest.cc b/chrome/browser/ash/smb_client/smbfs_share_unittest.cc
index 9e8e648..b665a05 100644
--- a/chrome/browser/ash/smb_client/smbfs_share_unittest.cc
+++ b/chrome/browser/ash/smb_client/smbfs_share_unittest.cc
@@ -455,7 +455,7 @@
   std::vector<std::string> tokens1 =
       base::SplitString(hash_input1, kMountIdHashSeparator,
                         base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL);
-  EXPECT_EQ(tokens1.size(), 5);
+  EXPECT_EQ(tokens1.size(), 5u);
   EXPECT_EQ(tokens1[0], profile_user_hash);
   EXPECT_EQ(tokens1[1], SmbUrl(kSharePath).ToString());
   EXPECT_EQ(tokens1[2], "0" /* kerberos */);
@@ -473,7 +473,7 @@
   std::vector<std::string> tokens2 =
       base::SplitString(hash_input2, kMountIdHashSeparator,
                         base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL);
-  EXPECT_EQ(tokens2.size(), 5);
+  EXPECT_EQ(tokens2.size(), 5u);
   EXPECT_EQ(tokens2[0], profile_user_hash);
   EXPECT_EQ(tokens2[1], SmbUrl(kSharePath2).ToString());
   EXPECT_EQ(tokens2[2], "1" /* kerberos */);
@@ -497,8 +497,8 @@
   EXPECT_TRUE(mount_id1.compare(mount_id2));
 
   // Check: String is 64 characters long (SHA256 encoded as hex).
-  EXPECT_EQ(mount_id1.size(), 64);
-  EXPECT_EQ(mount_id2.size(), 64);
+  EXPECT_EQ(mount_id1.size(), 64u);
+  EXPECT_EQ(mount_id2.size(), 64u);
 }
 
 }  // namespace smb_client
diff --git a/chrome/browser/ash/web_applications/personalization_app/personalization_app_ambient_provider_impl_unittest.cc b/chrome/browser/ash/web_applications/personalization_app/personalization_app_ambient_provider_impl_unittest.cc
index 50029b0..be1910e5 100644
--- a/chrome/browser/ash/web_applications/personalization_app/personalization_app_ambient_provider_impl_unittest.cc
+++ b/chrome/browser/ash/web_applications/personalization_app/personalization_app_ambient_provider_impl_unittest.cc
@@ -344,7 +344,7 @@
   auto albums = ObservedAlbums();
   // The fake albums are set in FakeAmbientBackendControllerImpl. Hidden setting
   // will be sent to JS side.
-  EXPECT_EQ(4, albums.size());
+  EXPECT_EQ(4u, albums.size());
 }
 
 TEST_F(PersonalizationAppAmbientProviderImplTest,
diff --git a/chrome/browser/autofill/autofill_interactive_uitest.cc b/chrome/browser/autofill/autofill_interactive_uitest.cc
index ae6ec3c..21acebb 100644
--- a/chrome/browser/autofill/autofill_interactive_uitest.cc
+++ b/chrome/browser/autofill/autofill_interactive_uitest.cc
@@ -3600,9 +3600,16 @@
 
 // Ensure that autofill suggestions are properly read out via ChromeVox.
 // This is a regressions test for crbug.com/1208913.
-// TODO(https://crbug.com/1294266): Check back if flakiness is fixed now.
+// TODO(https://crbug.com/1294266): Flaky on ChromeOS
+#if BUILDFLAG(IS_CHROMEOS)
+#define MAYBE_TestNotificationOfAutofillDropdown \
+  DISABLED_TestNotificationOfAutofillDropdown
+#else
+#define MAYBE_TestNotificationOfAutofillDropdown \
+  TestNotificationOfAutofillDropdown
+#endif
 IN_PROC_BROWSER_TEST_F(AutofillInteractiveTestChromeVox,
-                       TestNotificationOfAutofillDropdown) {
+                       MAYBE_TestNotificationOfAutofillDropdown) {
   CreateTestProfile();
   SetTestUrlResponse(kTestShippingFormString);
   ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetTestUrl()));
diff --git a/chrome/browser/captive_portal/captive_portal_browsertest.cc b/chrome/browser/captive_portal/captive_portal_browsertest.cc
index 95c9e83..41695d7 100644
--- a/chrome/browser/captive_portal/captive_portal_browsertest.cc
+++ b/chrome/browser/captive_portal/captive_portal_browsertest.cc
@@ -2082,6 +2082,47 @@
             GetInterstitialType(broken_tab_contents));
 }
 
+// A cert error triggers a captive portal check and results in opening a login
+// tab; that login tab should not itself show a captive portal interstitial.
+IN_PROC_BROWSER_TEST_F(CaptivePortalBrowserTest,
+                       CertErrorOnCaptivePortalLoginShowsSSLErrorInterstitial) {
+  net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
+  https_server.SetSSLConfig(net::EmbeddedTestServer::CERT_MISMATCHED_NAME);
+  https_server.ServeFilesFromSourceDirectory(GetChromeTestDataDir());
+  ASSERT_TRUE(https_server.Start());
+  SSLErrorHandler::SetOSReportsCaptivePortalForTesting(true);
+
+  TabStripModel* tab_strip_model = browser()->tab_strip_model();
+  WebContents* broken_tab_contents = tab_strip_model->GetActiveWebContents();
+
+  // The path does not matter.
+  GURL cert_error_url = https_server.GetURL(kTestServerLoginPath);
+
+  // Cause an interstitial to be loaded.
+  FastErrorBehindCaptivePortal(browser(), true /* expect_open_login_tab */,
+                               false /* expect_new_login_browser */,
+                               cert_error_url);
+  EXPECT_EQ(CaptivePortalBlockingPage::kTypeForTesting,
+            GetInterstitialType(broken_tab_contents));
+
+  WebContents* login_tab_contents = tab_strip_model->GetActiveWebContents();
+  int login_tab_index = tab_strip_model->active_index();
+  EXPECT_EQ(1, login_tab_index);
+  EXPECT_TRUE(IsLoginTab(login_tab_contents));
+
+  // Navigate the Login tab to a cert error. In the real world, the captive
+  // portal might take the user to a Login page with a bad certificate.
+  ui_test_utils::NavigateToURLWithDisposition(
+      browser(), cert_error_url, WindowOpenDisposition::CURRENT_TAB,
+      ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP);
+  EXPECT_TRUE(IsLoginTab(login_tab_contents));
+
+  // Ensure that the Login tab is showing a cert error interstitial and not a
+  // captive portal interstitial.
+  EXPECT_EQ(SSLBlockingPage::kTypeForTesting,
+            GetInterstitialType(login_tab_contents));
+}
+
 // Tests this scenario:
 // - Portal probe requests are ignored, so that no captive portal result can
 //   arrive.
diff --git a/chrome/browser/chromeos/extensions/file_manager/system_notification_manager_unittest.cc b/chrome/browser/chromeos/extensions/file_manager/system_notification_manager_unittest.cc
index dba631c..d8f4fd5 100644
--- a/chrome/browser/chromeos/extensions/file_manager/system_notification_manager_unittest.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/system_notification_manager_unittest.cc
@@ -297,7 +297,7 @@
           &SystemNotificationManagerTest::GetNotificationsCallback,
           weak_ptr_factory_.GetWeakPtr()));
   // Check: We have one notification.
-  ASSERT_EQ(1, notification_count);
+  ASSERT_EQ(1u, notification_count);
   // Get the strings for the displayed notification.
   TestNotificationStrings notification_strings;
   notification_strings =
@@ -327,7 +327,7 @@
           &SystemNotificationManagerTest::GetNotificationsCallback,
           weak_ptr_factory_.GetWeakPtr()));
   // Check: We have one notification.
-  ASSERT_EQ(1, notification_count);
+  ASSERT_EQ(1u, notification_count);
   // Get the strings for the displayed notification.
   TestNotificationStrings notification_strings;
   notification_strings =
@@ -351,7 +351,7 @@
           &SystemNotificationManagerTest::GetNotificationsCallback,
           weak_ptr_factory_.GetWeakPtr()));
   // Check: We have one notification.
-  ASSERT_EQ(1, notification_count);
+  ASSERT_EQ(1u, notification_count);
   // Get the strings for the displayed notification.
   TestNotificationStrings notification_strings;
   notification_strings =
@@ -376,7 +376,7 @@
           &SystemNotificationManagerTest::GetNotificationsCallback,
           weak_ptr_factory_.GetWeakPtr()));
   // Check: We have one notification.
-  ASSERT_EQ(1, notification_count);
+  ASSERT_EQ(1u, notification_count);
   // Get the strings for the displayed notification.
   TestNotificationStrings notification_strings;
   notification_strings =
@@ -403,7 +403,7 @@
           &SystemNotificationManagerTest::GetNotificationsCallback,
           weak_ptr_factory_.GetWeakPtr()));
   // Check: We have one notification.
-  ASSERT_EQ(1, notification_count);
+  ASSERT_EQ(1u, notification_count);
   // Get the strings for the displayed notification.
   TestNotificationStrings notification_strings;
   notification_strings =
@@ -428,7 +428,7 @@
           &SystemNotificationManagerTest::GetNotificationsCallback,
           weak_ptr_factory_.GetWeakPtr()));
   // Check: We have one notification.
-  ASSERT_EQ(1, notification_count);
+  ASSERT_EQ(1u, notification_count);
   // Get the strings for the displayed notification.
   TestNotificationStrings notification_strings;
   notification_strings =
@@ -455,7 +455,7 @@
           &SystemNotificationManagerTest::GetNotificationsCallback,
           weak_ptr_factory_.GetWeakPtr()));
   // Check: We have one notification.
-  ASSERT_EQ(1, notification_count);
+  ASSERT_EQ(1u, notification_count);
   // Get the strings for the displayed notification.
   TestNotificationStrings notification_strings;
   notification_strings =
@@ -493,7 +493,7 @@
           &SystemNotificationManagerTest::GetNotificationsCallback,
           weak_ptr_factory_.GetWeakPtr()));
   // Check: We have one notification.
-  ASSERT_EQ(1, notification_count);
+  ASSERT_EQ(1u, notification_count);
   // Get the strings for the displayed notification.
   TestNotificationStrings notification_strings;
   notification_strings =
@@ -537,7 +537,7 @@
           &SystemNotificationManagerTest::GetNotificationsCallback,
           weak_ptr_factory_.GetWeakPtr()));
   // Check: We have one notification.
-  ASSERT_EQ(1, notification_count);
+  ASSERT_EQ(1u, notification_count);
   // Get the strings for the displayed notification.
   TestNotificationStrings notification_strings;
   notification_strings =
@@ -585,7 +585,7 @@
           &SystemNotificationManagerTest::GetNotificationsCallback,
           weak_ptr_factory_.GetWeakPtr()));
   // Check: We have one notification.
-  ASSERT_EQ(1, notification_count);
+  ASSERT_EQ(1u, notification_count);
   // Get the strings for the displayed notification.
   TestNotificationStrings notification_strings;
   notification_strings =
@@ -631,7 +631,7 @@
           &SystemNotificationManagerTest::GetNotificationsCallback,
           weak_ptr_factory_.GetWeakPtr()));
   // Check: We have one notification.
-  ASSERT_EQ(1, notification_count);
+  ASSERT_EQ(1u, notification_count);
   notification_platform_bridge->ClickButtonIndexById(
       kRemovableDeviceNotificationId,
       /*button_index=*/1);
@@ -667,7 +667,7 @@
           &SystemNotificationManagerTest::GetNotificationsCallback,
           weak_ptr_factory_.GetWeakPtr()));
   // Check: We have one notification.
-  ASSERT_EQ(1, notification_count);
+  ASSERT_EQ(1u, notification_count);
   // Get the strings for the displayed notification.
   TestNotificationStrings notification_strings;
   notification_strings =
@@ -713,7 +713,7 @@
           &SystemNotificationManagerTest::GetNotificationsCallback,
           weak_ptr_factory_.GetWeakPtr()));
   // Check: We have one notification.
-  ASSERT_EQ(1, notification_count);
+  ASSERT_EQ(1u, notification_count);
   // Get the strings for the displayed notification.
   TestNotificationStrings notification_strings;
   notification_strings =
@@ -752,7 +752,7 @@
           &SystemNotificationManagerTest::GetNotificationsCallback,
           weak_ptr_factory_.GetWeakPtr()));
   // Check: We have one notification.
-  ASSERT_EQ(1, notification_count);
+  ASSERT_EQ(1u, notification_count);
   // Get the strings for the displayed notification.
   TestNotificationStrings notification_strings;
   notification_strings =
@@ -794,7 +794,7 @@
           &SystemNotificationManagerTest::GetNotificationsCallback,
           weak_ptr_factory_.GetWeakPtr()));
   // Check: We have one notification.
-  ASSERT_EQ(1, notification_count);
+  ASSERT_EQ(1u, notification_count);
   // Get the strings for the displayed notification.
   TestNotificationStrings notification_strings =
       notification_platform_bridge->GetNotificationStringsById(
@@ -819,7 +819,7 @@
           &SystemNotificationManagerTest::GetNotificationsCallback,
           weak_ptr_factory_.GetWeakPtr()));
   // Check: We have two notifications.
-  ASSERT_EQ(2, notification_count);
+  ASSERT_EQ(2u, notification_count);
   // Get the strings for the displayed notification.
   notification_strings =
       notification_platform_bridge->GetNotificationStringsById(
@@ -869,7 +869,7 @@
           &SystemNotificationManagerTest::GetNotificationsCallback,
           weak_ptr_factory_.GetWeakPtr()));
   // Check: We have two notifications.
-  ASSERT_EQ(2, notification_count);
+  ASSERT_EQ(2u, notification_count);
   // Get the strings for the displayed notification.
   TestNotificationStrings notification_strings =
       notification_platform_bridge->GetNotificationStringsById(
@@ -911,7 +911,7 @@
           &SystemNotificationManagerTest::GetNotificationsCallback,
           weak_ptr_factory_.GetWeakPtr()));
   // Check: We have one notification.
-  ASSERT_EQ(1, notification_count);
+  ASSERT_EQ(1u, notification_count);
   // Get the strings for the displayed notification.
   TestNotificationStrings notification_strings;
   notification_strings =
@@ -923,7 +923,7 @@
   EXPECT_EQ(notification_strings.title, kRemovableDeviceTitle);
   EXPECT_EQ(notification_strings.message,
             u"Sorry, your external storage device could not be recognized.");
-  EXPECT_EQ(notification_strings.buttons.size(), 1);
+  EXPECT_EQ(notification_strings.buttons.size(), 1u);
   EXPECT_EQ(notification_strings.buttons[0], u"Format this device");
   histogram_tester.ExpectUniqueSample(
       kNotificationShowHistogramName,
@@ -955,7 +955,7 @@
           &SystemNotificationManagerTest::GetNotificationsCallback,
           weak_ptr_factory_.GetWeakPtr()));
   // Check: We have one notification.
-  ASSERT_EQ(1, notification_count);
+  ASSERT_EQ(1u, notification_count);
   // Get the strings for the displayed notification.
   TestNotificationStrings notification_strings;
   notification_strings =
@@ -967,7 +967,7 @@
   EXPECT_EQ(notification_strings.title, kRemovableDeviceTitle);
   EXPECT_EQ(notification_strings.message,
             u"Sorry, the device MyUSB could not be recognized.");
-  EXPECT_EQ(notification_strings.buttons.size(), 1);
+  EXPECT_EQ(notification_strings.buttons.size(), 1u);
   EXPECT_EQ(notification_strings.buttons[0], u"Format this device");
   histogram_tester.ExpectUniqueSample(
       kNotificationShowHistogramName,
@@ -1001,7 +1001,7 @@
           &SystemNotificationManagerTest::GetNotificationsCallback,
           weak_ptr_factory_.GetWeakPtr()));
   // Check: We have one notification.
-  ASSERT_EQ(1, notification_count);
+  ASSERT_EQ(1u, notification_count);
   // Get the strings for the displayed notification.
   TestNotificationStrings notification_strings;
   notification_strings =
@@ -1012,7 +1012,7 @@
   EXPECT_EQ(notification_strings.message,
             u"Sorry, your external storage device could not be recognized.");
   // Device is read-only, expect no buttons present.
-  EXPECT_EQ(notification_strings.buttons.size(), 0);
+  EXPECT_EQ(notification_strings.buttons.size(), 0u);
   histogram_tester.ExpectUniqueSample(
       kNotificationShowHistogramName,
       DeviceNotificationUmaType::DEVICE_FAIL_UNKNOWN_READONLY, 1);
@@ -1040,7 +1040,7 @@
           &SystemNotificationManagerTest::GetNotificationsCallback,
           weak_ptr_factory_.GetWeakPtr()));
   // Check: We have one notification.
-  ASSERT_EQ(1, notification_count);
+  ASSERT_EQ(1u, notification_count);
   // Get the strings for the displayed notification.
   TestNotificationStrings notification_strings;
   notification_strings =
@@ -1091,7 +1091,7 @@
       base::BindOnce(&SystemNotificationManagerTest::GetNotificationsCallback,
                      weak_ptr_factory_.GetWeakPtr()));
   // Check: We have zero notifications.
-  ASSERT_EQ(0, notification_count);
+  ASSERT_EQ(0u, notification_count);
 
   // Send progress event.
   status.type =
@@ -1102,7 +1102,7 @@
       base::BindOnce(&SystemNotificationManagerTest::GetNotificationsCallback,
                      weak_ptr_factory_.GetWeakPtr()));
   // Check: We have 1 notification.
-  ASSERT_EQ(1, notification_count);
+  ASSERT_EQ(1u, notification_count);
   // Get the strings for the displayed notification.
   TestNotificationStrings notification_strings;
   notification_strings =
@@ -1119,7 +1119,7 @@
       base::BindOnce(&SystemNotificationManagerTest::GetNotificationsCallback,
                      weak_ptr_factory_.GetWeakPtr()));
   // Check: We have zero notifications (copy progress has been closed).
-  ASSERT_EQ(0, notification_count);
+  ASSERT_EQ(0u, notification_count);
   // Start another copy that ends in error.
   copy_id = 2;
   copy_size = 100.0;
@@ -1137,7 +1137,7 @@
       base::BindOnce(&SystemNotificationManagerTest::GetNotificationsCallback,
                      weak_ptr_factory_.GetWeakPtr()));
   // Check: We have 1 notification.
-  ASSERT_EQ(1, notification_count);
+  ASSERT_EQ(1u, notification_count);
 
   // Send copy error event.
   status.type = file_manager_private::COPY_OR_MOVE_PROGRESS_STATUS_TYPE_ERROR;
@@ -1146,7 +1146,7 @@
       base::BindOnce(&SystemNotificationManagerTest::GetNotificationsCallback,
                      weak_ptr_factory_.GetWeakPtr()));
   // Check: We have zero notifications (copy progress has been closed).
-  ASSERT_EQ(0, notification_count);
+  ASSERT_EQ(0u, notification_count);
 }
 
 storage::FileSystemURL CreateFileSystemURL(std::string url) {
@@ -1262,7 +1262,7 @@
       base::BindOnce(&SystemNotificationManagerTest::GetNotificationsCallback,
                      weak_ptr_factory_.GetWeakPtr()));
   // Check: We have one notification.
-  ASSERT_EQ(1, notification_count);
+  ASSERT_EQ(1u, notification_count);
   const char* id = file_manager_private::ToString(sync_error.type);
   // Get the strings for the displayed notification.
   TestNotificationStrings notification_strings =
@@ -1288,7 +1288,7 @@
       base::BindOnce(&SystemNotificationManagerTest::GetNotificationsCallback,
                      weak_ptr_factory_.GetWeakPtr()));
   // Check: We have two notifications.
-  ASSERT_EQ(2, notification_count);
+  ASSERT_EQ(2u, notification_count);
   id = file_manager_private::ToString(sync_error.type);
   // Get the strings for the displayed notification.
   notification_strings =
@@ -1313,7 +1313,7 @@
       base::BindOnce(&SystemNotificationManagerTest::GetNotificationsCallback,
                      weak_ptr_factory_.GetWeakPtr()));
   // Check: We have three notifications.
-  ASSERT_EQ(3, notification_count);
+  ASSERT_EQ(3u, notification_count);
   id = file_manager_private::ToString(sync_error.type);
   // Get the strings for the displayed notification.
   notification_strings =
@@ -1338,7 +1338,7 @@
       base::BindOnce(&SystemNotificationManagerTest::GetNotificationsCallback,
                      weak_ptr_factory_.GetWeakPtr()));
   // Check: We have four notifications.
-  ASSERT_EQ(4, notification_count);
+  ASSERT_EQ(4u, notification_count);
   id = file_manager_private::ToString(sync_error.type);
   // Get the strings for the displayed notification.
   notification_strings =
@@ -1361,7 +1361,7 @@
       base::BindOnce(&SystemNotificationManagerTest::GetNotificationsCallback,
                      weak_ptr_factory_.GetWeakPtr()));
   // Check: We have five notifications.
-  ASSERT_EQ(5, notification_count);
+  ASSERT_EQ(5u, notification_count);
   id = file_manager_private::ToString(sync_error.type);
   // Get the strings for the displayed notification.
   notification_strings =
@@ -1393,7 +1393,7 @@
           &SystemNotificationManagerTest::GetNotificationsCallback,
           weak_ptr_factory_.GetWeakPtr()));
   // Check: We have one notification.
-  ASSERT_EQ(1, notification_count);
+  ASSERT_EQ(1u, notification_count);
   // Get the strings for the displayed notification.
   TestNotificationStrings notification_strings =
       notification_platform_bridge->GetNotificationStringsById(
@@ -1430,7 +1430,7 @@
           &SystemNotificationManagerTest::GetNotificationsCallback,
           weak_ptr_factory_.GetWeakPtr()));
   // Check: We have one notification.
-  ASSERT_EQ(1, notification_count);
+  ASSERT_EQ(1u, notification_count);
   // Get the strings for the displayed notification.
   TestNotificationStrings notification_strings =
       notification_platform_bridge->GetNotificationStringsById(
@@ -1455,7 +1455,7 @@
           &SystemNotificationManagerTest::GetNotificationsCallback,
           weak_ptr_factory_.GetWeakPtr()));
   // Check: We have 0 notifications (notification closed on end).
-  ASSERT_EQ(0, notification_count);
+  ASSERT_EQ(0u, notification_count);
   // Start another transfer that ends in error.
   transfer_status.transfer_state =
       file_manager_private::TRANSFER_STATE_IN_PROGRESS;
@@ -1472,7 +1472,7 @@
           &SystemNotificationManagerTest::GetNotificationsCallback,
           weak_ptr_factory_.GetWeakPtr()));
   // Check: We have one notification.
-  ASSERT_EQ(1, notification_count);
+  ASSERT_EQ(1u, notification_count);
   // Setup an completed transfer event.
   transfer_status.transfer_state = file_manager_private::TRANSFER_STATE_FAILED;
   transfer_status.num_total_jobs = 0;
@@ -1489,7 +1489,7 @@
           &SystemNotificationManagerTest::GetNotificationsCallback,
           weak_ptr_factory_.GetWeakPtr()));
   // Check: We have 0 notifications (notification closed on end).
-  ASSERT_EQ(0, notification_count);
+  ASSERT_EQ(0u, notification_count);
 }
 
 TEST_F(SystemNotificationManagerTest, SyncProgressMultiple) {
@@ -1517,7 +1517,7 @@
           &SystemNotificationManagerTest::GetNotificationsCallback,
           weak_ptr_factory_.GetWeakPtr()));
   // Check: We have one notification.
-  ASSERT_EQ(1, notification_count);
+  ASSERT_EQ(1u, notification_count);
   // Get the strings for the displayed notification.
   TestNotificationStrings notification_strings =
       notification_platform_bridge->GetNotificationStringsById(
@@ -1550,7 +1550,7 @@
           &SystemNotificationManagerTest::GetNotificationsCallback,
           weak_ptr_factory_.GetWeakPtr()));
   // Check: We have one notification.
-  ASSERT_EQ(1, notification_count);
+  ASSERT_EQ(1u, notification_count);
   // Get the strings for the displayed notification.
   TestNotificationStrings notification_strings =
       notification_platform_bridge->GetNotificationStringsById("swa-drive-pin");
@@ -1573,7 +1573,7 @@
           &SystemNotificationManagerTest::GetNotificationsCallback,
           weak_ptr_factory_.GetWeakPtr()));
   // Check: We have 0 notifications (notification closed on end).
-  ASSERT_EQ(0, notification_count);
+  ASSERT_EQ(0u, notification_count);
 
   // Start another transfer that ends in error.
   pin_status.transfer_state = file_manager_private::TRANSFER_STATE_IN_PROGRESS;
@@ -1590,7 +1590,7 @@
           &SystemNotificationManagerTest::GetNotificationsCallback,
           weak_ptr_factory_.GetWeakPtr()));
   // Check: We have one notification.
-  ASSERT_EQ(1, notification_count);
+  ASSERT_EQ(1u, notification_count);
   // Setup an completed transfer event.
   pin_status.transfer_state = file_manager_private::TRANSFER_STATE_FAILED;
   pin_status.num_total_jobs = 0;
@@ -1607,7 +1607,7 @@
           &SystemNotificationManagerTest::GetNotificationsCallback,
           weak_ptr_factory_.GetWeakPtr()));
   // Check: We have 0 notifications (notification closed on end).
-  ASSERT_EQ(0, notification_count);
+  ASSERT_EQ(0u, notification_count);
 }
 
 TEST_F(SystemNotificationManagerTest, PinProgressMultiple) {
@@ -1633,7 +1633,7 @@
           &SystemNotificationManagerTest::GetNotificationsCallback,
           weak_ptr_factory_.GetWeakPtr()));
   // Check: We have one notification.
-  ASSERT_EQ(1, notification_count);
+  ASSERT_EQ(1u, notification_count);
   // Get the strings for the displayed notification.
   TestNotificationStrings notification_strings =
       notification_platform_bridge->GetNotificationStringsById("swa-drive-pin");
diff --git a/chrome/browser/chromeos/extensions/login_screen/login/cleanup/extension_cleanup_handler_unittest.cc b/chrome/browser/chromeos/extensions/login_screen/login/cleanup/extension_cleanup_handler_unittest.cc
index 6fbce0c..66f8e28 100644
--- a/chrome/browser/chromeos/extensions/login_screen/login/cleanup/extension_cleanup_handler_unittest.cc
+++ b/chrome/browser/chromeos/extensions/login_screen/login/cleanup/extension_cleanup_handler_unittest.cc
@@ -164,7 +164,7 @@
   SetupExemptList();
   std::unique_ptr<extensions::ExtensionSet> all_installed_extensions =
       extension_registry_->GenerateInstalledExtensionsSet();
-  EXPECT_EQ(all_installed_extensions->size(), 3);
+  EXPECT_EQ(all_installed_extensions->size(), 3u);
 
   base::RunLoop run_loop;
   extension_cleanup_handler_->Cleanup(
@@ -176,7 +176,7 @@
 
   all_installed_extensions =
       extension_registry_->GenerateInstalledExtensionsSet();
-  EXPECT_EQ(all_installed_extensions->size(), 1);
+  EXPECT_EQ(all_installed_extensions->size(), 1u);
   EXPECT_TRUE(all_installed_extensions->Contains(kExemptExtensionId));
 }
 
diff --git a/chrome/browser/chromeos/extensions/telemetry/api/telemetry_api_converters_unittest.cc b/chrome/browser/chromeos/extensions/telemetry/api/telemetry_api_converters_unittest.cc
index e5aeb34f5..e05057ee 100644
--- a/chrome/browser/chromeos/extensions/telemetry/api/telemetry_api_converters_unittest.cc
+++ b/chrome/browser/chromeos/extensions/telemetry/api/telemetry_api_converters_unittest.cc
@@ -70,11 +70,14 @@
       std::move(expected_c_states));
 
   auto result = ConvertPtr<telemetry_api::LogicalCpuInfo>(std::move(input));
-  EXPECT_EQ(kMaxClockSpeedKhz, *result.max_clock_speed_khz);
-  EXPECT_EQ(kScalingMaxFrequencyKhz, *result.scaling_max_frequency_khz);
-  EXPECT_EQ(kScalingCurrentFrequencyKhz, *result.scaling_current_frequency_khz);
+  EXPECT_EQ(kMaxClockSpeedKhz,
+            static_cast<uint32_t>(*result.max_clock_speed_khz));
+  EXPECT_EQ(kScalingMaxFrequencyKhz,
+            static_cast<uint32_t>(*result.scaling_max_frequency_khz));
+  EXPECT_EQ(kScalingCurrentFrequencyKhz,
+            static_cast<uint32_t>(*result.scaling_current_frequency_khz));
   EXPECT_EQ(kIdleTime, *result.idle_time_ms);
-  EXPECT_EQ(1, result.c_states.size());
+  EXPECT_EQ(1u, result.c_states.size());
   EXPECT_EQ(kCpuCStateName, *result.c_states[0].name);
   EXPECT_EQ(kCpuCStateTime,
             *result.c_states[0].time_in_state_since_last_boot_us);
@@ -108,14 +111,17 @@
 
   auto result = ConvertPtr<telemetry_api::PhysicalCpuInfo>(std::move(input));
   EXPECT_EQ(kModelName, *result.model_name);
-  EXPECT_EQ(1, result.logical_cpus.size());
-  EXPECT_EQ(kMaxClockSpeedKhz, *result.logical_cpus[0].max_clock_speed_khz);
-  EXPECT_EQ(kScalingMaxFrequencyKhz,
-            *result.logical_cpus[0].scaling_max_frequency_khz);
+  EXPECT_EQ(1u, result.logical_cpus.size());
+  EXPECT_EQ(kMaxClockSpeedKhz,
+            static_cast<uint32_t>(*result.logical_cpus[0].max_clock_speed_khz));
+  EXPECT_EQ(
+      kScalingMaxFrequencyKhz,
+      static_cast<uint32_t>(*result.logical_cpus[0].scaling_max_frequency_khz));
   EXPECT_EQ(kScalingCurrentFrequencyKhz,
-            *result.logical_cpus[0].scaling_current_frequency_khz);
+            static_cast<uint32_t>(
+                *result.logical_cpus[0].scaling_current_frequency_khz));
   EXPECT_EQ(kIdleTime, *result.logical_cpus[0].idle_time_ms);
-  EXPECT_EQ(1, result.logical_cpus[0].c_states.size());
+  EXPECT_EQ(1u, result.logical_cpus[0].c_states.size());
   EXPECT_EQ(kCpuCStateName, *result.logical_cpus[0].c_states[0].name);
   EXPECT_EQ(
       kCpuCStateTime,
diff --git a/chrome/browser/chromeos/policy/dlp/data_transfer_dlp_controller.cc b/chrome/browser/chromeos/policy/dlp/data_transfer_dlp_controller.cc
index 0ee19d7..59fa6ab 100644
--- a/chrome/browser/chromeos/policy/dlp/data_transfer_dlp_controller.cc
+++ b/chrome/browser/chromeos/policy/dlp/data_transfer_dlp_controller.cc
@@ -72,7 +72,7 @@
   if (!data_dst || !data_dst->IsUrlType())
     return false;
 
-  GURL url = data_dst->GetOrigin()->GetURL();
+  GURL url = *data_dst->GetURL();
   // TODO(b/207576430): Once Files Extension is removed, remove this condition.
   bool is_files_extension =
       url.has_scheme() && url.SchemeIs(extensions::kExtensionScheme) &&
@@ -124,7 +124,7 @@
     return DlpRulesManager::Level::kAllow;
   }
 
-  const GURL src_url = data_src->GetOrigin()->GetURL();
+  const GURL src_url = *data_src->GetURL();
   ui::EndpointType dst_type =
       data_dst ? data_dst->type() : ui::EndpointType::kDefault;
 
@@ -173,7 +173,7 @@
     }
 
     case ui::EndpointType::kUrl: {
-      GURL dst_url = data_dst->GetOrigin()->GetURL();
+      GURL dst_url = *data_dst->GetURL();
       level = dlp_rules_manager.IsRestrictedDestination(
           src_url, dst_url, DlpRulesManager::Restriction::kClipboard,
           src_pattern, dst_pattern);
diff --git a/chrome/browser/chromeos/policy/dlp/data_transfer_dlp_controller_browsertest.cc b/chrome/browser/chromeos/policy/dlp/data_transfer_dlp_controller_browsertest.cc
index ff75e1e..717332d 100644
--- a/chrome/browser/chromeos/policy/dlp/data_transfer_dlp_controller_browsertest.cc
+++ b/chrome/browser/chromeos/policy/dlp/data_transfer_dlp_controller_browsertest.cc
@@ -45,7 +45,6 @@
 #include "ui/views/controls/textfield/textfield.h"
 #include "ui/views/widget/widget.h"
 #include "ui/views/widget/widget_observer.h"
-#include "url/origin.h"
 
 // TODO(crbug.com/1262948): Enable and modify for lacros.
 namespace policy {
@@ -250,8 +249,7 @@
 
   SetClipboardText(kClipboardText116, nullptr);
 
-  ui::DataTransferEndpoint data_dst(
-      url::Origin::Create(GURL("https://google.com")));
+  ui::DataTransferEndpoint data_dst((GURL("https://google.com")));
   std::u16string result;
   ui::Clipboard::GetForCurrentThread()->ReadText(
       ui::ClipboardBuffer::kCopyPaste, &data_dst, &result);
@@ -295,34 +293,34 @@
 
   SetDlpRulesPolicy(std::move(rules));
 
-  SetClipboardText(kClipboardText116,
-                   std::make_unique<ui::DataTransferEndpoint>(
-                       url::Origin::Create(GURL(kMailUrl))));
+  SetClipboardText(
+      kClipboardText116,
+      std::make_unique<ui::DataTransferEndpoint>((GURL(kMailUrl))));
 
-  ui::DataTransferEndpoint data_dst1(url::Origin::Create(GURL(kMailUrl)));
+  ui::DataTransferEndpoint data_dst1((GURL(kMailUrl)));
   std::u16string result1;
   ui::Clipboard::GetForCurrentThread()->ReadText(
       ui::ClipboardBuffer::kCopyPaste, &data_dst1, &result1);
   EXPECT_EQ(kClipboardText116, result1);
 
-  ui::DataTransferEndpoint data_dst2(url::Origin::Create(GURL(kDocsUrl)));
+  ui::DataTransferEndpoint data_dst2((GURL(kDocsUrl)));
   std::u16string result2;
   ui::Clipboard::GetForCurrentThread()->ReadText(
       ui::ClipboardBuffer::kCopyPaste, &data_dst2, &result2);
   EXPECT_EQ(kClipboardText116, result2);
 
-  ui::DataTransferEndpoint data_dst3(url::Origin::Create(GURL(kExampleUrl)));
+  ui::DataTransferEndpoint data_dst3((GURL(kExampleUrl)));
   std::u16string result3;
   ui::Clipboard::GetForCurrentThread()->ReadText(
       ui::ClipboardBuffer::kCopyPaste, &data_dst3, &result3);
   EXPECT_EQ(std::u16string(), result3);
   ASSERT_TRUE(dlp_controller.ObserveWidget());
 
-  SetClipboardText(kClipboardText116,
-                   std::make_unique<ui::DataTransferEndpoint>(
-                       url::Origin::Create(GURL(kExampleUrl))));
+  SetClipboardText(
+      kClipboardText116,
+      std::make_unique<ui::DataTransferEndpoint>((GURL(kExampleUrl))));
 
-  ui::DataTransferEndpoint data_dst4(url::Origin::Create(GURL(kMailUrl)));
+  ui::DataTransferEndpoint data_dst4((GURL(kMailUrl)));
   std::u16string result4;
   ui::Clipboard::GetForCurrentThread()->ReadText(
       ui::ClipboardBuffer::kCopyPaste, &data_dst1, &result4);
@@ -361,9 +359,9 @@
   SetDlpRulesPolicy(rules);
 
   {
-    ui::ScopedClipboardWriter writer(ui::ClipboardBuffer::kCopyPaste,
-                                     std::make_unique<ui::DataTransferEndpoint>(
-                                         url::Origin::Create(GURL(kMailUrl))));
+    ui::ScopedClipboardWriter writer(
+        ui::ClipboardBuffer::kCopyPaste,
+        std::make_unique<ui::DataTransferEndpoint>((GURL(kMailUrl))));
     writer.WriteText(kClipboardText116);
   }
   ui::DataTransferEndpoint data_dst1(ui::EndpointType::kDefault);
@@ -426,9 +424,9 @@
     update->Append(std::move(rule));
   }
 
-  SetClipboardText(kClipboardText116,
-                   std::make_unique<ui::DataTransferEndpoint>(
-                       url::Origin::Create(GURL(kMailUrl))));
+  SetClipboardText(
+      kClipboardText116,
+      std::make_unique<ui::DataTransferEndpoint>((GURL(kMailUrl))));
 
   SetupTextfield();
   // Initiate a paste on textfield_.
@@ -447,7 +445,7 @@
   EXPECT_EQ(kClipboardText116, textfield_->GetText());
 
   SetClipboardText(kClipboardText2, std::make_unique<ui::DataTransferEndpoint>(
-                                        url::Origin::Create(GURL(kMailUrl))));
+                                        (GURL(kMailUrl))));
 
   // Initiate a paste on textfield_.
   textfield_->SetText(std::u16string());
@@ -514,9 +512,9 @@
   }
 
   {
-    ui::ScopedClipboardWriter writer(ui::ClipboardBuffer::kCopyPaste,
-                                     std::make_unique<ui::DataTransferEndpoint>(
-                                         url::Origin::Create(GURL(kMailUrl))));
+    ui::ScopedClipboardWriter writer(
+        ui::ClipboardBuffer::kCopyPaste,
+        std::make_unique<ui::DataTransferEndpoint>((GURL(kMailUrl))));
     writer.WriteText(kClipboardText116);
   }
 
@@ -629,9 +627,9 @@
     update->Append(std::move(rule));
   }
 
-  SetClipboardText(kClipboardText116,
-                   std::make_unique<ui::DataTransferEndpoint>(
-                       url::Origin::Create(GURL(kMailUrl))));
+  SetClipboardText(
+      kClipboardText116,
+      std::make_unique<ui::DataTransferEndpoint>((GURL(kMailUrl))));
 
   EXPECT_TRUE(
       ExecJs(GetActiveWebContents(),
@@ -723,9 +721,9 @@
     update->Append(std::move(rule));
   }
 
-  SetClipboardText(kClipboardText116,
-                   std::make_unique<ui::DataTransferEndpoint>(
-                       url::Origin::Create(GURL(kMailUrl))));
+  SetClipboardText(
+      kClipboardText116,
+      std::make_unique<ui::DataTransferEndpoint>((GURL(kMailUrl))));
 
   EXPECT_TRUE(
       ExecJs(GetActiveWebContents(),
@@ -814,9 +812,9 @@
     update->Append(std::move(rule));
   }
 
-  SetClipboardText(kClipboardText116,
-                   std::make_unique<ui::DataTransferEndpoint>(
-                       url::Origin::Create(GURL(kMailUrl))));
+  SetClipboardText(
+      kClipboardText116,
+      std::make_unique<ui::DataTransferEndpoint>((GURL(kMailUrl))));
 
   EXPECT_TRUE(
       ExecJs(GetActiveWebContents(),
@@ -898,9 +896,9 @@
     update->Append(std::move(rule));
   }
 
-  SetClipboardText(kClipboardText116,
-                   std::make_unique<ui::DataTransferEndpoint>(
-                       url::Origin::Create(GURL(kMailUrl))));
+  SetClipboardText(
+      kClipboardText116,
+      std::make_unique<ui::DataTransferEndpoint>((GURL(kMailUrl))));
 
   EXPECT_TRUE(
       ExecJs(GetActiveWebContents(),
diff --git a/chrome/browser/chromeos/policy/dlp/data_transfer_dlp_controller_unittest.cc b/chrome/browser/chromeos/policy/dlp/data_transfer_dlp_controller_unittest.cc
index 4c21c19f..efb23ab 100644
--- a/chrome/browser/chromeos/policy/dlp/data_transfer_dlp_controller_unittest.cc
+++ b/chrome/browser/chromeos/policy/dlp/data_transfer_dlp_controller_unittest.cc
@@ -30,7 +30,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "ui/base/data_transfer_policy/data_transfer_endpoint.h"
-#include "url/origin.h"
+#include "url/gurl.h"
 
 #if BUILDFLAG(IS_CHROMEOS_LACROS)
 #include "chromeos/lacros/lacros_service.h"
@@ -90,7 +90,7 @@
     bool notify_if_restricted) {
   if (type && *type == ui::EndpointType::kUrl) {
     return ui::DataTransferEndpoint(
-        url::Origin::Create(GURL(kExample2Url)),
+        (GURL(kExample2Url)),
         /*notify_if_restricted=*/notify_if_restricted);
   } else if (type) {
     return ui::DataTransferEndpoint(
@@ -168,7 +168,7 @@
 }
 
 TEST_F(DataTransferDlpControllerTest, ClipboardHistoryDst) {
-  ui::DataTransferEndpoint data_src(url::Origin::Create(GURL(kExample1Url)));
+  ui::DataTransferEndpoint data_src((GURL(kExample1Url)));
   ui::DataTransferEndpoint data_dst(ui::EndpointType::kClipboardHistory);
   EXPECT_EQ(true, dlp_controller_.IsClipboardReadAllowed(&data_src, &data_dst,
                                                          absl::nullopt));
@@ -178,7 +178,7 @@
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 TEST_F(DataTransferDlpControllerTest, LacrosDst) {
-  ui::DataTransferEndpoint data_src(url::Origin::Create(GURL(kExample1Url)));
+  ui::DataTransferEndpoint data_src((GURL(kExample1Url)));
   ui::DataTransferEndpoint data_dst(ui::EndpointType::kLacros);
   EXPECT_EQ(true, dlp_controller_.IsClipboardReadAllowed(&data_src, &data_dst,
                                                          absl::nullopt));
@@ -188,8 +188,8 @@
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
 TEST_F(DataTransferDlpControllerTest, PasteIfAllowed_Allow) {
-  ui::DataTransferEndpoint data_src(url::Origin::Create(GURL(kExample1Url)));
-  ui::DataTransferEndpoint data_dst(url::Origin::Create(GURL(kExample2Url)));
+  ui::DataTransferEndpoint data_src((GURL(kExample1Url)));
+  ui::DataTransferEndpoint data_dst((GURL(kExample2Url)));
 
   // IsClipboardReadAllowed
   EXPECT_CALL(rules_manager_, IsRestrictedDestination)
@@ -206,8 +206,8 @@
 }
 
 TEST_F(DataTransferDlpControllerTest, PasteIfAllowed_NullWebContents) {
-  ui::DataTransferEndpoint data_src(url::Origin::Create(GURL(kExample1Url)));
-  ui::DataTransferEndpoint data_dst(url::Origin::Create(GURL(kExample2Url)));
+  ui::DataTransferEndpoint data_src((GURL(kExample1Url)));
+  ui::DataTransferEndpoint data_dst((GURL(kExample2Url)));
 
   ::testing::StrictMock<base::MockOnceCallback<void(bool)>> callback;
   EXPECT_CALL(callback, Run(false));
@@ -216,8 +216,8 @@
 }
 
 TEST_F(DataTransferDlpControllerTest, PasteIfAllowed_WarnDst) {
-  ui::DataTransferEndpoint data_src(url::Origin::Create(GURL(kExample1Url)));
-  ui::DataTransferEndpoint data_dst(url::Origin::Create(GURL(kExample2Url)));
+  ui::DataTransferEndpoint data_src((GURL(kExample1Url)));
+  ui::DataTransferEndpoint data_dst((GURL(kExample2Url)));
 
   std::unique_ptr<TestingProfile> testing_profile =
       TestingProfile::Builder().Build();
@@ -247,8 +247,8 @@
 }
 
 TEST_F(DataTransferDlpControllerTest, PasteIfAllowed_ProceedDst) {
-  ui::DataTransferEndpoint data_src(url::Origin::Create(GURL(kExample1Url)));
-  ui::DataTransferEndpoint data_dst(url::Origin::Create(GURL(kExample2Url)));
+  ui::DataTransferEndpoint data_src((GURL(kExample1Url)));
+  ui::DataTransferEndpoint data_dst((GURL(kExample2Url)));
 
   std::unique_ptr<TestingProfile> testing_profile =
       TestingProfile::Builder().Build();
@@ -274,8 +274,8 @@
 }
 
 TEST_F(DataTransferDlpControllerTest, PasteIfAllowed_CancelDst) {
-  ui::DataTransferEndpoint data_src(url::Origin::Create(GURL(kExample1Url)));
-  ui::DataTransferEndpoint data_dst(url::Origin::Create(GURL(kExample2Url)));
+  ui::DataTransferEndpoint data_src((GURL(kExample1Url)));
+  ui::DataTransferEndpoint data_dst((GURL(kExample2Url)));
 
   std::unique_ptr<TestingProfile> testing_profile =
       TestingProfile::Builder().Build();
@@ -302,8 +302,7 @@
  protected:
   void SetUp() override {
     DataTransferDlpControllerTest::SetUp();
-    data_src_ =
-        ui::DataTransferEndpoint(url::Origin::Create(GURL(kExample1Url)));
+    data_src_ = ui::DataTransferEndpoint((GURL(kExample1Url)));
     absl::optional<ui::EndpointType> endpoint_type;
     std::tie(endpoint_type, do_notify_) = GetParam();
     data_dst_ =
@@ -505,8 +504,7 @@
  protected:
   void SetUp() override {
     DataTransferDlpControllerTest::SetUp();
-    data_src_ =
-        ui::DataTransferEndpoint(url::Origin::Create(GURL(kExample1Url)));
+    data_src_ = ui::DataTransferEndpoint((GURL(kExample1Url)));
     std::tie(endpoint_type_, do_notify_) = GetParam();
     ASSERT_TRUE(endpoint_type_.has_value());
     data_dst_ = ui::DataTransferEndpoint(endpoint_type_.value(), do_notify_);
@@ -527,7 +525,7 @@
                        testing::Bool()));
 
 TEST_P(DlpControllerVMsTest, Allow) {
-  ui::DataTransferEndpoint data_src(url::Origin::Create(GURL(kExample1Url)));
+  ui::DataTransferEndpoint data_src((GURL(kExample1Url)));
   auto [endpoint_type, do_notify] = GetParam();
   ASSERT_TRUE(endpoint_type.has_value());
   ui::DataTransferEndpoint data_dst(endpoint_type.value(), do_notify);
@@ -634,7 +632,7 @@
 }
 
 TEST_P(DlpControllerVMsTest, Warn_IsClipboardReadAllowed) {
-  ui::DataTransferEndpoint data_src(url::Origin::Create(GURL(kExample1Url)));
+  ui::DataTransferEndpoint data_src((GURL(kExample1Url)));
   auto [endpoint_type, do_notify] = GetParam();
   ASSERT_TRUE(endpoint_type.has_value());
   ui::DataTransferEndpoint data_dst(endpoint_type.value(), do_notify);
diff --git a/chrome/browser/chromeos/policy/dlp/dlp_clipboard_notifier.cc b/chrome/browser/chromeos/policy/dlp/dlp_clipboard_notifier.cc
index 4afa99a..a18792d 100644
--- a/chrome/browser/chromeos/policy/dlp/dlp_clipboard_notifier.cc
+++ b/chrome/browser/chromeos/policy/dlp/dlp_clipboard_notifier.cc
@@ -85,7 +85,7 @@
     if (ept.type() == endpoint_type) {
       if (endpoint_type != ui::EndpointType::kUrl)
         return true;
-      else if (ept.IsSameOriginWith(*endpoint))
+      else if (ept == *endpoint)
         return true;
     }
   }
@@ -114,9 +114,9 @@
     const ui::DataTransferEndpoint* const data_src,
     const ui::DataTransferEndpoint* const data_dst) {
   DCHECK(data_src);
-  DCHECK(data_src->GetOrigin());
+  DCHECK(data_src->GetURL());
   const std::u16string host_name =
-      base::UTF8ToUTF16(data_src->GetOrigin()->host());
+      base::UTF8ToUTF16(data_src->GetURL()->host());
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   if (data_dst) {
     if (data_dst->type() == ui::EndpointType::kCrostini) {
@@ -154,12 +154,12 @@
     const ui::DataTransferEndpoint* const data_src,
     const ui::DataTransferEndpoint* const data_dst) {
   DCHECK(data_src);
-  DCHECK(data_src->GetOrigin());
+  DCHECK(data_src->GetURL());
 
   CloseWidget(widget_.get(), views::Widget::ClosedReason::kUnspecified);
 
   const std::u16string host_name =
-      base::UTF8ToUTF16(data_src->GetOrigin()->host());
+      base::UTF8ToUTF16(data_src->GetURL()->host());
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   if (data_dst) {
     if (data_dst->type() == ui::EndpointType::kCrostini) {
@@ -207,12 +207,12 @@
     content::WebContents* web_contents,
     base::OnceCallback<void(bool)> paste_cb) {
   DCHECK(data_src);
-  DCHECK(data_src->GetOrigin());
+  DCHECK(data_src->GetURL());
 
   CloseWidget(widget_.get(), views::Widget::ClosedReason::kUnspecified);
 
   const std::u16string host_name =
-      base::UTF8ToUTF16(data_src->GetOrigin()->host());
+      base::UTF8ToUTF16(data_src->GetURL()->host());
 
   blink_paste_cb_ = std::move(paste_cb);
   Observe(web_contents);
diff --git a/chrome/browser/chromeos/policy/dlp/dlp_clipboard_notifier_unittest.cc b/chrome/browser/chromeos/policy/dlp/dlp_clipboard_notifier_unittest.cc
index 784fa36d..31e768a 100644
--- a/chrome/browser/chromeos/policy/dlp/dlp_clipboard_notifier_unittest.cc
+++ b/chrome/browser/chromeos/policy/dlp/dlp_clipboard_notifier_unittest.cc
@@ -26,7 +26,6 @@
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/views/widget/widget.h"
 #include "url/gurl.h"
-#include "url/origin.h"
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 #include "ash/public/cpp/system/toast_catalog.h"
@@ -57,7 +56,7 @@
 
 ui::DataTransferEndpoint CreateEndpoint(ui::EndpointType type) {
   if (type == ui::EndpointType::kUrl)
-    return ui::DataTransferEndpoint(url::Origin::Create(GURL(kExampleUrl)));
+    return ui::DataTransferEndpoint((GURL(kExampleUrl)));
   else
     return ui::DataTransferEndpoint(type);
 }
@@ -104,7 +103,7 @@
 
 TEST_P(ClipboardBubbleTestWithParam, BlockBubble) {
   ::testing::StrictMock<MockDlpClipboardNotifier> notifier;
-  ui::DataTransferEndpoint data_src(url::Origin::Create(GURL(kExampleUrl)));
+  ui::DataTransferEndpoint data_src((GURL(kExampleUrl)));
   absl::optional<ui::DataTransferEndpoint> data_dst;
   auto param = GetParam();
   if (param.has_value())
@@ -117,8 +116,7 @@
 
 TEST_P(ClipboardBubbleTestWithParam, WarnBubble) {
   ::testing::StrictMock<MockDlpClipboardNotifier> notifier;
-  url::Origin origin = url::Origin::Create(GURL(kExampleUrl));
-  ui::DataTransferEndpoint data_src(origin);
+  ui::DataTransferEndpoint data_src((GURL(kExampleUrl)));
   absl::optional<ui::DataTransferEndpoint> data_dst;
   auto param = GetParam();
   if (param.has_value())
@@ -201,9 +199,9 @@
 
 TEST_F(DlpClipboardNotifierTest, BlinkWarn) {
   ::testing::StrictMock<MockDlpClipboardNotifier> notifier;
-  url::Origin origin = url::Origin::Create(GURL(kExampleUrl));
-  ui::DataTransferEndpoint data_src(origin);
-  ui::DataTransferEndpoint data_dst(origin);
+  GURL url = GURL(kExampleUrl);
+  ui::DataTransferEndpoint data_src(url);
+  ui::DataTransferEndpoint data_dst(url);
 
   EXPECT_CALL(notifier, CloseWidget(testing::_,
                                     views::Widget::ClosedReason::kUnspecified));
@@ -227,12 +225,9 @@
 
 TEST_F(DlpClipboardNotifierTest, BlinkProceedSavedHistory) {
   ::testing::StrictMock<MockDlpClipboardNotifier> notifier;
-  const ui::DataTransferEndpoint url_dst1(
-      url::Origin::Create(GURL(kExampleUrl)));
-  const ui::DataTransferEndpoint url_dst2(
-      url::Origin::Create(GURL(kExample2Url)));
-  const ui::DataTransferEndpoint url_dst3(
-      url::Origin::Create(GURL(kExample3Url)));
+  const ui::DataTransferEndpoint url_dst1((GURL(kExampleUrl)));
+  const ui::DataTransferEndpoint url_dst2((GURL(kExample2Url)));
+  const ui::DataTransferEndpoint url_dst3((GURL(kExample3Url)));
 
   ::testing::StrictMock<base::MockOnceCallback<void(bool)>> callback;
 
@@ -268,8 +263,7 @@
 
 TEST_F(DlpClipboardNotifierTest, ProceedSavedHistory) {
   ::testing::StrictMock<MockDlpClipboardNotifier> notifier;
-  const ui::DataTransferEndpoint url_dst(
-      url::Origin::Create(GURL(kExampleUrl)));
+  const ui::DataTransferEndpoint url_dst((GURL(kExampleUrl)));
   const ui::DataTransferEndpoint default_dst(ui::EndpointType::kDefault);
 
   EXPECT_CALL(notifier,
@@ -315,8 +309,7 @@
 
 TEST_F(DlpClipboardNotifierTest, CancelSavedHistory) {
   ::testing::StrictMock<MockDlpClipboardNotifier> notifier;
-  const ui::DataTransferEndpoint url_dst(
-      url::Origin::Create(GURL(kExampleUrl)));
+  const ui::DataTransferEndpoint url_dst((GURL(kExampleUrl)));
   const ui::DataTransferEndpoint default_dst(ui::EndpointType::kDefault);
 
   EXPECT_CALL(notifier,
@@ -374,13 +367,13 @@
 
 TEST_P(ToastTestWithParam, BlockToast) {
   ::testing::StrictMock<MockDlpClipboardNotifier> notifier;
-  url::Origin origin = url::Origin::Create(GURL(kExampleUrl));
-  ui::DataTransferEndpoint data_src(origin);
+  GURL url = GURL(kExampleUrl);
+  ui::DataTransferEndpoint data_src(url);
   ui::DataTransferEndpoint data_dst(GetParam().dst_type);
 
   std::u16string expected_toast_str = l10n_util::GetStringFUTF16(
       IDS_POLICY_DLP_CLIPBOARD_BLOCKED_ON_COPY_VM,
-      base::UTF8ToUTF16(origin.host()),
+      base::UTF8ToUTF16(url.host()),
       l10n_util::GetStringUTF16(GetParam().expected_dst_name_id));
 
   EXPECT_CALL(
@@ -393,8 +386,8 @@
 
 TEST_P(ToastTestWithParam, WarnToast) {
   ::testing::StrictMock<MockDlpClipboardNotifier> notifier;
-  url::Origin origin = url::Origin::Create(GURL(kExampleUrl));
-  ui::DataTransferEndpoint data_src(origin);
+  GURL url = GURL(kExampleUrl);
+  ui::DataTransferEndpoint data_src(url);
   ui::DataTransferEndpoint data_dst(GetParam().dst_type);
 
   std::u16string expected_toast_str = l10n_util::GetStringFUTF16(
diff --git a/chrome/browser/chromeos/policy/dlp/dlp_drag_drop_notifier.cc b/chrome/browser/chromeos/policy/dlp/dlp_drag_drop_notifier.cc
index c7e638e..f3a66a4 100644
--- a/chrome/browser/chromeos/policy/dlp/dlp_drag_drop_notifier.cc
+++ b/chrome/browser/chromeos/policy/dlp/dlp_drag_drop_notifier.cc
@@ -24,9 +24,9 @@
     const ui::DataTransferEndpoint* const data_src,
     const ui::DataTransferEndpoint* const data_dst) {
   DCHECK(data_src);
-  DCHECK(data_src->GetOrigin());
+  DCHECK(data_src->GetURL());
   const std::u16string host_name =
-      base::UTF8ToUTF16(data_src->GetOrigin()->host());
+      base::UTF8ToUTF16(data_src->GetURL()->host());
 
   ShowBlockBubble(l10n_util::GetStringFUTF16(
       IDS_POLICY_DLP_CLIPBOARD_BLOCKED_ON_PASTE, host_name));
@@ -37,12 +37,12 @@
     const ui::DataTransferEndpoint* const data_dst,
     base::OnceClosure drop_cb) {
   DCHECK(data_src);
-  DCHECK(data_src->GetOrigin());
+  DCHECK(data_src->GetURL());
 
   CloseWidget(widget_.get(), views::Widget::ClosedReason::kUnspecified);
 
   const std::u16string host_name =
-      base::UTF8ToUTF16(data_src->GetOrigin()->host());
+      base::UTF8ToUTF16(data_src->GetURL()->host());
 
   drop_cb_ = std::move(drop_cb);
   auto proceed_cb = base::BindRepeating(&DlpDragDropNotifier::ProceedPressed,
diff --git a/chrome/browser/chromeos/policy/dlp/dlp_drag_drop_notifier_unittest.cc b/chrome/browser/chromeos/policy/dlp/dlp_drag_drop_notifier_unittest.cc
index b641dabf..ba289bf 100644
--- a/chrome/browser/chromeos/policy/dlp/dlp_drag_drop_notifier_unittest.cc
+++ b/chrome/browser/chromeos/policy/dlp/dlp_drag_drop_notifier_unittest.cc
@@ -20,7 +20,7 @@
 
 ui::DataTransferEndpoint CreateEndpoint(ui::EndpointType type) {
   if (type == ui::EndpointType::kUrl)
-    return ui::DataTransferEndpoint(url::Origin::Create(GURL(kExampleUrl)));
+    return ui::DataTransferEndpoint((GURL(kExampleUrl)));
   else
     return ui::DataTransferEndpoint(type);
 }
@@ -59,7 +59,7 @@
 
 TEST_P(DragDropBubbleTestWithParam, NotifyBlocked) {
   ::testing::StrictMock<MockDlpDragDropNotifier> notifier;
-  ui::DataTransferEndpoint data_src(url::Origin::Create(GURL(kExampleUrl)));
+  ui::DataTransferEndpoint data_src((GURL(kExampleUrl)));
   absl::optional<ui::DataTransferEndpoint> data_dst;
   auto param = GetParam();
   if (param.has_value())
@@ -72,7 +72,7 @@
 
 TEST_P(DragDropBubbleTestWithParam, ProceedWarnOnDrop) {
   ::testing::StrictMock<MockDlpDragDropNotifier> notifier;
-  ui::DataTransferEndpoint data_src(url::Origin::Create(GURL(kExampleUrl)));
+  ui::DataTransferEndpoint data_src((GURL(kExampleUrl)));
   absl::optional<ui::DataTransferEndpoint> data_dst;
   auto param = GetParam();
   if (param.has_value())
@@ -96,7 +96,7 @@
 
 TEST_P(DragDropBubbleTestWithParam, CancelWarnOnDrop) {
   ::testing::StrictMock<MockDlpDragDropNotifier> notifier;
-  ui::DataTransferEndpoint data_src(url::Origin::Create(GURL(kExampleUrl)));
+  ui::DataTransferEndpoint data_src((GURL(kExampleUrl)));
   absl::optional<ui::DataTransferEndpoint> data_dst;
   auto param = GetParam();
   if (param.has_value())
diff --git a/chrome/browser/chromeos/policy/dlp/dlp_rules_manager_impl.cc b/chrome/browser/chromeos/policy/dlp/dlp_rules_manager_impl.cc
index feccb68..887d950 100644
--- a/chrome/browser/chromeos/policy/dlp/dlp_rules_manager_impl.cc
+++ b/chrome/browser/chromeos/policy/dlp/dlp_rules_manager_impl.cc
@@ -29,6 +29,7 @@
 #include "components/prefs/pref_service.h"
 #include "components/url_matcher/url_util.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
+#include "url/origin.h"
 
 namespace policy {
 
diff --git a/chrome/browser/extensions/api/web_navigation/web_navigation_api.cc b/chrome/browser/extensions/api/web_navigation/web_navigation_api.cc
index eea280e..6a16b9c8 100644
--- a/chrome/browser/extensions/api/web_navigation/web_navigation_api.cc
+++ b/chrome/browser/extensions/api/web_navigation/web_navigation_api.cc
@@ -426,7 +426,7 @@
   if (existing_url == url)
     return false;
 
-  url::Replacements<char> replacements;
+  GURL::Replacements replacements;
   replacements.ClearRef();
   return existing_url.ReplaceComponents(replacements) ==
          url.ReplaceComponents(replacements);
diff --git a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedPlaceholderLayout.java b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedPlaceholderLayout.java
index 2dfec475..7cf263d 100644
--- a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedPlaceholderLayout.java
+++ b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedPlaceholderLayout.java
@@ -61,7 +61,7 @@
     private static final int TEXT_PLACEHOLDER_RADIUS_DP = 12;
     private static final int LARGE_IMAGE_HEIGHT_DP = 207;
 
-    private static final int START_DELAY_MS = 733;
+    private static final int START_DELAY_MS = 0;
     private static final int FADE_DURATION_MS = 620;
     private static final PathInterpolator INITIAL_FADE_IN_CURVE =
             new PathInterpolator(0.17f, 0.17f, 0.85f, 1f);
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index cd9da48..66cf237 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -421,11 +421,6 @@
     "expiry_milestone": 105
   },
   {
-    "name": "autofill-enable-offer-notification-cross-tab-tracking",
-    "owners": [ "siyua" ],
-    "expiry_milestone": 100
-  },
-  {
     "name": "autofill-enable-offer-notification-for-promo-codes",
     "owners": [ "jsaul@google.com", "siyua" ],
     "expiry_milestone": 105
@@ -484,11 +479,6 @@
     "expiry_milestone": 105
   },
   {
-    "name": "autofill-fix-offer-in-incognito",
-    "owners": [ "siyua" ],
-    "expiry_milestone": 100
-  },
-  {
     "name": "autofill-highlight-only-changed-value-in-preview-mode",
     "owners": [ "koerber", "mamir" ],
     "expiry_milestone": 103
@@ -2044,11 +2034,6 @@
     "expiry_milestone": 103
   },
   {
-    "name": "enable-experimental-accessibility-dictation-offline",
-    "owners": [ "akihiroota", "katie" ],
-    "expiry_milestone": 96
-  },
-  {
     "name": "enable-experimental-accessibility-dictation-with-pumpkin",
     "owners": [ "akihiroota"],
     "expiry_milestone": 104
@@ -3228,7 +3213,7 @@
   {
     "name": "extension-workflow-justification",
     "owners": [ "igorruvinov", "zmin" ],
-    "expiry_milestone": 100
+    "expiry_milestone": 105
   },
   {
     "name": "extensions-menu-access-control",
@@ -5318,16 +5303,6 @@
     "expiry_milestone": 92
   },
   {
-    "name": "share-usage-ranking",
-    "owners": [ "ellyjones", "chrome-sharing-eng@google.com" ],
-    "expiry_milestone": 100
-  },
-  {
-    "name": "share-usage-ranking-fixed-more",
-    "owners": [ "ellyjones", "chrome-sharing-eng@google.com" ],
-    "expiry_milestone": 100
-  },
-  {
     "name": "shared-clipboard-ui",
     "owners": [ "//chrome/browser/sharing/OWNERS" ],
     "expiry_milestone": 90
@@ -5461,17 +5436,17 @@
   {
     "name" : "side-search",
     "owners": [ "chrome-cros@google.com", "tluk" ],
-    "expiry_milestone" : 100
+    "expiry_milestone" : 104
   },
   {
     "name" : "side-search-clear-cache-when-closed",
     "owners": [ "chrome-cros@google.com", "tluk" ],
-    "expiry_milestone" : 100
+    "expiry_milestone" : 104
   },
   {
     "name" : "side-search-state-per-tab",
     "owners": [ "chrome-cros@google.com", "tluk" ],
-    "expiry_milestone" : 100
+    "expiry_milestone" : 104
   },
   {
     "name": "single-cell-content-suggestions",
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 122d35d..671befe 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -324,12 +324,6 @@
     "When enabled, Autofill will offer to use merchant bound virtual cards in "
     "payment forms.";
 
-const char kAutofillEnableOfferNotificationCrossTabTrackingName[] =
-    "Enable cross tab status tracking for Autofill offer notification";
-const char kAutofillEnableOfferNotificationCrossTabTrackingDescription[] =
-    "When enabled, the offer notification showing will be tracked cross-tab, "
-    "and on one merchant, the notification will only be shown once.";
-
 const char kAutofillEnableOfferNotificationForPromoCodesName[] =
     "Extend Autofill offers and rewards notification to promo code offers";
 const char kAutofillEnableOfferNotificationForPromoCodesDescription[] =
@@ -401,12 +395,6 @@
     "When enabled, Autofill will attempt to fill merchant promo/coupon/gift "
     "code fields when data is available.";
 
-const char kAutofillFixOfferInIncognitoName[] =
-    "Enable the fix for Autofill offer in Incognito mode";
-const char kAutofillFixOfferInIncognitoDescription[] =
-    "When enabled, the fix will be enabled and offers should work correctly in "
-    "Incognito mode.";
-
 const char kAutofillHighlightOnlyChangedValuesInPreviewModeName[] =
     "Highlight only changed values in preview mode.";
 const char kAutofillHighlightOnlyChangedValuesInPreviewModeDescription[] =
@@ -3511,17 +3499,6 @@
     "An option in `Site settings` to persistently request the "
     "desktop version of websites.";
 
-const char kShareUsageRankingName[] =
-    "Incorporate usage history into share target ranking.";
-const char kShareUsageRankingDescription[] =
-    "Incorporate the history of which apps were shared to when producing the "
-    "ordered list of 3P share targets in the share hub.";
-const char kShareUsageRankingFixedMoreName[] =
-    "Fix the position of the 'More' item in the Android share hub.";
-const char kShareUsageRankingFixedMoreDescription[] =
-    "When enabled with #share-usage-ranking, forces the 'More' option to "
-    "occupy the right-most slot on the screen instead of moving depending on "
-    "the length of the target list.";
 const char kSwapAndroidShareHubRowsName[] = "Swap Android share hub rows.";
 const char kSwapAndroidShareHubRowsDescription[] =
     "Swap the order of the first-party and third-party rows in the Android "
@@ -4788,12 +4765,6 @@
 const char kExperimentalAccessibilityDictationExtensionDescription[] =
     "Enables the JavaScript dictation extension.";
 
-const char kExperimentalAccessibilityDictationOfflineName[] =
-    "Experimental accessibility dictation offline.";
-const char kExperimentalAccessibilityDictationOfflineDescription[] =
-    "Enables offline speech recognition for the accessibility dictation "
-    "feature.";
-
 const char kExperimentalAccessibilityDictationCommandsName[] =
     "Experimental accessibility dictation commands";
 const char kExperimentalAccessibilityDictationCommandsDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index e27d980..b1468664 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -187,9 +187,6 @@
 extern const char kAutofillEnableMerchantBoundVirtualCardsName[];
 extern const char kAutofillEnableMerchantBoundVirtualCardsDescription[];
 
-extern const char kAutofillEnableOfferNotificationCrossTabTrackingName[];
-extern const char kAutofillEnableOfferNotificationCrossTabTrackingDescription[];
-
 extern const char kAutofillEnableOfferNotificationForPromoCodesName[];
 extern const char kAutofillEnableOfferNotificationForPromoCodesDescription[];
 
@@ -226,9 +223,6 @@
 extern const char kAutofillFillMerchantPromoCodeFieldsName[];
 extern const char kAutofillFillMerchantPromoCodeFieldsDescription[];
 
-extern const char kAutofillFixOfferInIncognitoName[];
-extern const char kAutofillFixOfferInIncognitoDescription[];
-
 extern const char kAutofillHighlightOnlyChangedValuesInPreviewModeName[];
 extern const char kAutofillHighlightOnlyChangedValuesInPreviewModeDescription[];
 
@@ -2012,10 +2006,6 @@
 extern const char kRequestDesktopSiteGlobalName[];
 extern const char kRequestDesktopSiteGlobalDescription[];
 
-extern const char kShareUsageRankingName[];
-extern const char kShareUsageRankingDescription[];
-extern const char kShareUsageRankingFixedMoreName[];
-extern const char kShareUsageRankingFixedMoreDescription[];
 extern const char kSwapAndroidShareHubRowsName[];
 extern const char kSwapAndroidShareHubRowsDescription[];
 
diff --git a/chrome/browser/flags/android/chrome_feature_list.cc b/chrome/browser/flags/android/chrome_feature_list.cc
index e33be52..89d7b56 100644
--- a/chrome/browser/flags/android/chrome_feature_list.cc
+++ b/chrome/browser/flags/android/chrome_feature_list.cc
@@ -125,8 +125,6 @@
     &features::kPwaUpdateDialogForNameAndIcon,
     &features::kQuietNotificationPrompts,
     &features::kRequestDesktopSiteForTablets,
-    &features::kShareUsageRanking,
-    &features::kShareUsageRankingFixedMore,
     &features::kToolbarUseHardwareBitmapDraw,
     &features::kWebNfc,
     &features::kIncognitoNtpRevamp,
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 bae18ab..dbe9cafd 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
@@ -476,8 +476,6 @@
             "ServiceManagerForBackgroundPrefetch";
     public static final String SERVICE_MANAGER_FOR_DOWNLOAD = "ServiceManagerForDownload";
     public static final String SHARE_BUTTON_IN_TOP_TOOLBAR = "ShareButtonInTopToolbar";
-    public static final String SHARE_USAGE_RANKING = "ShareUsageRanking";
-    public static final String SHARE_USAGE_RANKING_FIXED_MORE = "ShareUsageRankingFixedMore";
     public static final String SHARED_CLIPBOARD_UI = "SharedClipboardUI";
     public static final String SHARED_HIGHLIGHTING_V2 = "SharedHighlightingV2";
     public static final String SHARED_HIGHLIGHTING_AMP = "SharedHighlightingAmp";
diff --git a/chrome/browser/fuchsia/chrome_browser_main_parts_fuchsia.cc b/chrome/browser/fuchsia/chrome_browser_main_parts_fuchsia.cc
index f6712a6..ea5e28a4 100644
--- a/chrome/browser/fuchsia/chrome_browser_main_parts_fuchsia.cc
+++ b/chrome/browser/fuchsia/chrome_browser_main_parts_fuchsia.cc
@@ -8,33 +8,25 @@
 #include <fuchsia/ui/app/cpp/fidl.h>
 #include <fuchsia/ui/composition/cpp/fidl.h>
 #include <fuchsia/ui/scenic/cpp/fidl.h>
-#include <fuchsia/ui/views/cpp/fidl.h>
 #include <lib/sys/cpp/component_context.h>
-#include <lib/ui/scenic/cpp/commands.h>
-#include <lib/ui/scenic/cpp/resources.h>
-#include <lib/ui/scenic/cpp/session.h>
-#include <lib/ui/scenic/cpp/view_identity.h>
 
 #include <memory>
 #include <utility>
-#include <vector>
 
 #include "base/bind.h"
 #include "base/check.h"
+#include "base/command_line.h"
+#include "base/files/file_path.h"
 #include "base/fuchsia/fuchsia_logging.h"
 #include "base/fuchsia/process_context.h"
 #include "base/fuchsia/process_lifecycle.h"
 #include "base/fuchsia/scoped_service_binding.h"
 #include "base/memory/raw_ptr.h"
-#include "base/notreached.h"
-#include "base/numerics/clamped_math.h"
 #include "chrome/browser/fuchsia/element_manager_impl.h"
-#include "chrome/browser/fuchsia/switches.h"
 #include "chrome/browser/lifetime/application_lifetime.h"
 #include "chrome/common/chrome_switches.h"
 #include "components/keep_alive_registry/keep_alive_types.h"
 #include "components/keep_alive_registry/scoped_keep_alive.h"
-#include "ui/gfx/geometry/size.h"
 #include "ui/gfx/switches.h"
 #include "ui/ozone/public/ozone_switches.h"
 #include "ui/platform_window/fuchsia/initialize_presenter_api_view.h"
@@ -60,21 +52,9 @@
                                   scenic_uses_flatland ? "flatland" : "scenic");
 }
 
-void HandleCFv2Argument() {
-  base::CommandLine* const launch_args = base::CommandLine::ForCurrentProcess();
-  if (!launch_args->HasSwitch(switches::kEnableCFv2)) {
-    return;
-  }
-  launch_args->AppendSwitch(switches::kNoStartupWindow);
-}
-
-fuchsia::ui::views::ViewRef CloneViewRef(
-    const fuchsia::ui::views::ViewRef& view_ref) {
-  fuchsia::ui::views::ViewRef dup;
-  zx_status_t status =
-      view_ref.reference.duplicate(ZX_RIGHT_SAME_RIGHTS, &dup.reference);
-  ZX_CHECK(status == ZX_OK, status) << "zx_object_duplicate";
-  return dup;
+void EnsureChromeStartsInBackground() {
+  base::CommandLine::ForCurrentProcess()->AppendSwitch(
+      switches::kNoStartupWindow);
 }
 
 bool NotifyNewBrowserWindow(const base::CommandLine& command_line) {
@@ -83,452 +63,28 @@
       command_line, path);
 }
 
-// ViewProviderScenic ----------------------------------------------------------
-
-// ViewProvider implementation that provides a single view and exposes all
-// requested views from OzonePlatformScenic inside it. This class owns the top
-// level Scenic session.
-// TODO(fxbug.dev/94001): Delete ViewProviderScenic after Flatland migration
-// is completed.
-class ViewProviderScenic : public fuchsia::ui::app::ViewProvider {
- public:
-  ViewProviderScenic()
-      : scenic_(base::ComponentContextForProcess()
-                    ->svc()
-                    ->Connect<fuchsia::ui::scenic::Scenic>()),
-        scenic_session_(scenic_.get(), focuser_.NewRequest()) {
-    // This is safe since the callback is overwritten in dtor.
-    ui::fuchsia::SetScenicViewPresenter(base::BindRepeating(
-        &ViewProviderScenic::PresentView, base::Unretained(this)));
-
-    scenic_.set_error_handler(base::LogFidlErrorAndExitProcess(
-        FROM_HERE, "fuchsia.ui.scenic.Scenic"));
-    scenic_session_.set_event_handler(
-        fit::bind_member(this, &ViewProviderScenic::OnScenicEvents));
-  }
-  ViewProviderScenic(const ViewProviderScenic&) = delete;
-  ViewProviderScenic& operator=(const ViewProviderScenic&) = delete;
-  ~ViewProviderScenic() override {
-    ui::fuchsia::SetScenicViewPresenter(
-        ui::fuchsia::ScenicPresentViewCallback());
-    scenic_.Unbind();
-  }
-
-  fuchsia::element::ViewControllerPtr PresentView(
-      fuchsia::ui::views::ViewHolderToken view_holder_token,
-      fuchsia::ui::views::ViewRef view_ref) {
-    ScenicSubViewData subview = {
-        .view_holder = scenic::ViewHolder(&scenic_session_,
-                                          std::move(view_holder_token).value,
-                                          "subview-holder"),
-        .view_ref = std::move(view_ref)};
-    if (view_) {
-      if (view_properties_) {
-        subview.view_holder.SetViewProperties(*view_properties_);
-      }
-      node_->AddChild(subview.view_holder);
-      Present();
-    }
-    subviews_.push_back(std::move(subview));
-    return nullptr;
-  }
-
-  // fuchsia::ui::app::ViewProvider overrides.
-  void CreateView(
-      zx::eventpair token,
-      fidl::InterfaceRequest<fuchsia::sys::ServiceProvider> incoming_services,
-      fidl::InterfaceHandle<fuchsia::sys::ServiceProvider> outgoing_services)
-      override {
-    CreateViewWithViewRef(std::move(token), {}, {});
-  }
-  void CreateViewWithViewRef(zx::eventpair token,
-                             fuchsia::ui::views::ViewRefControl control_ref,
-                             fuchsia::ui::views::ViewRef view_ref) override {
-    if (view_) {
-      LOG(WARNING) << "Unexpected spurious call to CreateViewWithViewRef(). "
-                      "Deleting previously created view.";
-      subviews_.clear();
-      is_node_attached_ = false;
-      view_has_focus_ = false;
-      node_.reset();
-      view_.reset();
-      view_properties_ = absl::nullopt;
-    }
-
-    view_ = std::make_unique<scenic::View>(
-        &scenic_session_, fuchsia::ui::views::ViewToken({std::move(token)}),
-        std::move(control_ref), std::move(view_ref), "root-view");
-    node_ = std::make_unique<scenic::EntityNode>(&scenic_session_);
-    for (auto& subview : subviews_) {
-      node_->AddChild(subview.view_holder);
-    }
-    Present();
-  }
-  void CreateView2(fuchsia::ui::app::CreateView2Args view_args) override {
-    // Unexpected call to CreateView2(). OzonePlatformScenic cannot handle
-    // Flatland tokens. Make sure the correct Ozone platform is set.
-    NOTREACHED();
-  }
-
- private:
-  struct ScenicSubViewData {
-    scenic::ViewHolder view_holder;
-    fuchsia::ui::views::ViewRef view_ref;
-  };
-
-  void OnScenicEvents(std::vector<fuchsia::ui::scenic::Event> events) {
-    for (const auto& event : events) {
-      if (event.is_gfx() && event.gfx().is_view_properties_changed()) {
-        if (event.gfx().view_properties_changed().view_id != view_->id()) {
-          LOG(WARNING) << "Received event for unknown view.";
-          return;
-        }
-        UpdateViewProperties(event.gfx().view_properties_changed().properties);
-      } else if (event.is_input() && event.input().is_focus()) {
-        view_has_focus_ = event.input().focus().focused;
-        FocusView();
-      }
-    }
-  }
-
-  void UpdateViewProperties(
-      const fuchsia::ui::gfx::ViewProperties& view_properties) {
-    const float width =
-        view_properties.bounding_box.max.x - view_properties.bounding_box.min.x;
-    const float height =
-        view_properties.bounding_box.max.y - view_properties.bounding_box.min.y;
-    if (width == 0 || height == 0) {
-      if (is_node_attached_) {
-        node_->Detach();
-        is_node_attached_ = false;
-      }
-    } else {
-      if (!is_node_attached_) {
-        view_->AddChild(*node_);
-        is_node_attached_ = true;
-      }
-    }
-
-    view_properties_ = view_properties;
-    for (auto& subview : subviews_) {
-      subview.view_holder.SetViewProperties(*view_properties_);
-    }
-    Present();
-  }
-
-  void FocusView(int tries = 2) {
-    if (tries == 0) {
-      LOG(ERROR) << "Unable to pass focus to chrome window.";
-      return;
-    }
-    if (!subviews_.empty() && view_has_focus_) {
-      focuser_->RequestFocus({CloneViewRef(subviews_.front().view_ref)},
-                             [this, tries](auto result) {
-                               if (result.is_err()) {
-                                 FocusView(tries - 1);
-                               }
-                             });
-    }
-  }
-
-  void Present() {
-    scenic_session_.Present(
-        zx_clock_get_monotonic(),
-        [this](fuchsia::images::PresentationInfo info) { FocusView(); });
-  }
-
-  fuchsia::ui::scenic::ScenicPtr scenic_;
-  fuchsia::ui::views::FocuserPtr focuser_;
-  scenic::Session scenic_session_;
-
-  // The view created by this ViewProvider. The view is created lazily when a
-  // request is received.
-  std::unique_ptr<scenic::View> view_;
-
-  // Entity node for the |view_|.
-  std::unique_ptr<scenic::EntityNode> node_;
-
-  // True if the root EntityNode has been added to the View.
-  bool is_node_attached_ = false;
-
-  // True is the root view has focus.
-  bool view_has_focus_ = false;
-
-  // The holders for all the views that are presented.
-  std::vector<ScenicSubViewData> subviews_;
-
-  // The properties of the top level view. They are forwarded to the embedded
-  // views.
-  absl::optional<fuchsia::ui::gfx::ViewProperties> view_properties_;
-};
-
-// ViewProviderFlatland --------------------------------------------------------
-
-// ViewProvider implementation that provides a single view and exposes all
-// requested views from OzonePlatformFlatland inside it. This class owns the top
-// level Flatland session.
-class ViewProviderFlatland : public fuchsia::ui::app::ViewProvider,
-                             public fuchsia::element::ViewController {
- public:
-  ViewProviderFlatland() {
-    // This is safe since the callback is overwritten in dtor.
-    ui::fuchsia::SetFlatlandViewPresenter(base::BindRepeating(
-        &ViewProviderFlatland::PresentView, base::Unretained(this)));
-
-    flatland_.set_error_handler(
-        base::LogFidlErrorAndExitProcess(FROM_HERE, "Flatland"));
-    flatland_->SetDebugName("ChromeBrowserMainPartsFuchsia");
-    flatland_.events().OnError =
-        [](fuchsia::ui::composition::FlatlandError error) {
-          LOG(ERROR) << "Flatland error: " << static_cast<int>(error);
-        };
-    flatland_.events().OnNextFrameBegin =
-        fit::bind_member(this, &ViewProviderFlatland::OnNextFrameBegin);
-
-    // Each Flatland session requires defining a root transform.
-    flatland_->CreateTransform(kRootTransformId);
-    flatland_->SetRootTransform(kRootTransformId);
-  }
-  ViewProviderFlatland(const ViewProviderFlatland&) = delete;
-  ViewProviderFlatland& operator=(const ViewProviderFlatland&) = delete;
-  ~ViewProviderFlatland() override {
-    ui::fuchsia::SetFlatlandViewPresenter(
-        ui::fuchsia::FlatlandPresentViewCallback());
-  }
-
-  // fuchsia::ui::app::ViewProvider overrides.
-  void CreateView(
-      zx::eventpair token,
-      fidl::InterfaceRequest<fuchsia::sys::ServiceProvider> incoming_services,
-      fidl::InterfaceHandle<fuchsia::sys::ServiceProvider> outgoing_services)
-      override {
-    // Unexpected call to CreateView(). OzonePlatformFlatland cannot handle Gfx
-    // tokens. Make sure the correct Ozone platform is set.
-    NOTREACHED();
-  }
-  void CreateViewWithViewRef(zx::eventpair token,
-                             fuchsia::ui::views::ViewRefControl control_ref,
-                             fuchsia::ui::views::ViewRef view_ref) override {
-    // Unexpected call to CreateViewWithViewRef(). OzonePlatformFlatland cannot
-    // handle Gfx tokens. Make sure the correct Ozone platform is set.
-    NOTREACHED();
-  }
-  void CreateView2(fuchsia::ui::app::CreateView2Args view_args) override {
-    if (parent_viewport_watcher_.is_bound()) {
-      LOG(ERROR) << "Unexpected spurious call to CreateView2().";
-      return;
-    }
-
-    auto view_identity = scenic::NewViewIdentityOnCreation();
-    fuchsia::ui::composition::ViewBoundProtocols flatland_view_protocols;
-    flatland_view_protocols.set_view_ref_focused(
-        view_ref_focused_.NewRequest());
-    flatland_view_protocols.set_view_focuser(focuser_.NewRequest());
-    flatland_->CreateView2(std::move(*view_args.mutable_view_creation_token()),
-                           std::move(view_identity),
-                           std::move(flatland_view_protocols),
-                           parent_viewport_watcher_.NewRequest());
-    for (auto& subview : subviews_) {
-      flatland_->AddChild(kRootTransformId, subview.transform_id);
-    }
-    // No need to call Present() because OnGetLayout() will trigger Present()
-    // after receiving the proper size.
-    parent_viewport_watcher_->GetLayout(
-        fit::bind_member(this, &ViewProviderFlatland::OnGetLayout));
-    view_ref_focused_->Watch(
-        fit::bind_member(this, &ViewProviderFlatland::OnViewRefFocused));
-  }
-
-  // fuchsia::element::ViewController override
-  void Dismiss() override {
-    // Ignoring dismiss requests.
-  }
-
-  fuchsia::element::ViewControllerPtr PresentView(
-      fuchsia::ui::views::ViewportCreationToken viewport_creation_token) {
-    // Flatland requires a size to be set in CreateViewport() calls, so wait for
-    // receiving size before handling the presentation request. Flatland
-    // guarantees that the size returned in OnGetLayout() hanging get is
-    // non-zero. Size can be zero if we are running this code before
-    // OnGetLayout().
-    if (view_size_.IsZero()) {
-      pending_present_views_.push_back(std::move(viewport_creation_token));
-      return nullptr;
-    }
-
-    FlatlandSubViewData subview;
-    subview.transform_id = {next_id_++};
-    subview.content_id = {next_id_++};
-    flatland_->CreateTransform(subview.transform_id);
-    fuchsia::ui::composition::ViewportProperties properties;
-    properties.set_logical_size({static_cast<uint32_t>(view_size_.width()),
-                                 static_cast<uint32_t>(view_size_.height())});
-    flatland_->CreateViewport(
-        subview.content_id, std::move(viewport_creation_token),
-        std::move(properties), subview.child_view_watcher.NewRequest());
-    flatland_->SetContent(subview.transform_id, subview.content_id);
-    flatland_->AddChild(kRootTransformId, subview.transform_id);
-    subviews_.push_back(std::move(subview));
-
-    MaybePresent();
-    subviews_.back().child_view_watcher->GetViewRef(
-        [this, index = subviews_.size() - 1](fuchsia::ui::views::ViewRef ref) {
-          subviews_[index].child_view_ref = std::move(ref);
-          RequestFocus();
-        });
-    return nullptr;
-  }
-
- private:
-  struct FlatlandSubViewData {
-    fuchsia::ui::composition::TransformId transform_id;
-    fuchsia::ui::composition::ContentId content_id;
-    fuchsia::ui::composition::ChildViewWatcherPtr child_view_watcher;
-    fuchsia::ui::views::ViewRef child_view_ref;
-  };
-
-  void OnGetLayout(fuchsia::ui::composition::LayoutInfo info) {
-    const bool first_received_size = view_size_.IsZero();
-    view_size_.SetSize(info.logical_size().width, info.logical_size().height);
-
-    // If there were PresentView() calls before receiving OnGetLayout(), execute
-    // them.
-    if (first_received_size) {
-      for (auto& present_view : std::exchange(pending_present_views_, {})) {
-        PresentView(std::move(present_view));
-      }
-    } else {
-      for (const auto& subview : subviews_) {
-        fuchsia::ui::composition::ViewportProperties properties;
-        properties.set_logical_size(info.logical_size());
-        flatland_->SetViewportProperties(subview.content_id,
-                                         std::move(properties));
-      }
-      MaybePresent();
-    }
-
-    // Queue another hanging get in case the size changes.
-    parent_viewport_watcher_->GetLayout(
-        fit::bind_member(this, &ViewProviderFlatland::OnGetLayout));
-  }
-
-  void OnViewRefFocused(fuchsia::ui::views::FocusState focus_state) {
-    view_has_focus_ = focus_state.focused();
-    RequestFocus();
-
-    view_ref_focused_->Watch(
-        fit::bind_member(this, &ViewProviderFlatland::OnViewRefFocused));
-  }
-
-  void RequestFocus() {
-    if (!view_has_focus_ || subviews_.empty() ||
-        !subviews_.front().child_view_ref.reference.is_valid())
-      return;
-
-    focuser_->RequestFocus(
-        CloneViewRef(subviews_.front().child_view_ref),
-        [](fuchsia::ui::views::Focuser_RequestFocus_Result result) {
-          DCHECK(!result.is_err());
-        });
-  }
-
-  void MaybePresent() {
-    if (present_credits_ == 0) {
-      present_after_receiving_credits_ = true;
-      return;
-    }
-
-    present_after_receiving_credits_ = false;
-    --present_credits_;
-    Present();
-  }
-
-  void Present() {
-    fuchsia::ui::composition::PresentArgs present_args;
-    present_args.set_requested_presentation_time(0);
-    present_args.set_acquire_fences({});
-    present_args.set_release_fences({});
-    present_args.set_unsquashable(false);
-    flatland_->Present(std::move(present_args));
-  }
-
-  void OnNextFrameBegin(
-      fuchsia::ui::composition::OnNextFrameBeginValues values) {
-    present_credits_ =
-        base::ClampAdd(present_credits_, values.additional_present_credits());
-    if (present_after_receiving_credits_) {
-      MaybePresent();
-    }
-  }
-
-  fuchsia::ui::composition::FlatlandPtr flatland_ = {
-      base::ComponentContextForProcess()
-          ->svc()
-          ->Connect<fuchsia::ui::composition::Flatland>()};
-
-  // The counter used for limiting the number of Present calls to ensure that
-  // |flatland_|will not be shut down because if presenting more times than
-  // allowed.
-  uint32_t present_credits_ = 1;
-
-  // Root transform for |flatland_|. All |subviews_| are added as children.
-  static const fuchsia::ui::composition::TransformId kRootTransformId;
-
-  // Autoincrementing value of the next ID to use for |flatland_|.
-  uint64_t next_id_ = 2;
-
-  // True if we should queue Present() after receiving credits on
-  // OnNextFrameBegin().
-  bool present_after_receiving_credits_ = false;
-
-  // True if the top level view has focus.
-  bool view_has_focus_ = false;
-
-  // Protocol for watching focus changes.
-  fuchsia::ui::views::ViewRefFocusedPtr view_ref_focused_;
-
-  // Protocol for setting focus changes.
-  fuchsia::ui::views::FocuserPtr focuser_;
-
-  // Protocol for watching size changes.
-  fuchsia::ui::composition::ParentViewportWatcherPtr parent_viewport_watcher_;
-
-  // The layout size of the View occupied by |flatland_| in logical pixels.
-  gfx::Size view_size_;
-
-  // Pending ViewportCreationTokens to be processed and added as |subviews_|.
-  std::vector<fuchsia::ui::views::ViewportCreationToken> pending_present_views_;
-
-  // The holders for all the views that are presented.
-  std::vector<FlatlandSubViewData> subviews_;
-};
-
-// static
-constexpr fuchsia::ui::composition::TransformId
-    ViewProviderFlatland::kRootTransformId{1};
-
 }  // namespace
 
 // Helper class that configures Ozone to use GraphicalPresenter to display a new
 // View for each new top-level window.
-class ChromeBrowserMainPartsFuchsia::UseGraphicalPresenter final {
+class ChromeBrowserMainPartsFuchsia::ViewPresenter final {
  public:
-  explicit UseGraphicalPresenter(ElementManagerImpl* element_manager)
+  explicit ViewPresenter(ElementManagerImpl* element_manager)
       : element_manager_(element_manager) {
     ui::fuchsia::SetScenicViewPresenter(base::BindRepeating(
-        &UseGraphicalPresenter::PresentScenicView, base::Unretained(this)));
+        &ViewPresenter::PresentScenicView, base::Unretained(this)));
     ui::fuchsia::SetFlatlandViewPresenter(base::BindRepeating(
-        &UseGraphicalPresenter::PresentFlatlandView, base::Unretained(this)));
+        &ViewPresenter::PresentFlatlandView, base::Unretained(this)));
 
     base::ComponentContextForProcess()->svc()->Connect(
         graphical_presenter_.NewRequest());
     graphical_presenter_.set_error_handler(base::LogFidlErrorAndExitProcess(
         FROM_HERE, "fuchsia.element.GraphicalPresenter"));
   }
-  ~UseGraphicalPresenter() = default;
+  ~ViewPresenter() = default;
 
-  UseGraphicalPresenter(const UseGraphicalPresenter&) = delete;
-  UseGraphicalPresenter& operator=(const UseGraphicalPresenter&) = delete;
+  ViewPresenter(const ViewPresenter&) = delete;
+  ViewPresenter& operator=(const ViewPresenter&) = delete;
 
  private:
   fuchsia::element::ViewControllerPtr PresentScenicView(
@@ -563,66 +119,6 @@
   fuchsia::element::GraphicalPresenterPtr graphical_presenter_;
 };
 
-// ViewProviderRouter ----------------------------------------------------------
-
-// ViewProvider implementation that delegates calls to the correct Ozone
-// platform's ViewProvider.
-// TODO(fxbug.dev/94001): Delete ViewProviderRouter after moving |binding_|
-// to ViewProviderFlatland after migration is completed.
-class ChromeBrowserMainPartsFuchsia::ViewProviderRouter
-    : public fuchsia::ui::app::ViewProvider {
- public:
-  ViewProviderRouter(std::unique_ptr<ViewProviderScenic> scenic,
-                     std::unique_ptr<ViewProviderFlatland> flatland)
-      : scenic_(std::move(scenic)), flatland_(std::move(flatland)) {
-    DCHECK(scenic_);
-    DCHECK(flatland_);
-  }
-  ViewProviderRouter(const ViewProviderRouter&) = delete;
-  ViewProviderRouter& operator=(const ViewProviderRouter&) = delete;
-  ~ViewProviderRouter() override = default;
-
-  // fuchsia::ui::app::ViewProvider overrides.
-  void CreateView(
-      zx::eventpair token,
-      fidl::InterfaceRequest<fuchsia::sys::ServiceProvider> incoming_services,
-      fidl::InterfaceHandle<fuchsia::sys::ServiceProvider> outgoing_services)
-      override {
-    // Chrome is initialized with either Scenic or flatland Ozone platform.
-    // Scenic and Flatland calls cannot be combined.
-    DCHECK(scenic_);
-    flatland_.reset();
-
-    scenic_->CreateView(std::move(token), std::move(incoming_services),
-                        std::move(outgoing_services));
-  }
-  void CreateViewWithViewRef(zx::eventpair token,
-                             fuchsia::ui::views::ViewRefControl control_ref,
-                             fuchsia::ui::views::ViewRef view_ref) override {
-    // Chrome is initialized with either Scenic or flatland Ozone platform.
-    // Scenic and Flatland calls cannot be combined.
-    DCHECK(scenic_);
-    flatland_.reset();
-
-    scenic_->CreateViewWithViewRef(std::move(token), std::move(control_ref),
-                                   std::move(view_ref));
-  }
-  void CreateView2(fuchsia::ui::app::CreateView2Args view_args) override {
-    // Chrome is initialized with either Scenic or flatland Ozone platform.
-    // Scenic and Flatland calls cannot be combined.
-    DCHECK(flatland_);
-    scenic_.reset();
-
-    flatland_->CreateView2(std::move(view_args));
-  }
-
- private:
-  const base::ScopedServiceBinding<fuchsia::ui::app::ViewProvider> binding_ = {
-      base::ComponentContextForProcess()->outgoing().get(), this};
-  std::unique_ptr<ViewProviderScenic> scenic_;
-  std::unique_ptr<ViewProviderFlatland> flatland_;
-};
-
 // ChromeBrowserMainPartsFuchsia -----------------------------------------------
 
 ChromeBrowserMainPartsFuchsia::ChromeBrowserMainPartsFuchsia(
@@ -640,29 +136,20 @@
 
 int ChromeBrowserMainPartsFuchsia::PreEarlyInitialization() {
   HandleOzonePlatformArgs();
-  HandleCFv2Argument();
+  EnsureChromeStartsInBackground();
   return ChromeBrowserMainParts::PreEarlyInitialization();
 }
 
 int ChromeBrowserMainPartsFuchsia::PreMainMessageLoopRun() {
   if (!base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kHeadless)) {
-    if (base::CommandLine::ForCurrentProcess()->HasSwitch(
-            switches::kEnableCFv2)) {
-      // Configure Ozone to create top-level Views via GraphicalPresenter.
-      element_manager_ = std::make_unique<ElementManagerImpl>(
-          base::ComponentContextForProcess()->outgoing().get(),
-          base::BindRepeating(&NotifyNewBrowserWindow));
-      keep_alive_ = std::make_unique<ScopedKeepAlive>(
-          KeepAliveOrigin::BROWSER_PROCESS_FUCHSIA,
-          KeepAliveRestartOption::ENABLED);
-      use_graphical_presenter_ =
-          std::make_unique<UseGraphicalPresenter>(element_manager_.get());
-    } else {
-      // Register the ViewProvider API.
-      view_provider_ = std::make_unique<ViewProviderRouter>(
-          std::make_unique<ViewProviderScenic>(),
-          std::make_unique<ViewProviderFlatland>());
-    }
+    // Configure Ozone to create top-level Views via GraphicalPresenter.
+    element_manager_ = std::make_unique<ElementManagerImpl>(
+        base::ComponentContextForProcess()->outgoing().get(),
+        base::BindRepeating(&NotifyNewBrowserWindow));
+    keep_alive_ = std::make_unique<ScopedKeepAlive>(
+        KeepAliveOrigin::BROWSER_PROCESS_FUCHSIA,
+        KeepAliveRestartOption::ENABLED);
+    view_presenter_ = std::make_unique<ViewPresenter>(element_manager_.get());
   }
 
   zx_status_t status =
@@ -679,11 +166,3 @@
 
   return ChromeBrowserMainParts::PreMainMessageLoopRun();
 }
-
-void ChromeBrowserMainPartsFuchsia::PostMainMessageLoopRun() {
-  // |view_provider_| owns ViewProviderScenic and ViewProviderFlatland. They own
-  // the Scenic channels so resetting here will unbind.
-  view_provider_.reset();
-
-  ChromeBrowserMainParts::PostMainMessageLoopRun();
-}
diff --git a/chrome/browser/fuchsia/chrome_browser_main_parts_fuchsia.h b/chrome/browser/fuchsia/chrome_browser_main_parts_fuchsia.h
index f4fce9d..4e63332 100644
--- a/chrome/browser/fuchsia/chrome_browser_main_parts_fuchsia.h
+++ b/chrome/browser/fuchsia/chrome_browser_main_parts_fuchsia.h
@@ -32,24 +32,23 @@
   // content::BrowserMainParts overrides.
   int PreEarlyInitialization() override;
   int PreMainMessageLoopRun() override;
-  void PostMainMessageLoopRun() override;
 
  private:
-  class UseGraphicalPresenter;
-  class ViewProviderRouter;
+  class ViewPresenter;
 
   std::unique_ptr<base::ProcessLifecycle> lifecycle_;
 
-  // Implementations used when running under CFv2. Under CFv2 Chrome runs in the
-  // background, only opening windows when requested to via the
-  // fuchsia.element.Manager service. The browser process must remain live until
-  // explicitly torn-down by the ELF runner.
+  // Used to allow the shell to (re)open Chrome windows
   std::unique_ptr<ElementManagerImpl> element_manager_;
-  std::unique_ptr<ScopedKeepAlive> keep_alive_;
-  std::unique_ptr<UseGraphicalPresenter> use_graphical_presenter_;
 
-  // TODO(crbug.com/1284806): Remove this once ViewProvider is deprecated.
-  std::unique_ptr<ViewProviderRouter> view_provider_;
+  // Under CFv2 Chrome runs in the background, only opening windows when
+  // requested to via the fuchsia.element.Manager service. The browser process
+  // must remain live until explicitly torn-down by the ELF runner.
+  std::unique_ptr<ScopedKeepAlive> keep_alive_;
+
+  // Helper class that configures Ozone to use GraphicalPresenter to display a
+  // new View for each new top-level window.
+  std::unique_ptr<ViewPresenter> view_presenter_;
 };
 
 #endif  // CHROME_BROWSER_FUCHSIA_CHROME_BROWSER_MAIN_PARTS_FUCHSIA_H_
diff --git a/chrome/browser/fuchsia/switches.cc b/chrome/browser/fuchsia/switches.cc
deleted file mode 100644
index a0319d6..0000000
--- a/chrome/browser/fuchsia/switches.cc
+++ /dev/null
@@ -1,12 +0,0 @@
-// Copyright 2022 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/fuchsia/switches.h"
-
-namespace switches {
-
-// Enables running Chromium as a v2 component.
-const char kEnableCFv2[] = "enable-cfv2";
-
-}  // namespace switches
diff --git a/chrome/browser/fuchsia/switches.h b/chrome/browser/fuchsia/switches.h
deleted file mode 100644
index 72f46ae..0000000
--- a/chrome/browser/fuchsia/switches.h
+++ /dev/null
@@ -1,14 +0,0 @@
-// Copyright 2022 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_FUCHSIA_SWITCHES_H_
-#define CHROME_BROWSER_FUCHSIA_SWITCHES_H_
-
-namespace switches {
-
-extern const char kEnableCFv2[];
-
-}
-
-#endif  // CHROME_BROWSER_FUCHSIA_SWITCHES_H_
diff --git a/chrome/browser/media/encrypted_media_browsertest.cc b/chrome/browser/media/encrypted_media_browsertest.cc
index fbc3c4e3..99e4bb6 100644
--- a/chrome/browser/media/encrypted_media_browsertest.cc
+++ b/chrome/browser/media/encrypted_media_browsertest.cc
@@ -634,8 +634,9 @@
   TestSimplePlayback("bear-320x240-v_frag-vp9-cenc.mp4");
 }
 
-#if BUILDFLAG(IS_MAC)
+#if BUILDFLAG(IS_MAC) || (BUILDFLAG(IS_FUCHSIA) && defined(PLATFORM_IS_ARM))
 // TODO(https://crbug.com/1250305): Fails on dcheck-enabled builds on 11.0.
+// TODO(https://crbug.com/1280308): Fails on Fuchsia-arm64
 #define MAYBE_Playback_VideoOnly_WebM_VP9Profile2 \
   DISABLED_Playback_VideoOnly_WebM_VP9Profile2
 #else
@@ -647,8 +648,9 @@
   TestSimplePlayback("bear-320x240-v-vp9_profile2_subsample_cenc-v.webm");
 }
 
-#if BUILDFLAG(IS_MAC)
+#if BUILDFLAG(IS_MAC) || (BUILDFLAG(IS_FUCHSIA) && defined(PLATFORM_IS_ARM))
 // TODO(https://crbug.com/1250305): Fails on dcheck-enabled builds on 11.0.
+// TODO(https://crbug.com/1280308): Fails on Fuchsia-arm64
 #define MAYBE_Playback_VideoOnly_MP4_VP9Profile2 \
   DISABLED_Playback_VideoOnly_MP4_VP9Profile2
 #else
diff --git a/chrome/browser/nearby_sharing/bluetooth_advertising_interval_client_unittest.cc b/chrome/browser/nearby_sharing/bluetooth_advertising_interval_client_unittest.cc
index 5ef859d..0f6725a4 100644
--- a/chrome/browser/nearby_sharing/bluetooth_advertising_interval_client_unittest.cc
+++ b/chrome/browser/nearby_sharing/bluetooth_advertising_interval_client_unittest.cc
@@ -101,14 +101,14 @@
 
 TEST_F(BluetoothAdvertisingIntervalClientTest, SetAndRestore) {
   client_->ReduceInterval();
-  EXPECT_EQ(1, set_advertising_interval_call_count());
-  EXPECT_EQ(0, set_advertising_interval_error_call_count());
+  EXPECT_EQ(1u, set_advertising_interval_call_count());
+  EXPECT_EQ(0u, set_advertising_interval_error_call_count());
   EXPECT_EQ(kInterval, last_advertising_interval_min());
   EXPECT_EQ(kInterval, last_advertising_interval_max());
 
   RestoreDefaultInterval();
-  EXPECT_EQ(2, set_advertising_interval_call_count());
-  EXPECT_EQ(0, set_advertising_interval_error_call_count());
+  EXPECT_EQ(2u, set_advertising_interval_call_count());
+  EXPECT_EQ(0u, set_advertising_interval_error_call_count());
   EXPECT_EQ(kDefaultInterval, last_advertising_interval_min());
   EXPECT_EQ(kDefaultInterval, last_advertising_interval_max());
 }
@@ -116,6 +116,6 @@
 TEST_F(BluetoothAdvertisingIntervalClientTest, SetError) {
   mock_adapter_->SetAdvertisingIntervalError(true);
   client_->ReduceInterval();
-  EXPECT_EQ(0, set_advertising_interval_call_count());
-  EXPECT_EQ(1, set_advertising_interval_error_call_count());
+  EXPECT_EQ(0u, set_advertising_interval_call_count());
+  EXPECT_EQ(1u, set_advertising_interval_error_call_count());
 }
diff --git a/chrome/browser/pdf/pdf_find_request_manager_browsertest.cc b/chrome/browser/pdf/pdf_find_request_manager_browsertest.cc
index efc5eca2..ea18cdea 100644
--- a/chrome/browser/pdf/pdf_find_request_manager_browsertest.cc
+++ b/chrome/browser/pdf/pdf_find_request_manager_browsertest.cc
@@ -180,10 +180,8 @@
 
 // Tests searching in a PDF received in chunks via range-requests.  See also
 // https://crbug.com/1027173.
-
-// TODO(crbug.com/1247167): Flaky.
 IN_PROC_BROWSER_TEST_P(PdfFindRequestManagerTestWithPdfPartialLoading,
-                       DISABLED_FindInChunkedPDF) {
+                       FindInChunkedPDF) {
   constexpr uint32_t kStalledResponseSize =
       chrome_pdf::DocumentLoaderImpl::kDefaultRequestSize + 123;
 
diff --git a/chrome/browser/performance_hints/performance_hints_observer.cc b/chrome/browser/performance_hints/performance_hints_observer.cc
index 6f1ae691..971bcca 100644
--- a/chrome/browser/performance_hints/performance_hints_observer.cc
+++ b/chrome/browser/performance_hints/performance_hints_observer.cc
@@ -356,7 +356,7 @@
     case OptimizationGuideDecision::kTrue: {
       // Link hints only contain scheme, host, and path, so remove other
       // components.
-      url::Replacements<char> replacements;
+      GURL::Replacements replacements;
       replacements.ClearUsername();
       replacements.ClearPassword();
       replacements.ClearQuery();
diff --git a/chrome/browser/performance_manager/mechanisms/userspace_swap_chromeos.cc b/chrome/browser/performance_manager/mechanisms/userspace_swap_chromeos.cc
index e1e9a69..0a5e752 100644
--- a/chrome/browser/performance_manager/mechanisms/userspace_swap_chromeos.cc
+++ b/chrome/browser/performance_manager/mechanisms/userspace_swap_chromeos.cc
@@ -7,7 +7,6 @@
 #include <memory>
 
 #include "base/bind.h"
-#include "base/bits.h"
 #include "base/callback_helpers.h"
 #include "base/files/scoped_file.h"
 #include "base/memory/page_size.h"
diff --git a/chrome/browser/policy/configuration_policy_handler_list_factory.cc b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
index 6fb77fb1..6c4e2fd7 100644
--- a/chrome/browser/policy/configuration_policy_handler_list_factory.cc
+++ b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
@@ -932,9 +932,6 @@
   { key::kKeepFullscreenWithoutNotificationUrlAllowList,
     ash::prefs::kKeepFullscreenWithoutNotificationUrlAllowList,
     base::Value::Type::LIST },
-  { key::kFastPairEnabled,
-    ash::prefs::kFastPairEnabled,
-    base::Value::Type::BOOLEAN },
   { key::kDeviceLoginScreenDefaultLargeCursorEnabled,
     nullptr,
     base::Value::Type::BOOLEAN },
@@ -2197,6 +2194,8 @@
   handlers->AddHandler(std::make_unique<BooleanDisablingPolicyHandler>(
       key::kNearbyShareAllowed, prefs::kNearbySharingEnabledPrefName));
   handlers->AddHandler(std::make_unique<LacrosAvailabilityPolicyHandler>());
+  handlers->AddHandler(std::make_unique<BooleanDisablingPolicyHandler>(
+      key::kFastPairEnabled, ash::prefs::kFastPairEnabled));
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
 // On most platforms, there is a legacy policy
diff --git a/chrome/browser/privacy_sandbox/android/javatests/src/org/chromium/chrome/browser/privacy_sandbox/PrivacySandboxDialogTest.java b/chrome/browser/privacy_sandbox/android/javatests/src/org/chromium/chrome/browser/privacy_sandbox/PrivacySandboxDialogTest.java
index a70b5c61..9bf1285 100644
--- a/chrome/browser/privacy_sandbox/android/javatests/src/org/chromium/chrome/browser/privacy_sandbox/PrivacySandboxDialogTest.java
+++ b/chrome/browser/privacy_sandbox/android/javatests/src/org/chromium/chrome/browser/privacy_sandbox/PrivacySandboxDialogTest.java
@@ -37,6 +37,7 @@
 import org.chromium.base.StrictModeContext;
 import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.base.test.util.DisabledTest;
 import org.chromium.base.test.util.Feature;
 import org.chromium.base.test.util.JniMocker;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
@@ -58,6 +59,7 @@
 @RunWith(ChromeJUnit4ClassRunner.class)
 @Batch(Batch.PER_CLASS)
 @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
+@DisabledTest(message = "https://crbug.com/1300632")
 @Features.EnableFeatures(ChromeFeatureList.PRIVACY_SANDBOX_SETTINGS_3)
 public final class PrivacySandboxDialogTest {
     @ClassRule
diff --git a/chrome/browser/renderer_context_menu/link_to_text_menu_observer.cc b/chrome/browser/renderer_context_menu/link_to_text_menu_observer.cc
index f76ada36..e883d36a 100644
--- a/chrome/browser/renderer_context_menu/link_to_text_menu_observer.cc
+++ b/chrome/browser/renderer_context_menu/link_to_text_menu_observer.cc
@@ -380,7 +380,7 @@
   std::unique_ptr<ui::DataTransferEndpoint> data_transfer_endpoint =
       !render_frame_host_->GetBrowserContext()->IsOffTheRecord()
           ? std::make_unique<ui::DataTransferEndpoint>(
-                render_frame_host_->GetMainFrame()->GetLastCommittedOrigin())
+                render_frame_host_->GetMainFrame()->GetLastCommittedURL())
           : nullptr;
 
   ui::ScopedClipboardWriter scw(ui::ClipboardBuffer::kCopyPaste,
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu.cc b/chrome/browser/renderer_context_menu/render_view_context_menu.cc
index 91ad05b3..8b852b95 100644
--- a/chrome/browser/renderer_context_menu/render_view_context_menu.cc
+++ b/chrome/browser/renderer_context_menu/render_view_context_menu.cc
@@ -3071,7 +3071,7 @@
   if (render_frame_host &&
       !render_frame_host->GetBrowserContext()->IsOffTheRecord()) {
     return std::make_unique<ui::DataTransferEndpoint>(
-        render_frame_host->GetMainFrame()->GetLastCommittedOrigin(),
+        render_frame_host->GetMainFrame()->GetLastCommittedURL(),
         notify_if_restricted);
   }
   return nullptr;
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/accessibility_common_loader.js b/chrome/browser/resources/chromeos/accessibility/accessibility_common/accessibility_common_loader.js
index 24ce6cd00..d4ccd67 100644
--- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/accessibility_common_loader.js
+++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/accessibility_common_loader.js
@@ -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 {InstanceChecker} from '../common/instance_checker.js';
+
 import {Autoclick} from './autoclick/autoclick.js';
 import {Dictation} from './dictation/dictation.js';
 import {Magnifier} from './magnifier/magnifier.js';
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/background.html b/chrome/browser/resources/chromeos/accessibility/accessibility_common/background.html
index 73c2de1..e48fd356 100644
--- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/background.html
+++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/background.html
@@ -1,7 +1,6 @@
 <!-- Global scripts. -->
 <script src="/common/chrome_event_handler.js"></script>
 <script src="/common/closure_shim.js"></script>
-<script src="/common/instance_checker.js"></script>
 <script src="/common/event_generator.js"></script>
 <script src="/common/event_handler.js"></script>
 <script src="/common/key_code.js"></script>
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn b/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn
index a3b38cb5..6141d4b 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn
@@ -27,7 +27,6 @@
   "../common/cursors/recovery_strategy.js",
   "../common/event_generator.js",
   "../common/key_code.js",
-  "../common/instance_checker.js",
   "../common/string_util.js",
   "../common/tree_walker.js",
   "background/auto_scroll_handler.js",
@@ -120,6 +119,7 @@
 
 # ES6 modules.
 chromevox_es6_modules = [
+  "../common/instance_checker.js",
   "background/background.js",
   "background/earcon_engine.js",
   "background/earcons.js",
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/background.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/background.js
index 9dafd59..71f82706 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/background.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/background.js
@@ -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 {InstanceChecker} from '../../common/instance_checker.js';
+
 import {Earcons} from './earcons.js';
 import {FindHandler} from './find_handler.js';
 import {LiveRegions} from './live_regions.js';
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/loader.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/loader.js
index cad8879..7596b15 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/loader.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/loader.js
@@ -21,7 +21,6 @@
 goog.require('ExtensionBridge');
 goog.require('FocusAutomationHandler');
 goog.require('GestureCommandHandler');
-goog.require('InstanceChecker');
 goog.require('JaPhoneticMap');
 goog.require('LocaleOutputHelper');
 goog.require('MathHandler');
diff --git a/chrome/browser/resources/chromeos/accessibility/common/instance_checker.js b/chrome/browser/resources/chromeos/accessibility/common/instance_checker.js
index 09aa48d..9c9f0cf 100644
--- a/chrome/browser/resources/chromeos/accessibility/common/instance_checker.js
+++ b/chrome/browser/resources/chromeos/accessibility/common/instance_checker.js
@@ -2,13 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-goog.provide('InstanceChecker');
-
 /**
  * Used to prevent multiple instances of the extension from running
  * simultaneously.
  */
-const InstanceChecker = class {
+export class InstanceChecker {
   static closeExtraInstances() {
     // In 'split' manifest mode, the extension system runs two copies of the
     // extension. One in an incognito context; the other not. In guest mode, the
@@ -22,4 +20,4 @@
       window.close();
     }
   }
-};
+}
diff --git a/chrome/browser/resources/chromeos/accessibility/enhanced_network_tts/background.html b/chrome/browser/resources/chromeos/accessibility/enhanced_network_tts/background.html
index b2a4d2e..b731534 100644
--- a/chrome/browser/resources/chromeos/accessibility/enhanced_network_tts/background.html
+++ b/chrome/browser/resources/chromeos/accessibility/enhanced_network_tts/background.html
@@ -1,6 +1,5 @@
 <!-- Global scripts. -->
 <script src="/common/closure_shim.js"></script>
-<script src="/common/instance_checker.js"></script>
 
 <!-- Module entry point. -->
 <script type="module" src="/enhanced_network_tts/background.js"></script>
diff --git a/chrome/browser/resources/chromeos/accessibility/enhanced_network_tts/background.js b/chrome/browser/resources/chromeos/accessibility/enhanced_network_tts/background.js
index 8d6ed1b3..0bb3d23 100644
--- a/chrome/browser/resources/chromeos/accessibility/enhanced_network_tts/background.js
+++ b/chrome/browser/resources/chromeos/accessibility/enhanced_network_tts/background.js
@@ -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 {InstanceChecker} from '../common/instance_checker.js';
+
 import {EnhancedNetworkTts} from './enhanced_network_tts.js';
 
 InstanceChecker.closeExtraInstances();
diff --git a/chrome/browser/resources/chromeos/accessibility/select_to_speak/background.html b/chrome/browser/resources/chromeos/accessibility/select_to_speak/background.html
index cad25b8..b7ae5777 100644
--- a/chrome/browser/resources/chromeos/accessibility/select_to_speak/background.html
+++ b/chrome/browser/resources/chromeos/accessibility/select_to_speak/background.html
@@ -5,7 +5,6 @@
 <script src="/common/automation_predicate.js"></script>
 <script src="/common/automation_util.js"></script>
 <script src="/common/key_code.js"></script>
-<script src="/common/instance_checker.js"></script>
 <script src="/common/rect_util.js"></script>
 <script src="/common/tree_walker.js"></script>
 
diff --git a/chrome/browser/resources/chromeos/accessibility/select_to_speak/select_to_speak_main.js b/chrome/browser/resources/chromeos/accessibility/select_to_speak/select_to_speak_main.js
index 6f2df8f59..e6f18cd 100644
--- a/chrome/browser/resources/chromeos/accessibility/select_to_speak/select_to_speak_main.js
+++ b/chrome/browser/resources/chromeos/accessibility/select_to_speak/select_to_speak_main.js
@@ -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 {InstanceChecker} from '../common/instance_checker.js';
+
 import {SelectToSpeak} from './select_to_speak.js';
 
 InstanceChecker.closeExtraInstances();
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/background.html b/chrome/browser/resources/chromeos/accessibility/switch_access/background.html
index 7df4a815..147f6a4 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/background.html
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/background.html
@@ -7,7 +7,6 @@
 <script src="/common/event_generator.js"></script>
 <script src="/common/event_handler.js"></script>
 <script src="/common/key_code.js"></script>
-<script src="/common/instance_checker.js"></script>
 <script src="/common/rect_util.js"></script>
 <script src="/common/repeated_event_handler.js"></script>
 <script src="/common/repeated_tree_change_handler.js"></script>
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/background.js b/chrome/browser/resources/chromeos/accessibility/switch_access/background.js
index 0bdaae0..78491a2 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/background.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/background.js
@@ -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 {InstanceChecker} from '../common/instance_checker.js';
+
 import {SwitchAccess} from './switch_access.js';
 
 InstanceChecker.closeExtraInstances();
diff --git a/chrome/browser/resources/chromeos/multidevice_internals/multidevice_phonehub_browser_proxy.js b/chrome/browser/resources/chromeos/multidevice_internals/multidevice_phonehub_browser_proxy.js
index 6a0089c8..ee8cf63 100644
--- a/chrome/browser/resources/chromeos/multidevice_internals/multidevice_phonehub_browser_proxy.js
+++ b/chrome/browser/resources/chromeos/multidevice_internals/multidevice_phonehub_browser_proxy.js
@@ -2,9 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {addSingletonGetter} from 'chrome://resources/js/cr.m.js';
+import { addSingletonGetter } from 'chrome://resources/js/cr.m.js';
 
-import {BrowserTabsModel, CameraRollManager, FeatureStatus, FindMyDeviceStatus, Notification, PhoneStatusModel, TetherStatus} from './types.js';
+import { BrowserTabsModel, CameraRollManager, FeatureStatus, FindMyDeviceStatus, Notification, PhoneStatusModel, TetherStatus } from './types.js';
 
 /**
  * JavaScript hooks into the native WebUI handler for Phonehub tab.
@@ -111,8 +111,8 @@
    * Resets notification setup UI to not having been dismissed for the real
    * PhoneHubManager.
    */
-  resetHasNotificationSetupUiBeenDismissed() {
-    chrome.send('resetHasNotificationSetupUiBeenDismissed');
+  resetHasMultideviceFeatureSetupUiBeenDismissed() {
+    chrome.send('resetHasMultideviceFeatureSetupUiBeenDismissed');
   }
 
   /**
diff --git a/chrome/browser/resources/chromeos/multidevice_internals/phonehub_tab.html b/chrome/browser/resources/chromeos/multidevice_internals/phonehub_tab.html
index 3c923c8a..3df6f20 100644
--- a/chrome/browser/resources/chromeos/multidevice_internals/phonehub_tab.html
+++ b/chrome/browser/resources/chromeos/multidevice_internals/phonehub_tab.html
@@ -32,8 +32,7 @@
         Services" to off in Apps & notifications > Special app access >
         Notification access.
       </div>
-      <cr-button class="internals-button"
-          on-click="onResetHasNotificationSetupUiBeenDismissedButtonClick_">
+      <cr-button class="internals-button" on-click="onResetHasMultideviceFeatureSetupUiBeenDismissedButtonClick_">
         Reset
       </cr-button>
     </div>
@@ -44,8 +43,7 @@
         onboarding UI. Note that the user must have not started the opt-in flow
         yet.
       </div>
-      <cr-button class="internals-button"
-          on-click="onResetShouldShowOnboardingUiButtonClick_">
+      <cr-button class="internals-button" on-click="onResetShouldShowOnboardingUiButtonClick_">
         Reset
       </cr-button>
     </div>
@@ -57,8 +55,7 @@
         Photos from the settings page in order for the onboarding UI to
         reappear.
       </div>
-      <cr-button class="internals-button"
-          on-click="onResetCameraRollOnboardingUiDismissedButtonClick_">
+      <cr-button class="internals-button" on-click="onResetCameraRollOnboardingUiDismissedButtonClick_">
         Reset
       </cr-button>
     </div>
@@ -68,8 +65,7 @@
       <div class="cr-padded-text">
         Select feature status
       </div>
-      <select id="featureStatusList" class="md-select"
-          on-change="onFeatureStatusSelected_">
+      <select id="featureStatusList" class="md-select" on-change="onFeatureStatusSelected_">
         <template is="dom-repeat" items="[[featureStatusList_]]">
           <option selected="[[isEqual_(item, featureStatus_)]]">
             [[getFeatureStatusName_(item)]]
diff --git a/chrome/browser/resources/chromeos/multidevice_internals/phonehub_tab.js b/chrome/browser/resources/chromeos/multidevice_internals/phonehub_tab.js
index 9dda14bf..1b07f1ab 100644
--- a/chrome/browser/resources/chromeos/multidevice_internals/phonehub_tab.js
+++ b/chrome/browser/resources/chromeos/multidevice_internals/phonehub_tab.js
@@ -15,11 +15,11 @@
 import './shared_style.js';
 import './quick_action_controller_form.js';
 
-import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
-import {WebUIListenerBehavior} from 'chrome://resources/js/web_ui_listener_behavior.m.js';
-import {flush, html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-import {MultidevicePhoneHubBrowserProxy} from './multidevice_phonehub_browser_proxy.js';
-import {FeatureStatus} from './types.js';
+import { loadTimeData } from 'chrome://resources/js/load_time_data.m.js';
+import { WebUIListenerBehavior } from 'chrome://resources/js/web_ui_listener_behavior.m.js';
+import { flush, html, Polymer } from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import { MultidevicePhoneHubBrowserProxy } from './multidevice_phonehub_browser_proxy.js';
+import { FeatureStatus } from './types.js';
 
 /**
  * Maps a FeatureStatus to it's title label in the dropdown.
@@ -130,8 +130,8 @@
   /** @override */
   attached() {
     this.addWebUIListener(
-        'should-show-onboarding-ui-changed',
-        this.onShouldShowOnboardingUiChanged_.bind(this));
+      'should-show-onboarding-ui-changed',
+      this.onShouldShowOnboardingUiChanged_.bind(this));
   },
 
   /**
@@ -140,7 +140,7 @@
    */
   canOnboardingFlowBeShownComputed_() {
     if (this.featureStatus_ === FeatureStatus.DISABLED ||
-        this.featureStatus_ === FeatureStatus.ELIGIBLE_PHONE_BUT_NOT_SETUP) {
+      this.featureStatus_ === FeatureStatus.ELIGIBLE_PHONE_BUT_NOT_SETUP) {
       return true;
     }
     return false;
@@ -152,7 +152,7 @@
    */
   isPhoneSetUpComputed_() {
     if (this.featureStatus_ === FeatureStatus.NOT_ELIGIBLE_FOR_FEATURE ||
-        this.featureStatus_ === FeatureStatus.ELIGIBLE_PHONE_BUT_NOT_SETUP) {
+      this.featureStatus_ === FeatureStatus.ELIGIBLE_PHONE_BUT_NOT_SETUP) {
       return false;
     }
 
@@ -170,7 +170,7 @@
   /** @private */
   onShouldEnableFakePhoneHubManagerChanged_() {
     this.browserProxy_.setFakePhoneHubManagerEnabled(
-        this.shouldEnableFakePhoneHubManager_);
+      this.shouldEnableFakePhoneHubManager_);
 
     if (!this.shouldEnableFakePhoneHubManager_) {
       return;
@@ -185,7 +185,7 @@
   /** @private */
   onFeatureStatusSelected_() {
     const select = /** @type {!HTMLSelectElement} */
-        (this.$$('#featureStatusList'));
+      (this.$$('#featureStatusList'));
     this.featureStatus_ = this.featureStatusList_[select.selectedIndex];
     this.browserProxy_.setFeatureStatus(this.featureStatus_);
   },
@@ -214,8 +214,8 @@
   },
 
   /** @private */
-  onResetHasNotificationSetupUiBeenDismissedButtonClick_() {
-    this.browserProxy_.resetHasNotificationSetupUiBeenDismissed();
+  onResetHasMultideviceFeatureSetupUiBeenDismissedButtonClick_() {
+    this.browserProxy_.resetHasMultideviceFeatureSetupUiBeenDismissed();
   },
 
   /** @private */
diff --git a/chrome/browser/resources/settings/chromeos/os_a11y_page/manage_a11y_page.html b/chrome/browser/resources/settings/chromeos/os_a11y_page/manage_a11y_page.html
index 3a4c34a..b8d198a 100644
--- a/chrome/browser/resources/settings/chromeos/os_a11y_page/manage_a11y_page.html
+++ b/chrome/browser/resources/settings/chromeos/os_a11y_page/manage_a11y_page.html
@@ -214,30 +214,28 @@
         deep-link-focus-id$="[[Setting.kDictation]]">
   </settings-toggle-button>
 </template>
-<template is="dom-if" if="[[areDictationLocalePrefsAllowed_]]">
-  <template is="dom-if" if="[[prefs.settings.a11y.dictation.value]]">
-    <div class="settings-box continuation indented">
-      <div class="flex start settings-box-text" id="labelWrapper">
-        <div class="label" aria-hidden="true" id="dictationLocaleMenuLabel">
-            $i18n{dictationLocaleMenuLabel}
-        </div>
-        <div class="secondary label" id="dictationLocaleMenuSubtitle">
-            [[dictationLocaleMenuSubtitle_]]
-        </div>
+<template is="dom-if" if="[[prefs.settings.a11y.dictation.value]]">
+  <div class="settings-box continuation indented">
+    <div class="flex start settings-box-text" id="labelWrapper">
+      <div class="label" aria-hidden="true" id="dictationLocaleMenuLabel">
+          $i18n{dictationLocaleMenuLabel}
       </div>
-      <cr-button on-click="onChangeDictationLocaleButtonClicked_">
-        $i18n{dictationChangeLanguageButton}
-      </cr-button>
-      <template is="dom-if" if="[[showDictationLocaleMenu_]]" restamp>
-        <os-settings-change-dictation-locale-dialog
-          id="changeDictationLocaleDialog"
-          pref="{{prefs.settings.a11y.dictation_locale}}"
-          on-close="onChangeDictationLocalesDialogClosed_"
-          options="[[dictationLocaleOptions_]]">
-        </os-settings-change-dictation-locale-dialog>
-      </template>
+      <div class="secondary label" id="dictationLocaleMenuSubtitle">
+          [[dictationLocaleMenuSubtitle_]]
+      </div>
     </div>
-  </template>
+    <cr-button on-click="onChangeDictationLocaleButtonClicked_">
+      $i18n{dictationChangeLanguageButton}
+    </cr-button>
+    <template is="dom-if" if="[[showDictationLocaleMenu_]]" restamp>
+      <os-settings-change-dictation-locale-dialog
+        id="changeDictationLocaleDialog"
+        pref="{{prefs.settings.a11y.dictation_locale}}"
+        on-close="onChangeDictationLocalesDialogClosed_"
+        options="[[dictationLocaleOptions_]]">
+      </os-settings-change-dictation-locale-dialog>
+    </template>
+  </div>
 </template>
 <settings-toggle-button
     class="hr"
diff --git a/chrome/browser/resources/settings/chromeos/os_a11y_page/manage_a11y_page.js b/chrome/browser/resources/settings/chromeos/os_a11y_page/manage_a11y_page.js
index 6475dbef..0c51783 100644
--- a/chrome/browser/resources/settings/chromeos/os_a11y_page/manage_a11y_page.js
+++ b/chrome/browser/resources/settings/chromeos/os_a11y_page/manage_a11y_page.js
@@ -268,15 +268,6 @@
     },
 
     /** @private */
-    areDictationLocalePrefsAllowed_: {
-      type: Boolean,
-      readOnly: true,
-      value() {
-        return loadTimeData.getBoolean('areDictationLocalePrefsAllowed');
-      }
-    },
-
-    /** @private */
     dictationLocaleOptions_: {
       type: Array,
       value() {
@@ -763,9 +754,7 @@
 
   /** @private */
   onChangeDictationLocaleButtonClicked_() {
-    if (this.areDictationLocalePrefsAllowed_) {
-      this.showDictationLocaleMenu_ = true;
-    }
+    this.showDictationLocaleMenu_ = true;
   },
 
   /** @private */
diff --git a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/share_sheet/ShareSheetCoordinator.java b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/share_sheet/ShareSheetCoordinator.java
index a5e5015..3dde0d48 100644
--- a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/share_sheet/ShareSheetCoordinator.java
+++ b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/share_sheet/ShareSheetCoordinator.java
@@ -367,8 +367,7 @@
     }
 
     private boolean shouldUseUsageRanking() {
-        return getCurrentProfile() != null
-                && ChromeFeatureList.isEnabled(ChromeFeatureList.SHARE_USAGE_RANKING);
+        return getCurrentProfile() != null;
     }
 
     /**
@@ -454,7 +453,7 @@
         }
 
         int fold = numberOf3PTilesThatFitOnScreen(activity);
-        int length = numberOf3PTilesToShow(fold);
+        int length = fold;
 
         // TODO(ellyjones): Does !saveLastUsed always imply that we shouldn't incorporate the share
         // into our ranking?
@@ -480,20 +479,6 @@
         }
         return remaining;
     }
-
-    private int numberOf3PTilesToShow(int fold) {
-        final boolean shouldFixMore =
-                ChromeFeatureList.isEnabled(ChromeFeatureList.SHARE_USAGE_RANKING_FIXED_MORE);
-
-        // Let's say that the screen is 4 tiles wide, and MAX_NUM_APPS is 7.
-        // Then, in FIXED_MORE mode, there should be 4 app tiles total:
-        //    aaa bbb ccc more ^
-        // where ^ marks the screen edge.
-        // In non-FIXED_MORE mode there should be 8:
-        //    aaa bbb ccc ddd ^ eee fff ggg more
-        return shouldFixMore ? fold : ShareSheetPropertyModelBuilder.MAX_NUM_APPS + 1;
-    }
-
     private int numberOf3PTilesThatFitOnScreen(Activity activity) {
         int screenWidth = FORCED_SCREEN_WIDTH_FOR_TEST != 0 ? FORCED_SCREEN_WIDTH_FOR_TEST
                                                             : ContextUtils.getApplicationContext()
diff --git a/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/share_sheet/ShareSheetTest.java b/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/share_sheet/ShareSheetTest.java
index 1ef104e3..5cf991d 100644
--- a/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/share_sheet/ShareSheetTest.java
+++ b/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/share_sheet/ShareSheetTest.java
@@ -33,16 +33,13 @@
 import org.chromium.base.ContextUtils;
 import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.CommandLineFlags;
-import org.chromium.base.test.util.Feature;
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.share.ShareHistoryBridge;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
 import org.chromium.chrome.test.util.MenuUtils;
-import org.chromium.chrome.test.util.browser.Features.EnableFeatures;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetTestSupport;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
@@ -289,11 +286,7 @@
 
     @Test
     @SmallTest
-    @Feature({ChromeFeatureList.SHARE_USAGE_RANKING})
-    @EnableFeatures({ChromeFeatureList.SHARE_USAGE_RANKING,
-            ChromeFeatureList.SHARE_USAGE_RANKING_FIXED_MORE})
-    public void
-    nothingFromDefaultRankingAvailable() {
+    public void nothingFromDefaultRankingAvailable() {
         replaceRecentShareHistory(defaultTestHistory());
         replaceSystemApps(defaultTestSystemApps());
 
diff --git a/chrome/browser/task_manager/providers/crosapi/crosapi_task_provider_ash_unittest.cc b/chrome/browser/task_manager/providers/crosapi/crosapi_task_provider_ash_unittest.cc
index 4f957c07..faa967f 100644
--- a/chrome/browser/task_manager/providers/crosapi/crosapi_task_provider_ash_unittest.cc
+++ b/chrome/browser/task_manager/providers/crosapi/crosapi_task_provider_ash_unittest.cc
@@ -153,8 +153,8 @@
 
   // Verify all mojo tasks have been added to the task providers,
   // and GetSortedTaskIds returns the correct number of the task count.
-  DCHECK_EQ(3, GetTaskCount());
-  DCHECK_EQ(3, GetSortedTaskIdsCount());
+  DCHECK_EQ(3u, GetTaskCount());
+  DCHECK_EQ(3u, GetSortedTaskIdsCount());
   DCHECK_EQ(3, task_added_count());
   DCHECK_EQ(0, task_removed_count());
   // Verify that task ids returned by GetSortedTaskIds() matches
@@ -179,8 +179,8 @@
                                          std::move(mojo_task_groups));
 
   // Verify that one of the tasks has been removed.
-  DCHECK_EQ(2, GetTaskCount());
-  DCHECK_EQ(2, GetSortedTaskIdsCount());
+  DCHECK_EQ(2u, GetTaskCount());
+  DCHECK_EQ(2u, GetSortedTaskIdsCount());
   DCHECK_EQ(0, task_added_count());
   DCHECK_EQ(1, task_removed_count());
   // Verify that task ids returned by GetSortedTaskIds() matches
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java
index 98ad054..2718ff3 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java
@@ -450,6 +450,13 @@
             assert tabModel != null;
 
             int tabIndex = TabModelUtils.getTabIndexById(tabModel, tab.getId());
+            // In the event the user deleted the tab as part during the interaction with the
+            // Omnibox, reject the switch to tab action.
+            if (tabIndex < 0) {
+                onSuggestionClicked(suggestion, position, suggestion.getUrl());
+                return;
+            }
+
             tabModel.setIndex(tabIndex, TabSelectionType.FROM_OMNIBOX, false);
         } else {
             mBringTabToFrontCallback.onResult(tab);
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/pedal/PedalSuggestionViewBinder.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/pedal/PedalSuggestionViewBinder.java
index e16c46e..8b47902 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/pedal/PedalSuggestionViewBinder.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/pedal/PedalSuggestionViewBinder.java
@@ -8,6 +8,8 @@
 import android.graphics.drawable.Drawable;
 import android.view.View;
 
+import androidx.core.view.ViewCompat;
+
 import org.chromium.chrome.browser.omnibox.R;
 import org.chromium.chrome.browser.omnibox.styles.OmniboxResourceProvider;
 import org.chromium.chrome.browser.omnibox.suggestions.SuggestionCommonProperties;
@@ -66,6 +68,9 @@
             Drawable backgroundDrawable =
                     BaseSuggestionViewBinder.getSelectableBackgroundDrawable(view, model);
             view.getPedalView().setBackground(backgroundDrawable);
+        } else if (SuggestionCommonProperties.LAYOUT_DIRECTION == propertyKey) {
+            ViewCompat.setLayoutDirection(
+                    view.getPedalView(), model.get(SuggestionCommonProperties.LAYOUT_DIRECTION));
         }
     }
 }
diff --git a/chrome/browser/ui/ash/app_list/bubble_apps_grid_drag_browsertest.cc b/chrome/browser/ui/ash/app_list/bubble_apps_grid_drag_browsertest.cc
index 8abcf04..d3474aa 100644
--- a/chrome/browser/ui/ash/app_list/bubble_apps_grid_drag_browsertest.cc
+++ b/chrome/browser/ui/ash/app_list/bubble_apps_grid_drag_browsertest.cc
@@ -157,7 +157,7 @@
   // Verify that the folder apps grid contains two items.
   ash::AppsGridView* folder_apps_grid_view =
       app_list_test_api()->GetFolderAppsGridView();
-  EXPECT_EQ(2u, folder_apps_grid_view->view_model()->view_size());
+  EXPECT_EQ(2, folder_apps_grid_view->view_model()->view_size());
 
   const std::vector<std::string> top_level_ids_after_merging =
       app_list_test_api()->GetTopLevelViewIdList();
diff --git a/chrome/browser/ui/ash/clipboard_history_browsertest.cc b/chrome/browser/ui/ash/clipboard_history_browsertest.cc
index b4aa9a31..3ca833e 100644
--- a/chrome/browser/ui/ash/clipboard_history_browsertest.cc
+++ b/chrome/browser/ui/ash/clipboard_history_browsertest.cc
@@ -1158,8 +1158,7 @@
 class FakeDataTransferPolicyController
     : public ui::DataTransferPolicyController {
  public:
-  FakeDataTransferPolicyController()
-      : allowed_origin_(url::Origin::Create(GURL(kUrlString))) {}
+  FakeDataTransferPolicyController() : allowed_url_(GURL(kUrlString)) {}
   ~FakeDataTransferPolicyController() override = default;
 
   // ui::DataTransferPolicyController:
@@ -1170,10 +1169,10 @@
     if (data_dst && data_dst->type() == ui::EndpointType::kClipboardHistory)
       return true;
 
-    // For other data destinations, only the data from `allowed_origin_`
+    // For other data destinations, only the data from `allowed_url_`
     // should be accessible.
     return data_src && data_src->IsUrlType() &&
-           (*data_src->GetOrigin() == allowed_origin_);
+           (*data_src->GetURL() == allowed_url_);
   }
 
   void PasteIfAllowed(const ui::DataTransferEndpoint* const data_src,
@@ -1187,7 +1186,7 @@
                      base::OnceClosure drop_cb) override {}
 
  private:
-  const url::Origin allowed_origin_;
+  const GURL allowed_url_;
 };
 
 // The browser test equipped with the custom policy controller.
@@ -1209,9 +1208,9 @@
   // Write text into the clipboard buffer and it should be accessible from
   // the multipaste menu.
   void SetClipboardTextWithAccessibleSrc(const std::string& text) {
-    ui::ScopedClipboardWriter(ui::ClipboardBuffer::kCopyPaste,
-                              std::make_unique<ui::DataTransferEndpoint>(
-                                  url::Origin::Create(GURL(kUrlString))))
+    ui::ScopedClipboardWriter(
+        ui::ClipboardBuffer::kCopyPaste,
+        std::make_unique<ui::DataTransferEndpoint>((GURL(kUrlString))))
         .WriteText(base::UTF8ToUTF16(text));
 
     // ClipboardHistory will post a task to process clipboard data in order to
diff --git a/chrome/browser/ui/ash/desks_templates/desks_templates_client_browsertest.cc b/chrome/browser/ui/ash/desks_templates/desks_templates_client_browsertest.cc
index d669d41..aed0daf 100644
--- a/chrome/browser/ui/ash/desks_templates/desks_templates_client_browsertest.cc
+++ b/chrome/browser/ui/ash/desks_templates/desks_templates_client_browsertest.cc
@@ -766,7 +766,8 @@
       /*active_url_index=*/browser_active_index);
 
   // Verify that the active tab is correct.
-  EXPECT_EQ(browser_active_index, browser->tab_strip_model()->active_index());
+  EXPECT_EQ(static_cast<int>(browser_active_index),
+            browser->tab_strip_model()->active_index());
 
   aura::Window* window = browser->window()->GetNativeWindow();
   const int32_t browser_window_id =
@@ -791,7 +792,7 @@
   Browser* new_browser = FindBrowser(browser_window_id);
   ASSERT_TRUE(new_browser);
   EXPECT_EQ(urls, GetURLsForBrowserWindow(new_browser));
-  EXPECT_EQ(browser_active_index,
+  EXPECT_EQ(static_cast<int>(browser_active_index),
             new_browser->tab_strip_model()->active_index());
 
   // Verify that the browser window has been launched on the new desk (desk B).
@@ -843,7 +844,8 @@
   // that browser session restore did not restore any windows/tabs.
   Browser* new_browser = FindBrowser(browser_window_id);
   ASSERT_TRUE(new_browser);
-  EXPECT_EQ(expected_tab_count, GetURLsForBrowserWindow(new_browser).size());
+  EXPECT_EQ(1u * expected_tab_count,
+            GetURLsForBrowserWindow(new_browser).size());
   EXPECT_EQ(1u, chrome::GetTotalBrowserCount());
 }
 
@@ -1167,7 +1169,8 @@
       /*active_url_index=*/browser_active_index);
 
   // Verify that the active tab is correct.
-  EXPECT_EQ(browser_active_index, browser->tab_strip_model()->active_index());
+  EXPECT_EQ(static_cast<int>(browser_active_index),
+            browser->tab_strip_model()->active_index());
 
   aura::Window* window = browser->window()->GetNativeWindow();
   const int32_t browser_window_id =
@@ -1198,7 +1201,7 @@
   Browser* new_browser = FindBrowser(browser_window_id);
   ASSERT_TRUE(new_browser);
   EXPECT_EQ(urls, GetURLsForBrowserWindow(new_browser));
-  EXPECT_EQ(browser_active_index,
+  EXPECT_EQ(static_cast<int>(browser_active_index),
             new_browser->tab_strip_model()->active_index());
 
   // Verify that the browser window has been launched on the new desk (desk B).
@@ -1743,12 +1746,20 @@
 
   ash::ToggleOverview();
   ash::WaitForOverviewEnterAnimation();
+  auto* desks_controller = ash::DesksController::Get();
+  auto active_desk_index = desks_controller->GetActiveDeskIndex();
 
   // Save 3 templates.
   const int saves = 3;
   for (int i = 0; i < saves; i++) {
     ClickSaveDeskAsTemplateButton();
 
+    // Change desk name to avoid duplication on template name. Having duplicate
+    // names invokes a workflow that involves showing and accepting the replace
+    // dialog, which is unnecessary for this test.
+    desks_controller->desks()[active_desk_index]->SetName(
+        base::UTF8ToUTF16(base::NumberToString(i)), true);
+
     // Exit and renenter overview to save the next template. Once we are viewing
     // the grid we can't go back to regular overview unless we exit overview or
     // delete all the templates.
diff --git a/chrome/browser/ui/ash/projector/pending_screencast_manager_browsertest.cc b/chrome/browser/ui/ash/projector/pending_screencast_manager_browsertest.cc
index e02510e..7f3d0c4 100644
--- a/chrome/browser/ui/ash/projector/pending_screencast_manager_browsertest.cc
+++ b/chrome/browser/ui/ash/projector/pending_screencast_manager_browsertest.cc
@@ -201,7 +201,7 @@
 
   const PendingScreencastSet pending_screencasts =
       pending_screencast_manager()->GetPendingScreencasts();
-  EXPECT_EQ(pending_screencasts.size(), 1);
+  EXPECT_EQ(pending_screencasts.size(), 1u);
   ash::PendingScreencast ps = *(pending_screencasts.begin());
   EXPECT_EQ(ps.container_dir, base::FilePath(kTestScreencastPath));
   EXPECT_EQ(ps.name, kTestScreencastName);
@@ -286,10 +286,10 @@
 IN_PROC_BROWSER_TEST_F(PendingScreencastMangerBrowserTest,
                        MultipleValidAndInvalidScreencasts) {
   drivefs::mojom::SyncingStatus syncing_status;
-  int num_of_screencasts = 10;
+  size_t num_of_screencasts = 10;
   {
     // Create multiple valid pending screencasts.
-    for (int i = 0; i < num_of_screencasts; ++i) {
+    for (size_t i = 0; i < num_of_screencasts; ++i) {
       const std::string test_screencast_path =
           base::StrCat({kTestScreencastPath, base::NumberToString(i)});
       const std::string media =
@@ -330,7 +330,7 @@
 
   // Only valid screencasts could be processed.
   EXPECT_EQ(pending_screencasts.size(), num_of_screencasts);
-  for (int i = 0; i < num_of_screencasts; ++i) {
+  for (size_t i = 0; i < num_of_screencasts; ++i) {
     const std::string container_dir =
         base::StrCat({kTestScreencastPath, base::NumberToString(i)});
     const std::string name =
@@ -364,7 +364,7 @@
 
   const PendingScreencastSet pending_screencasts_1 =
       pending_screencast_manager()->GetPendingScreencasts();
-  EXPECT_EQ(pending_screencasts_1.size(), 1);
+  EXPECT_EQ(pending_screencasts_1.size(), 1u);
   ash::PendingScreencast ps = *(pending_screencasts_1.begin());
   const int total_size = kTestMediaFileBytes + kTestMetadataFileBytes;
   EXPECT_EQ(total_size, ps.total_size_in_bytes);
@@ -483,8 +483,8 @@
   EXPECT_NE(set1, set4);
   EXPECT_NE(set1, set5);
   EXPECT_EQ(set5, set6);
-  EXPECT_EQ(2, set5.size());
-  EXPECT_EQ(2, set7.size());
+  EXPECT_EQ(2u, set5.size());
+  EXPECT_EQ(2u, set7.size());
 }
 
 class PendingScreencastMangerMultiProfileTest : public LoginManagerTest {
diff --git a/chrome/browser/ui/ash/shelf/browser_app_shelf_controller_browsertest.cc b/chrome/browser/ui/ash/shelf/browser_app_shelf_controller_browsertest.cc
index a9dfd36..657d16f 100644
--- a/chrome/browser/ui/ash/shelf/browser_app_shelf_controller_browsertest.cc
+++ b/chrome/browser/ui/ash/shelf/browser_app_shelf_controller_browsertest.cc
@@ -568,7 +568,7 @@
               (SelectResult{ash::SHELF_ACTION_NEW_WINDOW_CREATED, {}}));
     ASSERT_EQ(SelectShelfItem(kAppId_B),
               (SelectResult{ash::SHELF_ACTION_NEW_WINDOW_CREATED, {}}));
-    ASSERT_EQ(registry_->GetLacrosBrowserWindowInstances().size(), 1);
+    ASSERT_EQ(registry_->GetLacrosBrowserWindowInstances().size(), 1u);
     ASSERT_EQ(ShelfStatus(kAppId_A), ash::STATUS_RUNNING);
     ASSERT_EQ(ShelfStatus(kAppId_B), ash::STATUS_RUNNING);
     ASSERT_EQ(WindowAppId(lacros->window), kAppId_B);
diff --git a/chrome/browser/ui/ash/test_wallpaper_controller.cc b/chrome/browser/ui/ash/test_wallpaper_controller.cc
index da5c61fa..f26bb88 100644
--- a/chrome/browser/ui/ash/test_wallpaper_controller.cc
+++ b/chrome/browser/ui/ash/test_wallpaper_controller.cc
@@ -49,7 +49,7 @@
     const base::FilePath& file_path,
     ash::WallpaperLayout layout,
     bool preview_mode,
-    SetCustomWallpaperCallback callback) {
+    SetWallpaperCallback callback) {
   ++set_custom_wallpaper_count_;
   std::move(callback).Run(true);
 }
@@ -64,7 +64,7 @@
 
 void TestWallpaperController::SetOnlineWallpaper(
     const ash::OnlineWallpaperParams& params,
-    SetOnlineWallpaperCallback callback) {
+    SetWallpaperCallback callback) {
   ++set_online_wallpaper_count_;
   wallpaper_info_ = ash::WallpaperInfo(params);
   std::move(callback).Run(/*success=*/true);
@@ -72,7 +72,7 @@
 
 void TestWallpaperController::SetGooglePhotosWallpaper(
     const ash::GooglePhotosWallpaperParams& params,
-    SetGooglePhotosWallpaperCallback callback) {
+    SetWallpaperCallback callback) {
   ++set_google_photos_wallpaper_count_;
   if (!ash::features::IsWallpaperGooglePhotosIntegrationEnabled()) {
     std::move(callback).Run(/*success=*/false);
@@ -84,14 +84,14 @@
 
 void TestWallpaperController::SetOnlineWallpaperIfExists(
     const ash::OnlineWallpaperParams& params,
-    SetOnlineWallpaperCallback callback) {
+    SetWallpaperCallback callback) {
   NOTIMPLEMENTED();
 }
 
 void TestWallpaperController::SetOnlineWallpaperFromData(
     const ash::OnlineWallpaperParams& params,
     const std::string& image_data,
-    SetOnlineWallpaperCallback callback) {
+    SetWallpaperCallback callback) {
   NOTIMPLEMENTED();
 }
 
diff --git a/chrome/browser/ui/ash/test_wallpaper_controller.h b/chrome/browser/ui/ash/test_wallpaper_controller.h
index 2b753e7..bca4a877 100644
--- a/chrome/browser/ui/ash/test_wallpaper_controller.h
+++ b/chrome/browser/ui/ash/test_wallpaper_controller.h
@@ -62,22 +62,21 @@
                           const base::FilePath& file_path,
                           ash::WallpaperLayout layout,
                           bool preview_mode,
-                          SetCustomWallpaperCallback callback) override;
+                          SetWallpaperCallback callback) override;
   void SetCustomWallpaper(const AccountId& account_id,
                           const std::string& file_name,
                           ash::WallpaperLayout layout,
                           const gfx::ImageSkia& image,
                           bool preview_mode) override;
   void SetOnlineWallpaper(const ash::OnlineWallpaperParams& params,
-                          SetOnlineWallpaperCallback callback) override;
+                          SetWallpaperCallback callback) override;
   void SetOnlineWallpaperIfExists(const ash::OnlineWallpaperParams& params,
-                                  SetOnlineWallpaperCallback callback) override;
+                                  SetWallpaperCallback callback) override;
   void SetOnlineWallpaperFromData(const ash::OnlineWallpaperParams& params,
                                   const std::string& image_data,
-                                  SetOnlineWallpaperCallback callback) override;
-  void SetGooglePhotosWallpaper(
-      const ash::GooglePhotosWallpaperParams& params,
-      SetGooglePhotosWallpaperCallback callback) override;
+                                  SetWallpaperCallback callback) override;
+  void SetGooglePhotosWallpaper(const ash::GooglePhotosWallpaperParams& params,
+                                SetWallpaperCallback callback) override;
   void SetDefaultWallpaper(const AccountId& account_id,
                            bool show_wallpaper) override;
   void SetCustomizedDefaultWallpaperPaths(
diff --git a/chrome/browser/ui/ash/wallpaper_controller_client_impl.cc b/chrome/browser/ui/ash/wallpaper_controller_client_impl.cc
index 8e7344b..6b0a195 100644
--- a/chrome/browser/ui/ash/wallpaper_controller_client_impl.cc
+++ b/chrome/browser/ui/ash/wallpaper_controller_client_impl.cc
@@ -353,7 +353,7 @@
 
 void WallpaperControllerClientImpl::SetOnlineWallpaper(
     const ash::OnlineWallpaperParams& params,
-    ash::WallpaperController::SetOnlineWallpaperCallback callback) {
+    ash::WallpaperController::SetWallpaperCallback callback) {
   if (!IsKnownUser(params.account_id))
     return;
 
@@ -362,7 +362,7 @@
 
 void WallpaperControllerClientImpl::SetGooglePhotosWallpaper(
     const ash::GooglePhotosWallpaperParams& params,
-    ash::WallpaperController::SetGooglePhotosWallpaperCallback callback) {
+    ash::WallpaperController::SetWallpaperCallback callback) {
   if (!IsKnownUser(params.account_id))
     return;
 
@@ -371,7 +371,7 @@
 
 void WallpaperControllerClientImpl::SetOnlineWallpaperIfExists(
     const ash::OnlineWallpaperParams& params,
-    ash::WallpaperController::SetOnlineWallpaperCallback callback) {
+    ash::WallpaperController::SetWallpaperCallback callback) {
   if (!IsKnownUser(params.account_id))
     return;
   wallpaper_controller_->SetOnlineWallpaperIfExists(params,
@@ -381,7 +381,7 @@
 void WallpaperControllerClientImpl::SetOnlineWallpaperFromData(
     const ash::OnlineWallpaperParams& params,
     const std::string& image_data,
-    ash::WallpaperController::SetOnlineWallpaperCallback callback) {
+    ash::WallpaperController::SetWallpaperCallback callback) {
   if (!IsKnownUser(params.account_id))
     return;
   wallpaper_controller_->SetOnlineWallpaperFromData(params, image_data,
diff --git a/chrome/browser/ui/ash/wallpaper_controller_client_impl.h b/chrome/browser/ui/ash/wallpaper_controller_client_impl.h
index d81dda4..9eceb76 100644
--- a/chrome/browser/ui/ash/wallpaper_controller_client_impl.h
+++ b/chrome/browser/ui/ash/wallpaper_controller_client_impl.h
@@ -98,17 +98,17 @@
                           bool preview_mode);
   void SetOnlineWallpaper(
       const ash::OnlineWallpaperParams& params,
-      ash::WallpaperController::SetOnlineWallpaperCallback callback);
+      ash::WallpaperController::SetWallpaperCallback callback);
   void SetGooglePhotosWallpaper(
       const ash::GooglePhotosWallpaperParams& params,
-      ash::WallpaperController::SetGooglePhotosWallpaperCallback callback);
+      ash::WallpaperController::SetWallpaperCallback callback);
   void SetOnlineWallpaperIfExists(
       const ash::OnlineWallpaperParams& params,
-      ash::WallpaperController::SetOnlineWallpaperCallback callback);
+      ash::WallpaperController::SetWallpaperCallback callback);
   void SetOnlineWallpaperFromData(
       const ash::OnlineWallpaperParams& params,
       const std::string& image_data,
-      ash::WallpaperController::SetOnlineWallpaperCallback callback);
+      ash::WallpaperController::SetWallpaperCallback callback);
   void SetCustomizedDefaultWallpaperPaths(
       const base::FilePath& customized_default_small_path,
       const base::FilePath& customized_default_large_path);
diff --git a/chrome/browser/ui/qrcode_generator/qrcode_generator_bubble_controller.cc b/chrome/browser/ui/qrcode_generator/qrcode_generator_bubble_controller.cc
index 4f029b7..bded170e 100644
--- a/chrome/browser/ui/qrcode_generator/qrcode_generator_bubble_controller.cc
+++ b/chrome/browser/ui/qrcode_generator/qrcode_generator_bubble_controller.cc
@@ -40,6 +40,10 @@
 
 void QRCodeGeneratorBubbleController::ShowBubble(const GURL& url,
                                                  bool show_back_button) {
+  // Ignore subsequent calls to open the dialog if it already is open.
+  if (bubble_shown_)
+    return;
+
   bubble_shown_ = true;
   Browser* browser = chrome::FindBrowserWithWebContents(&GetWebContents());
   qrcode_generator_bubble_ = browser->window()->ShowQRCodeGeneratorBubble(
@@ -60,6 +64,17 @@
   return qrcode_generator_bubble_;
 }
 
+base::OnceClosure QRCodeGeneratorBubbleController::GetOnBubbleClosedCallback() {
+  return base::BindOnce(&QRCodeGeneratorBubbleController::OnBubbleClosed,
+                        weak_ptr_factory_.GetWeakPtr());
+}
+
+base::OnceClosure
+QRCodeGeneratorBubbleController::GetOnBackButtonPressedCallback() {
+  return base::BindOnce(&QRCodeGeneratorBubbleController::OnBackButtonPressed,
+                        weak_ptr_factory_.GetWeakPtr());
+}
+
 void QRCodeGeneratorBubbleController::OnBubbleClosed() {
   bubble_shown_ = false;
   qrcode_generator_bubble_ = nullptr;
diff --git a/chrome/browser/ui/qrcode_generator/qrcode_generator_bubble_controller.h b/chrome/browser/ui/qrcode_generator/qrcode_generator_bubble_controller.h
index 5237d7f..a8971b6 100644
--- a/chrome/browser/ui/qrcode_generator/qrcode_generator_bubble_controller.h
+++ b/chrome/browser/ui/qrcode_generator/qrcode_generator_bubble_controller.h
@@ -6,6 +6,7 @@
 #define CHROME_BROWSER_UI_QRCODE_GENERATOR_QRCODE_GENERATOR_BUBBLE_CONTROLLER_H_
 
 #include "base/memory/raw_ptr.h"
+#include "base/memory/weak_ptr.h"
 #include "content/public/browser/web_contents_user_data.h"
 
 class GURL;
@@ -47,10 +48,8 @@
   // Returns nullptr if no bubble is currently shown.
   QRCodeGeneratorBubbleView* qrcode_generator_bubble_view() const;
 
-  // Handler for when the bubble is dismissed.
-  void OnBubbleClosed();
-
-  void OnBackButtonPressed();
+  base::OnceClosure GetOnBubbleClosedCallback();
+  base::OnceClosure GetOnBackButtonPressedCallback();
 
  protected:
   explicit QRCodeGeneratorBubbleController(content::WebContents* web_contents);
@@ -58,6 +57,11 @@
  private:
   friend class content::WebContentsUserData<QRCodeGeneratorBubbleController>;
 
+  // Handler for when the bubble is dismissed.
+  void OnBubbleClosed();
+  // Handler for when the back button is pressed.
+  void OnBackButtonPressed();
+
   void UpdateIcon();
 
   // Will be nullptr if no bubble is currently shown.
@@ -67,6 +71,8 @@
   bool bubble_shown_ = false;
 
   WEB_CONTENTS_USER_DATA_KEY_DECL();
+
+  base::WeakPtrFactory<QRCodeGeneratorBubbleController> weak_ptr_factory_{this};
 };
 
 }  // namespace qrcode_generator
diff --git a/chrome/browser/ui/singleton_tabs.cc b/chrome/browser/ui/singleton_tabs.cc
index 6b37490..98263487 100644
--- a/chrome/browser/ui/singleton_tabs.cc
+++ b/chrome/browser/ui/singleton_tabs.cc
@@ -21,7 +21,7 @@
 // Returns true if two URLs are equal after taking |replacements| into account.
 bool CompareURLsWithReplacements(const GURL& url,
                                  const GURL& other,
-                                 const url::Replacements<char>& replacements,
+                                 const GURL::Replacements& replacements,
                                  ChromeAutocompleteProviderClient* client) {
   GURL url_replaced = url.ReplaceComponents(replacements);
   GURL other_replaced = other.ReplaceComponents(replacements);
@@ -110,7 +110,7 @@
     content::BrowserURLHandler::GetInstance()->RewriteURLIfNecessary(
         &rewritten_tab_url, browser->profile());
 
-    url::Replacements<char> replacements;
+    GURL::Replacements replacements;
     replacements.ClearRef();
     if (params.path_behavior == NavigateParams::IGNORE_AND_NAVIGATE) {
       replacements.ClearPath();
diff --git a/chrome/browser/ui/views/autofill/payments/virtual_card_manual_fallback_bubble_views.cc b/chrome/browser/ui/views/autofill/payments/virtual_card_manual_fallback_bubble_views.cc
index 30bf385..da52c0dd 100644
--- a/chrome/browser/ui/views/autofill/payments/virtual_card_manual_fallback_bubble_views.cc
+++ b/chrome/browser/ui/views/autofill/payments/virtual_card_manual_fallback_bubble_views.cc
@@ -124,14 +124,16 @@
       .AddRows(1, views::TableLayout::kFixedSize);  // CVC.
 
   // Virtual card number.
-  AddChildView(
+  card_information_section->AddChildView(
       CreateRowItemLabel(controller_->GetVirtualCardNumberFieldLabel()));
-  AddChildView(CreateRowItemButtonForField(
+  card_information_section->AddChildView(CreateRowItemButtonForField(
       VirtualCardManualFallbackBubbleField::kCardNumber));
 
   // Expiration date.
-  AddChildView(CreateRowItemLabel(controller_->GetExpirationDateFieldLabel()));
-  auto* expiry_row = AddChildView(std::make_unique<views::View>());
+  card_information_section->AddChildView(
+      CreateRowItemLabel(controller_->GetExpirationDateFieldLabel()));
+  auto* expiry_row =
+      card_information_section->AddChildView(std::make_unique<views::View>());
   expiry_row->SetLayoutManager(std::make_unique<views::FlexLayout>())
       ->SetOrientation(views::LayoutOrientation::kHorizontal)
       .SetMainAxisAlignment(views::LayoutAlignment::kStart)
@@ -153,13 +155,15 @@
       VirtualCardManualFallbackBubbleField::kExpirationYear));
 
   // Cardholder name.
-  AddChildView(CreateRowItemLabel(controller_->GetCardholderNameFieldLabel()));
-  AddChildView(CreateRowItemButtonForField(
+  card_information_section->AddChildView(
+      CreateRowItemLabel(controller_->GetCardholderNameFieldLabel()));
+  card_information_section->AddChildView(CreateRowItemButtonForField(
       VirtualCardManualFallbackBubbleField::kCardholderName));
 
   // CVC.
-  AddChildView(CreateRowItemLabel(controller_->GetCvcFieldLabel()));
-  AddChildView(
+  card_information_section->AddChildView(
+      CreateRowItemLabel(controller_->GetCvcFieldLabel()));
+  card_information_section->AddChildView(
       CreateRowItemButtonForField(VirtualCardManualFallbackBubbleField::kCvc));
   UpdateButtonTooltipsAndAccessibleNames();
 }
diff --git a/chrome/browser/ui/views/frame/browser_view.cc b/chrome/browser/ui/views/frame/browser_view.cc
index 915afff..58bab15 100644
--- a/chrome/browser/ui/views/frame/browser_view.cc
+++ b/chrome/browser/ui/views/frame/browser_view.cc
@@ -2175,18 +2175,10 @@
     qrcode_generator::QRCodeGeneratorBubbleController* controller,
     const GURL& url,
     bool show_back_button) {
-  base::OnceClosure on_closing = base::BindOnce(
-      &qrcode_generator::QRCodeGeneratorBubbleController::OnBubbleClosed,
-      // Unretained is safe: controller is a WebContentsUserData, owned by
-      // WebContents, and the bubble can't outlive the WebContents.
-      base::Unretained(controller));
+  base::OnceClosure on_closing = controller->GetOnBubbleClosedCallback();
   base::OnceClosure on_back_button_pressed;
   if (show_back_button) {
-    on_back_button_pressed = base::BindOnce(
-        &qrcode_generator::QRCodeGeneratorBubbleController::OnBackButtonPressed,
-        // Unretained is safe: controller is a WebContentsUserData, owned by
-        // WebContents, and the bubble can't outlive the WebContents.
-        base::Unretained(controller));
+    on_back_button_pressed = controller->GetOnBackButtonPressedCallback();
   }
 
   PageActionIconType icon_type =
diff --git a/chrome/browser/ui/views/tabs/tab_container.cc b/chrome/browser/ui/views/tabs/tab_container.cc
index 9718b410..3889c63 100644
--- a/chrome/browser/ui/views/tabs/tab_container.cc
+++ b/chrome/browser/ui/views/tabs/tab_container.cc
@@ -4,12 +4,14 @@
 
 #include "chrome/browser/ui/views/tabs/tab_container.h"
 
+#include "base/bits.h"
 #include "chrome/browser/ui/layout_constants.h"
 #include "chrome/browser/ui/views/tabs/tab.h"
 #include "chrome/browser/ui/views/tabs/tab_group_header.h"
 #include "chrome/browser/ui/views/tabs/tab_group_highlight.h"
 #include "chrome/browser/ui/views/tabs/tab_group_underline.h"
 #include "chrome/browser/ui/views/tabs/tab_group_views.h"
+#include "chrome/browser/ui/views/tabs/tab_hover_card_controller.h"
 #include "chrome/browser/ui/views/tabs/tab_strip.h"
 #include "chrome/browser/ui/views/tabs/tab_strip_controller.h"
 #include "chrome/browser/ui/views/tabs/tab_style_views.h"
@@ -234,8 +236,10 @@
   AnimationEnded(animation);
 }
 
-TabContainer::TabContainer(TabStripController* controller)
+TabContainer::TabContainer(TabStripController* controller,
+                           TabHoverCardController* hover_card_controller)
     : controller_(controller),
+      hover_card_controller_(hover_card_controller),
       bounds_animator_(this),
       layout_helper_(std::make_unique<TabStripLayoutHelper>(
           controller,
@@ -271,6 +275,8 @@
 }
 
 void TabContainer::RemoveTabFromViewModel(int index) {
+  UpdateHoverCard(nullptr, TabController::HoverCardUpdateType::kTabRemoved);
+
   Tab* tab = GetTabAtModelIndex(index);
   tabs_view_model_.Remove(index);
   layout_helper_->RemoveTabAt(index, tab);
@@ -338,6 +344,24 @@
   return tabs_view_model_.view_size();
 }
 
+void TabContainer::UpdateHoverCard(
+    Tab* tab,
+    TabController::HoverCardUpdateType update_type) {
+  // Some operations (including e.g. starting a drag) can cause the tab focus
+  // to change at the same time as the tabstrip is starting to animate; the
+  // hover card should not be visible at this time.
+  // See crbug.com/1220840 for an example case.
+  if (bounds_animator_.IsAnimating()) {
+    tab = nullptr;
+    update_type = TabController::HoverCardUpdateType::kAnimating;
+  }
+
+  if (!hover_card_controller_)
+    return;
+
+  hover_card_controller_->UpdateHoverCard(tab, update_type);
+}
+
 void TabContainer::UpdateAccessibleTabIndices() {
   const int num_tabs = GetTabCount();
   for (int i = 0; i < num_tabs; ++i)
@@ -396,6 +420,8 @@
 }
 
 void TabContainer::AnimateToIdealBounds() {
+  UpdateHoverCard(nullptr, TabController::HoverCardUpdateType::kAnimating);
+
   for (int i = 0; i < GetTabCount(); ++i) {
     // If the tab is being dragged manually, skip it.
     Tab* tab = GetTabAtModelIndex(i);
@@ -465,6 +491,15 @@
 }
 
 void TabContainer::AnimateTabClosed(Tab* tab, int former_model_index) {
+  if (in_tab_close_ && GetTabCount() > 0 &&
+      override_available_width_for_tabs_ >
+          tabs_view_model_.ideal_bounds(GetTabCount() - 1).right()) {
+    // Tab closing mode is no longer constraining tab widths - they're at full
+    // size. Exit tab closing mode so that it doesn't artificially inflate our
+    // bounds.
+    ExitTabClosingMode();
+  }
+
   const int tab_overlap = TabStyle::GetTabOverlap();
 
   // TODO(pkasting): When closing multiple tabs, we get repeated RemoveTabAt()
@@ -506,6 +541,54 @@
                               base::Unretained(this))));
 }
 
+void TabContainer::EnterTabClosingMode(absl::optional<int> override_width) {
+  in_tab_close_ = true;
+  override_available_width_for_tabs_ = override_width;
+}
+
+void TabContainer::ExitTabClosingMode() {
+  in_tab_close_ = false;
+  override_available_width_for_tabs_.reset();
+}
+
+void TabContainer::OnTabWillBeRemovedAt(int model_index, bool was_active) {
+  // The tab at |model_index| has already been removed from the model, but is
+  // still in |tabs_view_model_|.  Index math with care!
+  const int model_count = GetTabCount() - 1;
+  const int tab_overlap = TabStyle::GetTabOverlap();
+  if (in_tab_close() && model_count > 0 && model_index != model_count) {
+    // The user closed a tab other than the last tab. Set
+    // override_available_width_for_tabs_ so that as the user closes tabs with
+    // the mouse a tab continues to fall under the mouse.
+    int next_active_index = controller_->GetActiveIndex();
+    DCHECK(IsValidModelIndex(next_active_index));
+    if (model_index <= next_active_index) {
+      // At this point, model's internal state has already been updated.
+      // |contents| has been detached from model and the active index has been
+      // updated. But the tab for |contents| isn't removed yet. Thus, we need to
+      // fix up next_active_index based on it.
+      next_active_index++;
+    }
+    Tab* next_active_tab = GetTabAtModelIndex(next_active_index);
+    Tab* tab_being_removed = GetTabAtModelIndex(model_index);
+
+    int size_delta = tab_being_removed->width();
+    if (!tab_being_removed->data().pinned && was_active &&
+        layout_helper_->active_tab_width() >
+            layout_helper_->inactive_tab_width()) {
+      // When removing an active, non-pinned tab, an inactive tab will be made
+      // active and thus given the active width. Thus the width being removed
+      // from the container is really the current width of whichever inactive
+      // tab will be made active.
+      size_delta = next_active_tab->width();
+    }
+
+    override_available_width_for_tabs_ =
+        tabs_view_model_.ideal_bounds(model_count).right() - size_delta +
+        tab_overlap;
+  }
+}
+
 void TabContainer::PaintChildren(const views::PaintInfo& paint_info) {
   // Groups that are being dragged by their header, or that contain the dragged
   // tabs, need an adjusted z-value. Find that group, if it exists.
diff --git a/chrome/browser/ui/views/tabs/tab_container.h b/chrome/browser/ui/views/tabs/tab_container.h
index 6adf7b4f..ce85e76 100644
--- a/chrome/browser/ui/views/tabs/tab_container.h
+++ b/chrome/browser/ui/views/tabs/tab_container.h
@@ -7,6 +7,7 @@
 
 #include <memory>
 #include "chrome/browser/ui/views/tabs/tab.h"
+#include "chrome/browser/ui/views/tabs/tab_controller.h"
 #include "chrome/browser/ui/views/tabs/tab_group_underline.h"
 #include "chrome/browser/ui/views/tabs/tab_group_views.h"
 #include "chrome/browser/ui/views/tabs/tab_slot_view.h"
@@ -20,13 +21,15 @@
 
 class TabStrip;
 class TabGroupHeader;
+class TabHoverCardController;
 
 // A View that contains a sequence of Tabs for the TabStrip.
 class TabContainer : public views::View, public views::ViewTargeterDelegate {
  public:
   METADATA_HEADER(TabContainer);
 
-  explicit TabContainer(TabStripController* controller_);
+  TabContainer(TabStripController* controller_,
+               TabHoverCardController* hover_card_controller_);
   ~TabContainer() override;
 
   Tab* AddTab(std::unique_ptr<Tab> tab, int model_index, TabPinned pinned);
@@ -56,6 +59,9 @@
 
   int GetTabCount() const;
 
+  void UpdateHoverCard(Tab* tab,
+                       TabController::HoverCardUpdateType update_type);
+
   // Updates the indexes and count for AX data on all tabs. Used by some screen
   // readers (e.g. ChromeVox).
   void UpdateAccessibleTabIndices();
@@ -82,6 +88,16 @@
   void AnimateTabClosed(Tab* tab, int former_model_index);
   void StartResetDragAnimation(int tab_model_index);
 
+  void EnterTabClosingMode(absl::optional<int> override_width);
+  void ExitTabClosingMode();
+
+  bool in_tab_close() { return in_tab_close_; }
+  absl::optional<int> override_available_width_for_tabs() {
+    return override_available_width_for_tabs_;
+  }
+
+  void OnTabWillBeRemovedAt(int model_index, bool was_active);
+
   // TODO (1295774): Move callers down into TabContainer so this
   // encapsulation-breaking getter can be removed.
   TabStripLayoutHelper* layout_helper() const { return layout_helper_.get(); }
@@ -144,10 +160,24 @@
 
   TabStripController* controller_;
 
+  TabHoverCardController* hover_card_controller_;
+
   // Responsible for animating tabs in response to model changes.
   views::BoundsAnimator bounds_animator_;
 
   std::unique_ptr<TabStripLayoutHelper> layout_helper_;
+
+  // If this value is defined, it is used as the width to lay out tabs
+  // (instead of GetAvailableWidthForTabStrip()). It is defined when closing
+  // tabs with the mouse, and is used to control which tab will end up under the
+  // cursor after the close animation completes.
+  absl::optional<int> override_available_width_for_tabs_;
+
+  // True if EnterTabClosingMode has been invoked. Currently, this happens when
+  // a tab is closed with the mouse/touch and when collapsing a tab group. When
+  // true, remove animations preserve current tab bounds, making tabs move more
+  // predictably in case the user wants to perform more mouse-based actions.
+  bool in_tab_close_ = false;
 };
 
 #endif  // CHROME_BROWSER_UI_VIEWS_TABS_TAB_CONTAINER_H_
diff --git a/chrome/browser/ui/views/tabs/tab_container_unittest.cc b/chrome/browser/ui/views/tabs/tab_container_unittest.cc
index 7647cac3..c6d1bb0 100644
--- a/chrome/browser/ui/views/tabs/tab_container_unittest.cc
+++ b/chrome/browser/ui/views/tabs/tab_container_unittest.cc
@@ -21,8 +21,8 @@
     ChromeViewsTestBase::SetUp();
 
     tab_strip_controller_ = std::make_unique<FakeBaseTabStripController>();
-    tab_container_ =
-        std::make_unique<TabContainer>(tab_strip_controller_.get());
+    tab_container_ = std::make_unique<TabContainer>(
+        tab_strip_controller_.get(), nullptr /*hover_card_controller_*/);
     tab_controller_ = std::make_unique<FakeTabController>();
   }
 
diff --git a/chrome/browser/ui/views/tabs/tab_strip.cc b/chrome/browser/ui/views/tabs/tab_strip.cc
index 2e1850e..4dafa99 100644
--- a/chrome/browser/ui/views/tabs/tab_strip.cc
+++ b/chrome/browser/ui/views/tabs/tab_strip.cc
@@ -781,9 +781,10 @@
 
 TabStrip::TabStrip(std::unique_ptr<TabStripController> controller)
     : controller_(std::move(controller)),
-      tab_container_(
-          AddChildView(std::make_unique<TabContainer>(controller_.get()))),
       hover_card_controller_(std::make_unique<TabHoverCardController>(this)),
+      tab_container_(AddChildView(
+          std::make_unique<TabContainer>(controller_.get(),
+                                         hover_card_controller_.get()))),
       drag_context_(std::make_unique<TabDragContextImpl>(this)) {
   // TODO(pbos): This is probably incorrect, the background of individual tabs
   // depend on their selected state. This should probably be pushed down into
@@ -1079,7 +1080,7 @@
   }
 
   if (group.has_value())
-    ExitTabClosingMode();
+    tab_container_->ExitTabClosingMode();
 }
 
 void TabStrip::OnGroupCreated(const tab_groups::TabGroupId& group) {
@@ -1095,7 +1096,7 @@
   // move in terms of model indices.
   OnGroupMoved(group);
   UpdateIdealBounds();
-  AnimateToIdealBounds();
+  tab_container_->AnimateToIdealBounds();
 }
 
 void TabStrip::OnGroupVisualsChanged(
@@ -1109,16 +1110,15 @@
   bool is_collapsing = old_visuals && !old_visuals->is_collapsed() &&
                        new_visuals->is_collapsed();
   if (!is_collapsing)
-    ExitTabClosingMode();
+    tab_container_->ExitTabClosingMode();
   UpdateIdealBounds();
-  AnimateToIdealBounds();
+  tab_container_->AnimateToIdealBounds();
 }
 
 void TabStrip::ToggleTabGroup(const tab_groups::TabGroupId& group,
                               bool is_collapsing,
                               ToggleTabGroupCollapsedStateOrigin origin) {
   if (is_collapsing && GetWidget()) {
-    in_tab_close_ = true;
     if (origin == ToggleTabGroupCollapsedStateOrigin::kMouse) {
       AddMessageLoopObserver();
     } else if (origin == ToggleTabGroupCollapsedStateOrigin::kGesture) {
@@ -1141,12 +1141,12 @@
                                  empty_group_title_adjustment;
     const int collapsed_header_width =
         title_chip_width + 2 * TabGroupUnderline::GetStrokeInset();
-    override_available_width_for_tabs_ =
+    tab_container_->EnterTabClosingMode(
         ideal_bounds(GetModelCount() - 1).right() -
         tab_container_->group_views()[group]->GetBounds().width() +
-        collapsed_header_width;
+        collapsed_header_width);
   } else {
-    ExitTabClosingMode();
+    tab_container_->ExitTabClosingMode();
   }
 }
 
@@ -1159,7 +1159,7 @@
   tab_container_->layout_helper()->RemoveGroupHeader(group);
 
   UpdateIdealBounds();
-  AnimateToIdealBounds();
+  tab_container_->AnimateToIdealBounds();
   tab_container_->group_views().erase(group);
 }
 
@@ -1310,7 +1310,7 @@
     // width--because PrepareForCloseAt() will have set
     // |override_available_width_for_tabs_| already.
     UpdateIdealBounds();
-    AnimateToIdealBounds();
+    tab_container_->AnimateToIdealBounds();
   } else {
     // As in the animating case above, the selection change will have
     // affected the desired bounds of the tabs, but since we're not animating
@@ -1676,19 +1676,7 @@
 }
 
 void TabStrip::UpdateHoverCard(Tab* tab, HoverCardUpdateType update_type) {
-  // Some operations (including e.g. starting a drag) can cause the tab focus
-  // to change at the same time as the tabstrip is starting to animate; the
-  // hover card should not be visible at this time.
-  // See crbug.com/1220840 for an example case.
-  if (tab_container_->bounds_animator().IsAnimating()) {
-    tab = nullptr;
-    update_type = HoverCardUpdateType::kAnimating;
-  }
-
-  if (!hover_card_controller_)
-    return;
-
-  hover_card_controller_->UpdateHoverCard(tab, update_type);
+  tab_container_->UpdateHoverCard(tab, update_type);
 }
 
 bool TabStrip::ShowDomainInHoverCards() const {
@@ -1887,9 +1875,8 @@
     preferred_width = max_x;
   } else {
     preferred_width =
-        override_available_width_for_tabs_
-            ? override_available_width_for_tabs_.value()
-            : tab_container_->layout_helper()->CalculatePreferredWidth();
+        tab_container_->override_available_width_for_tabs().value_or(
+            tab_container_->layout_helper()->CalculatePreferredWidth());
   }
 
   return gfx::Size(preferred_width, GetLayoutConstant(TAB_HEIGHT));
@@ -2072,7 +2059,7 @@
 void TabStrip::StartInsertTabAnimation(int model_index) {
   PrepareForAnimation();
 
-  ExitTabClosingMode();
+  tab_container_->ExitTabClosingMode();
 
   gfx::Rect bounds = tab_at(model_index)->bounds();
   bounds.set_height(GetLayoutConstant(TAB_HEIGHT));
@@ -2097,41 +2084,11 @@
 
   // Animate in to the full width.
   UpdateIdealBounds();
-  AnimateToIdealBounds();
+  tab_container_->AnimateToIdealBounds();
 }
 
 void TabStrip::StartRemoveTabAnimation(int model_index, bool was_active) {
-  const int model_count = GetModelCount();
-  const int tab_overlap = TabStyle::GetTabOverlap();
-  if (in_tab_close_ && model_count > 0 && model_index != model_count) {
-    // The user closed a tab other than the last tab. Set
-    // override_available_width_for_tabs_ so that as the user closes tabs with
-    // the mouse a tab continues to fall under the mouse.
-    int next_active_index = controller_->GetActiveIndex();
-    DCHECK(IsValidModelIndex(next_active_index));
-    if (model_index <= next_active_index) {
-      // At this point, model's internal state has already been updated.
-      // |contents| has been detached from model and the active index has been
-      // updated. But the tab for |contents| isn't removed yet. Thus, we need to
-      // fix up next_active_index based on it.
-      next_active_index++;
-    }
-    Tab* next_active_tab = tab_at(next_active_index);
-    Tab* tab_being_removed = tab_at(model_index);
-
-    int size_delta = tab_being_removed->width();
-    if (!tab_being_removed->data().pinned && was_active &&
-        GetActiveTabWidth() > GetInactiveTabWidth()) {
-      // When removing an active, non-pinned tab, an inactive tab will be made
-      // active and thus given the active width. Thus the width being removed
-      // from the strip is really the current width of whichever inactive tab
-      // will be made active.
-      size_delta = next_active_tab->width();
-    }
-
-    override_available_width_for_tabs_ =
-        ideal_bounds(model_count).right() - size_delta + tab_overlap;
-  }
+  tab_container_->OnTabWillBeRemovedAt(model_index, was_active);
 
   PrepareForAnimation();
 
@@ -2141,16 +2098,7 @@
   RemoveTabFromViewModel(model_index);
 
   UpdateIdealBounds();
-  AnimateToIdealBounds();
-
-  if (in_tab_close_ && model_count > 0 &&
-      override_available_width_for_tabs_ >
-          ideal_bounds(model_count - 1).right()) {
-    // Tab closing mode is no longer constraining tab widths - they're at full
-    // size. Exit tab closing mode so that it doesn't artificially inflate the
-    // tabstrip's bounds.
-    ExitTabClosingMode();
-  }
+  tab_container_->AnimateToIdealBounds();
 
   // Animate the tab closed.
   tab_container_->AnimateTabClosed(tab, model_index);
@@ -2159,22 +2107,11 @@
 void TabStrip::StartMoveTabAnimation() {
   PrepareForAnimation();
   UpdateIdealBounds();
-  AnimateToIdealBounds();
-}
-
-void TabStrip::AnimateToIdealBounds() {
-  UpdateHoverCard(nullptr, HoverCardUpdateType::kAnimating);
-
   tab_container_->AnimateToIdealBounds();
 }
 
-void TabStrip::ExitTabClosingMode() {
-  in_tab_close_ = false;
-  override_available_width_for_tabs_.reset();
-}
-
 bool TabStrip::ShouldHighlightCloseButtonAfterRemove() {
-  return in_tab_close_;
+  return tab_container_->in_tab_close();
 }
 
 bool TabStrip::TitlebarBackgroundIsTransparent() const {
@@ -2262,14 +2199,17 @@
   if (!controller_->BeforeCloseTab(model_index, source))
     return;
 
-  if (!in_tab_close_ && IsAnimating()) {
+  if (!tab_container_->in_tab_close() && IsAnimating()) {
     // Cancel any current animations. We do this as remove uses the current
     // ideal bounds and we need to know ideal bounds is in a good state.
     StopAnimating(true);
   }
 
   if (GetWidget()) {
-    in_tab_close_ = true;
+    // Enter tab closing mode now, but wait to calculate the width constraint
+    // until RemoveTabAt() is called, since there are code paths that go through
+    // RemoveTabAt() but not this method that must also set that constraint.
+    tab_container_->EnterTabClosingMode(absl::nullopt);
     resize_layout_timer_.Stop();
     if (source == CLOSE_TAB_FROM_TOUCH)
       StartResizeLayoutTabsFromTouchTimer();
@@ -2287,8 +2227,6 @@
   Tab* closing_tab = tab_at(index);
   bool closing_tab_was_active = closing_tab->IsActive();
 
-  UpdateHoverCard(nullptr, HoverCardUpdateType::kTabRemoved);
-
   // We still need to keep the tab alive until the remove tab animation
   // completes. Defer destroying it until then.
   tab_container_->RemoveTabFromViewModel(index);
@@ -2321,7 +2259,7 @@
 
     // Animate the view back to its correct position.
     UpdateIdealBounds();
-    AnimateToIdealBounds();
+    tab_container_->AnimateToIdealBounds();
   }
 
   tab_container_->StartResetDragAnimation(tab_data_index);
@@ -2476,7 +2414,7 @@
   // keep spying on messages forever.
   RemoveMessageLoopObserver();
 
-  ExitTabClosingMode();
+  tab_container_->ExitTabClosingMode();
   int pinned_tab_count = GetPinnedTabCount();
   if (pinned_tab_count == GetTabCount()) {
     // Only pinned tabs, we know the tab widths won't have changed (all
@@ -2728,7 +2666,7 @@
 }
 
 int TabStrip::CalculateAvailableWidthForTabs() const {
-  return override_available_width_for_tabs_.value_or(
+  return tab_container_->override_available_width_for_tabs().value_or(
       GetAvailableWidthForTabStrip());
 }
 
@@ -2741,16 +2679,16 @@
 void TabStrip::StartResizeLayoutAnimation() {
   PrepareForAnimation();
   UpdateIdealBounds();
-  AnimateToIdealBounds();
+  tab_container_->AnimateToIdealBounds();
 }
 
 void TabStrip::StartPinnedTabAnimation() {
-  ExitTabClosingMode();
+  tab_container_->ExitTabClosingMode();
 
   PrepareForAnimation();
 
   UpdateIdealBounds();
-  AnimateToIdealBounds();
+  tab_container_->AnimateToIdealBounds();
 }
 
 const gfx::Rect& TabStrip::ideal_bounds(tab_groups::TabGroupId group) const {
diff --git a/chrome/browser/ui/views/tabs/tab_strip.h b/chrome/browser/ui/views/tabs/tab_strip.h
index 65b4c30d..22b9b3d 100644
--- a/chrome/browser/ui/views/tabs/tab_strip.h
+++ b/chrome/browser/ui/views/tabs/tab_strip.h
@@ -429,13 +429,6 @@
   // move.
   void StartMoveTabAnimation();
 
-  // Animates all the views to their ideal bounds.
-  // NOTE: this does *not* invoke UpdateIdealBounds, it uses the bounds
-  // currently set in ideal_bounds.
-  void AnimateToIdealBounds();
-
-  void ExitTabClosingMode();
-
   // Returns whether the close button should be highlighted after a remove.
   bool ShouldHighlightCloseButtonAfterRemove();
 
@@ -588,6 +581,8 @@
 
   std::unique_ptr<TabStripController> controller_;
 
+  std::unique_ptr<TabHoverCardController> hover_card_controller_;
+
   // The View parent for the tabs and the various group views.
   TabContainer* tab_container_;
 
@@ -596,19 +591,9 @@
   // Responsible for animating the scroll of the tab strip.
   std::unique_ptr<gfx::LinearAnimation> tab_scrolling_animation_;
 
-  // If this value is defined, it is used as the width to lay out tabs
-  // (instead of GetAvailableWidthForTabStrip()). It is defined when closing
-  // tabs with the mouse, and is used to control which tab will end up under the
-  // cursor after the close animation completes.
-  absl::optional<int> override_available_width_for_tabs_;
-
   // The background offset used by inactive tabs to match the frame image.
   int background_offset_ = 0;
 
-  // True if PrepareForCloseAt has been invoked. When true remove animations
-  // preserve current tab bounds.
-  bool in_tab_close_ = false;
-
   // Valid for the lifetime of a drag over us.
   std::unique_ptr<DropArrow> drop_arrow_;
 
@@ -659,8 +644,6 @@
 
   SkColor separator_color_ = gfx::kPlaceholderColor;
 
-  std::unique_ptr<TabHoverCardController> hover_card_controller_;
-
   const base::CallbackListSubscription subscription_ =
       ui::TouchUiController::Get()->RegisterCallback(
           base::BindRepeating(&TabStrip::OnTouchUiChanged,
diff --git a/chrome/browser/ui/views/tabs/tab_strip_unittest.cc b/chrome/browser/ui/views/tabs/tab_strip_unittest.cc
index 4b561e03..495757f 100644
--- a/chrome/browser/ui/views/tabs/tab_strip_unittest.cc
+++ b/chrome/browser/ui/views/tabs/tab_strip_unittest.cc
@@ -192,7 +192,9 @@
     tab_strip_parent_->Layout();
   }
 
-  void AnimateToIdealBounds() { tab_strip_->AnimateToIdealBounds(); }
+  void AnimateToIdealBounds() {
+    tab_strip_->tab_container_->AnimateToIdealBounds();
+  }
 
   views::BoundsAnimator* bounds_animator() {
     return &tab_strip_->tab_container_->bounds_animator();
diff --git a/chrome/browser/ui/views/web_apps/web_app_integration_test_driver.cc b/chrome/browser/ui/views/web_apps/web_app_integration_test_driver.cc
index e2e26a5..4a29e926 100644
--- a/chrome/browser/ui/views/web_apps/web_app_integration_test_driver.cc
+++ b/chrome/browser/ui/views/web_apps/web_app_integration_test_driver.cc
@@ -461,7 +461,7 @@
 void WebAppIntegrationTestDriver::TearDownOnMainThread() {
   observation_.Reset();
   if (delegate_->IsSyncTest())
-    delegate_->SyncTurnOff();
+    SyncTurnOff();
   for (auto* profile : delegate_->GetAllProfiles()) {
     auto* provider = GetProviderForProfile(profile);
     base::RunLoop run_loop;
@@ -502,7 +502,7 @@
 }
 
 void WebAppIntegrationTestDriver::AcceptAppIdUpdateDialog() {
-  BeforeStateChangeAction();
+  BeforeStateChangeAction(__FUNCTION__);
 
   views::NamedWidgetShownWaiter waiter(views::test::AnyWidgetTestPasskey{},
                                        "WebAppIdentityUpdateConfirmationView");
@@ -514,7 +514,7 @@
 }
 
 void WebAppIntegrationTestDriver::CloseCustomToolbar() {
-  BeforeStateChangeAction();
+  BeforeStateChangeAction(__FUNCTION__);
   ASSERT_TRUE(app_browser());
   BrowserView* app_view = BrowserView::GetBrowserViewForBrowser(app_browser());
   content::WebContents* web_contents = app_view->GetActiveWebContents();
@@ -529,7 +529,7 @@
 }
 
 void WebAppIntegrationTestDriver::ClosePwa() {
-  BeforeStateChangeAction();
+  BeforeStateChangeAction(__FUNCTION__);
   ASSERT_TRUE(app_browser()) << "No current app browser";
   app_browser()->window()->Close();
   ui_test_utils::WaitForBrowserToClose(app_browser());
@@ -538,21 +538,21 @@
 
 void WebAppIntegrationTestDriver::DisableRunOnOSLoginMode(
     const std::string& site_mode) {
-  BeforeStateChangeAction();
+  BeforeStateChangeAction(__FUNCTION__);
   SetRunOnOsLoginMode(site_mode, apps::mojom::RunOnOsLoginMode::kNotRun);
   AfterStateChangeAction();
 }
 
 void WebAppIntegrationTestDriver::EnableRunOnOSLoginMode(
     const std::string& site_mode) {
-  BeforeStateChangeAction();
+  BeforeStateChangeAction(__FUNCTION__);
   SetRunOnOsLoginMode(site_mode, apps::mojom::RunOnOsLoginMode::kWindowed);
   AfterStateChangeAction();
 }
 
 void WebAppIntegrationTestDriver::InstallCreateShortcutTabbed(
     const std::string& site_mode) {
-  BeforeStateChangeAction();
+  BeforeStateChangeAction(__FUNCTION__);
   MaybeNavigateTabbedBrowserInScope(site_mode);
   InstallCreateShortcut(/*open_in_window=*/false);
   AfterStateChangeAction();
@@ -560,7 +560,7 @@
 
 void WebAppIntegrationTestDriver::InstallCreateShortcutWindowed(
     const std::string& site_mode) {
-  BeforeStateChangeAction();
+  BeforeStateChangeAction(__FUNCTION__);
   MaybeNavigateTabbedBrowserInScope(site_mode);
   InstallCreateShortcut(/*open_in_window=*/true);
   AfterStateChangeAction();
@@ -568,7 +568,7 @@
 
 void WebAppIntegrationTestDriver::InstallMenuOption(
     const std::string& site_mode) {
-  BeforeStateChangeAction();
+  BeforeStateChangeAction(__FUNCTION__);
   MaybeNavigateTabbedBrowserInScope(site_mode);
   chrome::SetAutoAcceptPWAInstallConfirmationForTesting(/*auto_accept=*/true);
   content::WindowedNotificationObserver app_loaded_observer(
@@ -586,7 +586,7 @@
 
 #if !BUILDFLAG(IS_CHROMEOS)
 void WebAppIntegrationTestDriver::InstallLocally(const std::string& site_mode) {
-  BeforeStateChangeAction();
+  BeforeStateChangeAction(__FUNCTION__);
   absl::optional<AppState> app_state = GetAppBySiteMode(
       before_state_change_action_state_.get(), profile(), site_mode);
   ASSERT_TRUE(app_state) << "App not installed: " << site_mode;
@@ -610,7 +610,7 @@
 
 void WebAppIntegrationTestDriver::InstallOmniboxIcon(
     const std::string& site_mode) {
-  BeforeStateChangeAction();
+  BeforeStateChangeAction(__FUNCTION__);
   MaybeNavigateTabbedBrowserInScope(site_mode);
   chrome::SetAutoAcceptPWAInstallConfirmationForTesting(true);
 
@@ -643,7 +643,7 @@
 
 void WebAppIntegrationTestDriver::InstallPolicyAppTabbedNoShortcut(
     const std::string& site_mode) {
-  BeforeStateChangeAction();
+  BeforeStateChangeAction(__FUNCTION__);
   InstallPolicyAppInternal(
       site_mode, base::Value(kDefaultLaunchContainerTabValue),
       /*create_shortcut=*/false, apps::mojom::RunOnOsLoginPtr());
@@ -652,7 +652,7 @@
 
 void WebAppIntegrationTestDriver::InstallPolicyAppTabbedShortcut(
     const std::string& site_mode) {
-  BeforeStateChangeAction();
+  BeforeStateChangeAction(__FUNCTION__);
   InstallPolicyAppInternal(
       site_mode, base::Value(kDefaultLaunchContainerTabValue),
       /*create_shortcut=*/true, apps::mojom::RunOnOsLoginPtr());
@@ -661,7 +661,7 @@
 
 void WebAppIntegrationTestDriver::InstallPolicyAppWindowedNoShortcut(
     const std::string& site_mode) {
-  BeforeStateChangeAction();
+  BeforeStateChangeAction(__FUNCTION__);
   InstallPolicyAppInternal(
       site_mode, base::Value(kDefaultLaunchContainerWindowValue),
       /*create_shortcut=*/false, apps::mojom::RunOnOsLoginPtr());
@@ -670,7 +670,7 @@
 
 void WebAppIntegrationTestDriver::InstallPolicyAppWindowedShortcut(
     const std::string& site_mode) {
-  BeforeStateChangeAction();
+  BeforeStateChangeAction(__FUNCTION__);
   InstallPolicyAppInternal(
       site_mode, base::Value(kDefaultLaunchContainerWindowValue),
       /*create_shortcut=*/true, apps::mojom::RunOnOsLoginPtr());
@@ -679,7 +679,7 @@
 
 void WebAppIntegrationTestDriver::InstallPolicyAppOsLoginModeAllowed(
     const std::string& site_mode) {
-  BeforeStateChangeAction();
+  BeforeStateChangeAction(__FUNCTION__);
   InstallPolicyAppInternal(
       site_mode, base::Value(kDefaultLaunchContainerTabValue),
       /*create_shortcut=*/true,
@@ -690,7 +690,7 @@
 
 void WebAppIntegrationTestDriver::InstallPolicyAppOsLoginModeBlocked(
     const std::string& site_mode) {
-  BeforeStateChangeAction();
+  BeforeStateChangeAction(__FUNCTION__);
   InstallPolicyAppInternal(
       site_mode, base::Value(kDefaultLaunchContainerTabValue),
       /*create_shortcut=*/true,
@@ -701,7 +701,7 @@
 
 void WebAppIntegrationTestDriver::LaunchFromChromeApps(
     const std::string& site_mode) {
-  BeforeStateChangeAction();
+  BeforeStateChangeAction(__FUNCTION__);
   absl::optional<AppState> app_state = GetAppBySiteMode(
       before_state_change_action_state_.get(), profile(), site_mode);
   ASSERT_TRUE(app_state.has_value())
@@ -729,7 +729,7 @@
 
 void WebAppIntegrationTestDriver::LaunchFromLaunchIcon(
     const std::string& site_mode) {
-  BeforeStateChangeAction();
+  BeforeStateChangeAction(__FUNCTION__);
   NavigateBrowser(site_mode);
 
   EXPECT_TRUE(intent_picker_view()->GetVisible());
@@ -757,7 +757,7 @@
 
 void WebAppIntegrationTestDriver::LaunchFromMenuOption(
     const std::string& site_mode) {
-  BeforeStateChangeAction();
+  BeforeStateChangeAction(__FUNCTION__);
   NavigateBrowser(site_mode);
   absl::optional<AppState> app_state = GetAppBySiteMode(
       before_state_change_action_state_.get(), profile(), site_mode);
@@ -781,7 +781,7 @@
 
 void WebAppIntegrationTestDriver::LaunchFromShortcut(
     const std::string& site_mode) {
-  BeforeStateChangeAction();
+  BeforeStateChangeAction(__FUNCTION__);
   absl::optional<AppState> app_state = GetAppBySiteMode(
       before_state_change_action_state_.get(), profile(), site_mode);
   ASSERT_TRUE(app_state);
@@ -822,7 +822,7 @@
 void WebAppIntegrationTestDriver::LaunchAppSettingsFromAppMenu(
     const std::string& site_mode) {
 #if !BUILDFLAG(IS_CHROMEOS)
-  BeforeStateChangeAction();
+  BeforeStateChangeAction(__FUNCTION__);
   absl::optional<AppState> app_state = GetAppBySiteMode(
       before_state_change_action_state_.get(), profile(), site_mode);
   ASSERT_TRUE(app_state.has_value())
@@ -860,7 +860,7 @@
 void WebAppIntegrationTestDriver::LaunchAppSettingsFromChromeApps(
     const std::string& site_mode) {
 #if !BUILDFLAG(IS_CHROMEOS)
-  BeforeStateChangeAction();
+  BeforeStateChangeAction(__FUNCTION__);
   absl::optional<AppState> app_state = GetAppBySiteMode(
       before_state_change_action_state_.get(), profile(), site_mode);
   ASSERT_TRUE(app_state.has_value())
@@ -887,28 +887,28 @@
 
 void WebAppIntegrationTestDriver::NavigateBrowser(
     const std::string& site_mode) {
-  BeforeStateChangeAction();
+  BeforeStateChangeAction(__FUNCTION__);
   NavigateTabbedBrowserToSite(GetInScopeURL(site_mode));
   AfterStateChangeAction();
 }
 
 void WebAppIntegrationTestDriver::NavigatePwaSiteATo(
     const std::string& site_mode) {
-  BeforeStateChangeAction();
+  BeforeStateChangeAction(__FUNCTION__);
   app_browser_ = GetAppBrowserForSite("SiteA");
   NavigateToURLAndWait(app_browser(), GetAppStartURL(site_mode), false);
   AfterStateChangeAction();
 }
 
 void WebAppIntegrationTestDriver::NavigateNotfoundUrl() {
-  BeforeStateChangeAction();
+  BeforeStateChangeAction(__FUNCTION__);
   NavigateTabbedBrowserToSite(
       embedded_test_server()->GetURL("/non-existant/index.html"));
   AfterStateChangeAction();
 }
 
 void WebAppIntegrationTestDriver::NavigateTabbedBrowserToSite(const GURL& url) {
-  BeforeStateChangeAction();
+  BeforeStateChangeAction(__FUNCTION__);
   DCHECK(browser());
   content::WebContents* web_contents = GetCurrentTab(browser());
   auto* app_banner_manager =
@@ -921,7 +921,7 @@
 
 void WebAppIntegrationTestDriver::ManifestUpdateTitle(
     const std::string& site_mode) {
-  BeforeStateChangeAction();
+  BeforeStateChangeAction(__FUNCTION__);
   ASSERT_EQ("SiteA", site_mode) << "Only site mode of 'SiteA' is supported";
   ASSERT_TRUE(base::Contains(g_site_mode_to_relative_scope_url, site_mode));
   auto scope_url_path =
@@ -936,7 +936,7 @@
 
 void WebAppIntegrationTestDriver::ManifestUpdateDisplayBrowser(
     const std::string& site_mode) {
-  BeforeStateChangeAction();
+  BeforeStateChangeAction(__FUNCTION__);
   ASSERT_EQ("SiteA", site_mode) << "Only site mode of 'SiteA' is supported";
   ASSERT_TRUE(base::Contains(g_site_mode_to_relative_scope_url, site_mode));
   auto scope_url_path =
@@ -951,7 +951,7 @@
 
 void WebAppIntegrationTestDriver::ManifestUpdateDisplayMinimal(
     const std::string& site_mode) {
-  BeforeStateChangeAction();
+  BeforeStateChangeAction(__FUNCTION__);
   ASSERT_EQ("SiteA", site_mode) << "Only site mode of 'SiteA' is supported";
   ASSERT_TRUE(base::Contains(g_site_mode_to_relative_scope_url, site_mode));
   auto scope_url_path =
@@ -966,7 +966,7 @@
 
 void WebAppIntegrationTestDriver::ManifestUpdateScopeSiteAFooTo(
     const std::string& scope_mode) {
-  BeforeStateChangeAction();
+  BeforeStateChangeAction(__FUNCTION__);
   // The `scope_mode` would be changing the scope set in the manifest file. For
   // simplicity, right now only SiteA is supported, so that is just hardcoded in
   // manifest_scope_site_a.json, which is specified in the URL.
@@ -983,7 +983,7 @@
 }
 
 void WebAppIntegrationTestDriver::OpenInChrome(const std::string& site_mode) {
-  BeforeStateChangeAction();
+  BeforeStateChangeAction(__FUNCTION__);
   absl::optional<AppState> app_state = GetAppBySiteMode(
       before_state_change_action_state_.get(), profile(), site_mode);
   ASSERT_TRUE(app_state);
@@ -999,7 +999,7 @@
 }
 
 void WebAppIntegrationTestDriver::SetOpenInTab(const std::string& site_mode) {
-  BeforeStateChangeAction();
+  BeforeStateChangeAction(__FUNCTION__);
   absl::optional<AppState> app_state = GetAppBySiteMode(
       before_state_change_action_state_.get(), profile(), site_mode);
   ASSERT_TRUE(app_state.has_value())
@@ -1026,7 +1026,7 @@
 
 void WebAppIntegrationTestDriver::SetOpenInWindow(
     const std::string& site_mode) {
-  BeforeStateChangeAction();
+  BeforeStateChangeAction(__FUNCTION__);
   absl::optional<AppState> app_state = GetAppBySiteMode(
       before_state_change_action_state_.get(), profile(), site_mode);
   ASSERT_TRUE(app_state.has_value())
@@ -1053,7 +1053,7 @@
 
 void WebAppIntegrationTestDriver::SwitchProfileClients(
     const std::string& client_mode) {
-  BeforeStateChangeAction();
+  BeforeStateChangeAction(__FUNCTION__);
   std::vector<Profile*> profiles = delegate_->GetAllProfiles();
   ASSERT_EQ(2U, profiles.size())
       << "Cannot switch profile clients if delegate only supports one profile";
@@ -1072,20 +1072,20 @@
 }
 
 void WebAppIntegrationTestDriver::SyncTurnOff() {
-  BeforeStateChangeAction();
+  BeforeStateChangeAction(__FUNCTION__);
   delegate_->SyncTurnOff();
   AfterStateChangeAction();
 }
 
 void WebAppIntegrationTestDriver::SyncTurnOn() {
-  BeforeStateChangeAction();
+  BeforeStateChangeAction(__FUNCTION__);
   delegate_->SyncTurnOn();
   AfterStateChangeAction();
 }
 
 void WebAppIntegrationTestDriver::UninstallFromList(
     const std::string& site_mode) {
-  BeforeStateChangeAction();
+  BeforeStateChangeAction(__FUNCTION__);
   absl::optional<AppState> app_state = GetAppBySiteMode(
       before_state_change_action_state_.get(), profile(), site_mode);
   ASSERT_TRUE(app_state) << "App not installed: " << site_mode;
@@ -1133,7 +1133,7 @@
 
 void WebAppIntegrationTestDriver::UninstallFromMenu(
     const std::string& site_mode) {
-  BeforeStateChangeAction();
+  BeforeStateChangeAction(__FUNCTION__);
   absl::optional<AppState> app_state = GetAppBySiteMode(
       before_state_change_action_state_.get(), profile(), site_mode);
   ASSERT_TRUE(app_state.has_value())
@@ -1168,7 +1168,7 @@
 
 void WebAppIntegrationTestDriver::UninstallPolicyApp(
     const std::string& site_mode) {
-  BeforeStateChangeAction();
+  BeforeStateChangeAction(__FUNCTION__);
   GURL url = GetAppStartURL(site_mode);
   auto policy_app = GetAppBySiteMode(before_state_change_action_state_.get(),
                                      profile(), site_mode);
@@ -1205,7 +1205,7 @@
 void WebAppIntegrationTestDriver::UninstallFromOs(
     const std::string& site_mode) {
 #if BUILDFLAG(IS_WIN)
-  BeforeStateChangeAction();
+  BeforeStateChangeAction(__FUNCTION__);
   absl::optional<AppState> app_state = GetAppBySiteMode(
       before_state_change_action_state_.get(), profile(), site_mode);
   ASSERT_TRUE(app_state.has_value())
@@ -1231,7 +1231,7 @@
 }
 
 void WebAppIntegrationTestDriver::CheckAppListEmpty() {
-  BeforeStateCheckAction();
+  BeforeStateCheckAction(__FUNCTION__);
   absl::optional<ProfileState> state =
       GetStateForProfile(after_state_change_action_state_.get(), profile());
   ASSERT_TRUE(state.has_value());
@@ -1241,7 +1241,7 @@
 
 void WebAppIntegrationTestDriver::CheckAppInListNotLocallyInstalled(
     const std::string& site_mode) {
-  BeforeStateCheckAction();
+  BeforeStateCheckAction(__FUNCTION__);
   // Note: This is a partially supported action.
   absl::optional<AppState> app_state = GetAppBySiteMode(
       after_state_change_action_state_.get(), profile(), site_mode);
@@ -1252,7 +1252,7 @@
 
 void WebAppIntegrationTestDriver::CheckAppInListTabbed(
     const std::string& site_mode) {
-  BeforeStateCheckAction();
+  BeforeStateCheckAction(__FUNCTION__);
   // Note: This is a partially supported action.
   absl::optional<AppState> app_state = GetAppBySiteMode(
       after_state_change_action_state_.get(), profile(), site_mode);
@@ -1263,7 +1263,7 @@
 
 void WebAppIntegrationTestDriver::CheckAppInListWindowed(
     const std::string& site_mode) {
-  BeforeStateCheckAction();
+  BeforeStateCheckAction(__FUNCTION__);
   // Note: This is a partially supported action.
   absl::optional<AppState> app_state = GetAppBySiteMode(
       after_state_change_action_state_.get(), profile(), site_mode);
@@ -1274,7 +1274,7 @@
 }
 
 void WebAppIntegrationTestDriver::CheckAppNavigationIsStartUrl() {
-  BeforeStateCheckAction();
+  BeforeStateCheckAction(__FUNCTION__);
   ASSERT_FALSE(active_app_id_.empty());
   ASSERT_TRUE(app_browser());
   GURL url = app_browser()->tab_strip_model()->GetActiveWebContents()->GetURL();
@@ -1285,7 +1285,7 @@
 void WebAppIntegrationTestDriver::CheckBrowserNavigationIsAppSettings(
     const std::string& site_mode) {
 #if !BUILDFLAG(IS_CHROMEOS)
-  BeforeStateCheckAction();
+  BeforeStateCheckAction(__FUNCTION__);
   absl::optional<AppState> app_state = GetAppBySiteMode(
       after_state_change_action_state_.get(), profile(), site_mode);
   ASSERT_TRUE(app_state.has_value());
@@ -1301,7 +1301,7 @@
 
 void WebAppIntegrationTestDriver::CheckAppNotInList(
     const std::string& site_mode) {
-  BeforeStateCheckAction();
+  BeforeStateCheckAction(__FUNCTION__);
   absl::optional<AppState> app_state = GetAppBySiteMode(
       after_state_change_action_state_.get(), profile(), site_mode);
   EXPECT_FALSE(app_state.has_value());
@@ -1310,7 +1310,7 @@
 
 void WebAppIntegrationTestDriver::CheckPlatformShortcutAndIcon(
     const std::string& site_mode) {
-  BeforeStateCheckAction();
+  BeforeStateCheckAction(__FUNCTION__);
   absl::optional<AppState> app_state = GetAppBySiteMode(
       after_state_change_action_state_.get(), profile(), site_mode);
   ASSERT_TRUE(app_state);
@@ -1324,7 +1324,7 @@
   // when no web app is installed (or any other action has happened yet).
   if (!before_state_change_action_state_ && !after_state_change_action_state_)
     return;
-  BeforeStateCheckAction();
+  BeforeStateCheckAction(__FUNCTION__);
   absl::optional<AppState> app_state = GetAppBySiteMode(
       after_state_change_action_state_.get(), profile(), site_mode);
   if (!app_state) {
@@ -1347,7 +1347,7 @@
 }
 
 void WebAppIntegrationTestDriver::CheckAppTitleSiteA(const std::string& title) {
-  BeforeStateCheckAction();
+  BeforeStateCheckAction(__FUNCTION__);
   absl::optional<AppState> app_state = GetAppBySiteMode(
       after_state_change_action_state_.get(), profile(), "SiteA");
   ASSERT_TRUE(app_state);
@@ -1358,7 +1358,7 @@
 void WebAppIntegrationTestDriver::CheckAppWindowMode(
     const std::string& site_mode,
     apps::mojom::WindowMode window_mode) {
-  BeforeStateCheckAction();
+  BeforeStateCheckAction(__FUNCTION__);
   absl::optional<AppState> app_state = GetAppBySiteMode(
       after_state_change_action_state_.get(), profile(), site_mode);
   ASSERT_TRUE(app_state);
@@ -1367,7 +1367,7 @@
 }
 
 void WebAppIntegrationTestDriver::CheckInstallable() {
-  BeforeStateCheckAction();
+  BeforeStateCheckAction(__FUNCTION__);
   absl::optional<BrowserState> browser_state = GetStateForBrowser(
       after_state_change_action_state_.get(), profile(), browser());
   ASSERT_TRUE(browser_state.has_value());
@@ -1379,7 +1379,7 @@
 }
 
 void WebAppIntegrationTestDriver::CheckInstallIconShown() {
-  BeforeStateCheckAction();
+  BeforeStateCheckAction(__FUNCTION__);
   absl::optional<BrowserState> browser_state = GetStateForBrowser(
       after_state_change_action_state_.get(), profile(), browser());
   ASSERT_TRUE(browser_state.has_value());
@@ -1389,7 +1389,7 @@
 }
 
 void WebAppIntegrationTestDriver::CheckInstallIconNotShown() {
-  BeforeStateCheckAction();
+  BeforeStateCheckAction(__FUNCTION__);
   absl::optional<BrowserState> browser_state = GetStateForBrowser(
       after_state_change_action_state_.get(), profile(), browser());
   ASSERT_TRUE(browser_state.has_value());
@@ -1399,7 +1399,7 @@
 }
 
 void WebAppIntegrationTestDriver::CheckLaunchIconShown() {
-  BeforeStateCheckAction();
+  BeforeStateCheckAction(__FUNCTION__);
   absl::optional<BrowserState> browser_state = GetStateForBrowser(
       after_state_change_action_state_.get(), profile(), browser());
   ASSERT_TRUE(browser_state.has_value());
@@ -1408,7 +1408,7 @@
 }
 
 void WebAppIntegrationTestDriver::CheckLaunchIconNotShown() {
-  BeforeStateCheckAction();
+  BeforeStateCheckAction(__FUNCTION__);
   absl::optional<BrowserState> browser_state = GetStateForBrowser(
       after_state_change_action_state_.get(), profile(), browser());
   ASSERT_TRUE(browser_state.has_value());
@@ -1417,7 +1417,7 @@
 }
 
 void WebAppIntegrationTestDriver::CheckTabCreated() {
-  BeforeStateCheckAction();
+  BeforeStateCheckAction(__FUNCTION__);
   DCHECK(before_state_change_action_state_);
   absl::optional<BrowserState> most_recent_browser_state = GetStateForBrowser(
       after_state_change_action_state_.get(), profile(), browser());
@@ -1435,7 +1435,7 @@
 }
 
 void WebAppIntegrationTestDriver::CheckTabNotCreated() {
-  BeforeStateCheckAction();
+  BeforeStateCheckAction(__FUNCTION__);
   DCHECK(before_state_change_action_state_);
   absl::optional<BrowserState> most_recent_browser_state = GetStateForBrowser(
       after_state_change_action_state_.get(), profile(), browser());
@@ -1449,7 +1449,7 @@
 }
 
 void WebAppIntegrationTestDriver::CheckCustomToolbar() {
-  BeforeStateCheckAction();
+  BeforeStateCheckAction(__FUNCTION__);
   ASSERT_TRUE(app_browser());
   EXPECT_TRUE(app_browser()->app_controller()->ShouldShowCustomTabBar());
   BrowserView* app_view = BrowserView::GetBrowserViewForBrowser(app_browser());
@@ -1462,7 +1462,7 @@
 
 void WebAppIntegrationTestDriver::CheckRunOnOSLoginModeEnabled(
     const std::string& site_mode) {
-  BeforeStateCheckAction();
+  BeforeStateCheckAction(__FUNCTION__);
   absl::optional<AppState> app_state = GetAppBySiteMode(
       after_state_change_action_state_.get(), profile(), site_mode);
   ASSERT_TRUE(app_state);
@@ -1473,7 +1473,7 @@
 
 void WebAppIntegrationTestDriver::CheckRunOnOSLoginModeDisabled(
     const std::string& site_mode) {
-  BeforeStateCheckAction();
+  BeforeStateCheckAction(__FUNCTION__);
   absl::optional<AppState> app_state = GetAppBySiteMode(
       after_state_change_action_state_.get(), profile(), site_mode);
   ASSERT_TRUE(app_state);
@@ -1484,7 +1484,7 @@
 
 void WebAppIntegrationTestDriver::CheckUserDisplayModeInternal(
     DisplayMode display_mode) {
-  BeforeStateCheckAction();
+  BeforeStateCheckAction(__FUNCTION__);
   absl::optional<AppState> app_state = GetStateForAppId(
       after_state_change_action_state_.get(), profile(), active_app_id_);
   ASSERT_TRUE(app_state.has_value());
@@ -1493,7 +1493,7 @@
 }
 
 void WebAppIntegrationTestDriver::CheckWindowClosed() {
-  BeforeStateCheckAction();
+  BeforeStateCheckAction(__FUNCTION__);
   DCHECK(before_state_change_action_state_);
   absl::optional<ProfileState> after_action_profile =
       GetStateForProfile(after_state_change_action_state_.get(), profile());
@@ -1507,7 +1507,7 @@
 }
 
 void WebAppIntegrationTestDriver::CheckWindowCreated() {
-  BeforeStateCheckAction();
+  BeforeStateCheckAction(__FUNCTION__);
   DCHECK(before_state_change_action_state_);
   absl::optional<ProfileState> after_action_profile =
       GetStateForProfile(after_state_change_action_state_.get(), profile());
@@ -1524,7 +1524,7 @@
 }
 
 void WebAppIntegrationTestDriver::CheckWindowDisplayMinimal() {
-  BeforeStateCheckAction();
+  BeforeStateCheckAction(__FUNCTION__);
   DCHECK(app_browser());
   DCHECK(app_browser()->app_controller()->AsWebAppBrowserController());
   absl::optional<AppState> app_state = GetStateForAppId(
@@ -1545,7 +1545,7 @@
 }
 
 void WebAppIntegrationTestDriver::CheckWindowDisplayStandalone() {
-  BeforeStateCheckAction();
+  BeforeStateCheckAction(__FUNCTION__);
   DCHECK(app_browser());
   DCHECK(app_browser()->app_controller()->AsWebAppBrowserController());
   absl::optional<AppState> app_state = GetStateForAppId(
@@ -1587,7 +1587,9 @@
   }
 }
 
-void WebAppIntegrationTestDriver::BeforeStateChangeAction() {
+void WebAppIntegrationTestDriver::BeforeStateChangeAction(
+    const char* function) {
+  LOG(INFO) << "BeforeStateChangeAction: " << function;
   ++executing_action_level_;
   std::unique_ptr<StateSnapshot> current_state = ConstructStateSnapshot();
   if (after_state_change_action_state_) {
@@ -1622,7 +1624,8 @@
   MaybeWaitForManifestUpdates();
 }
 
-void WebAppIntegrationTestDriver::BeforeStateCheckAction() {
+void WebAppIntegrationTestDriver::BeforeStateCheckAction(const char* function) {
+  LOG(INFO) << "BeforeStateCheckAction: " << function;
   ++executing_action_level_;
   DCHECK(after_state_change_action_state_);
 }
diff --git a/chrome/browser/ui/views/web_apps/web_app_integration_test_driver.h b/chrome/browser/ui/views/web_apps/web_app_integration_test_driver.h
index 98f46d08..f6643e4 100644
--- a/chrome/browser/ui/views/web_apps/web_app_integration_test_driver.h
+++ b/chrome/browser/ui/views/web_apps/web_app_integration_test_driver.h
@@ -224,11 +224,11 @@
 
  private:
   // Must be called at the beginning of every state change action function.
-  void BeforeStateChangeAction();
+  void BeforeStateChangeAction(const char* function);
   // Must be called at the end of every state change action function.
   void AfterStateChangeAction();
   // Must be called at the beginning of every state check action function.
-  void BeforeStateCheckAction();
+  void BeforeStateCheckAction(const char* function);
   // Must be called at the end of every state check action function.
   void AfterStateCheckAction();
 
diff --git a/chrome/browser/ui/web_applications/test/system_web_app_interactive_uitest.cc b/chrome/browser/ui/web_applications/test/system_web_app_interactive_uitest.cc
index ce4b66dc..91973418 100644
--- a/chrome/browser/ui/web_applications/test/system_web_app_interactive_uitest.cc
+++ b/chrome/browser/ui/web_applications/test/system_web_app_interactive_uitest.cc
@@ -882,7 +882,7 @@
   WaitForTestSystemAppInstall();
 
   // There might be an initial browser from the testing framework.
-  int initial_browser_count = BrowserList::GetInstance()->size();
+  size_t initial_browser_count = BrowserList::GetInstance()->size();
 
   // Test that a non descript URL gets rejected.
   GURL url1 = GURL("http://www.foo.bar");
@@ -914,7 +914,7 @@
   WaitForTestSystemAppInstall();
 
   // There might be an initial browser from the testing framework.
-  int initial_browser_count = BrowserList::GetInstance()->size();
+  size_t initial_browser_count = BrowserList::GetInstance()->size();
 
   // Start an application which uses the OS url handler.
   LaunchAndWaitForActivationChange(GURL(chrome::kOsUICreditsURL));
@@ -939,7 +939,7 @@
   WaitForTestSystemAppInstall();
 
   // There might be an initial browser from the testing framework.
-  int initial_browser_count = BrowserList::GetInstance()->size();
+  size_t initial_browser_count = BrowserList::GetInstance()->size();
 
   // Start an application using the OS Url handler.
   LaunchAndWaitForActivationChange(GURL(chrome::kOsUICreditsURL));
diff --git a/chrome/browser/ui/webui/chromeos/multidevice_internals/multidevice_internals_phone_hub_handler.cc b/chrome/browser/ui/webui/chromeos/multidevice_internals/multidevice_internals_phone_hub_handler.cc
index 0e417f2..1f2159a 100644
--- a/chrome/browser/ui/webui/chromeos/multidevice_internals/multidevice_internals_phone_hub_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/multidevice_internals/multidevice_internals_phone_hub_handler.cc
@@ -210,10 +210,11 @@
           base::Unretained(this)));
 
   web_ui()->RegisterDeprecatedMessageCallback(
-      "resetHasNotificationSetupUiBeenDismissed",
-      base::BindRepeating(&MultidevicePhoneHubHandler::
-                              HandleResetHasNotificationSetupUiBeenDismissed,
-                          base::Unretained(this)));
+      "resetHasMultideviceFeatureSetupUiBeenDismissed",
+      base::BindRepeating(
+          &MultidevicePhoneHubHandler::
+              HandleResetHasMultideviceFeatureSetupUiBeenDismissed,
+          base::Unretained(this)));
 
   web_ui()->RegisterDeprecatedMessageCallback(
       "resetCameraRollOnboardingUiDismissed",
@@ -600,8 +601,9 @@
   PA_LOG(VERBOSE) << "Reset kHideOnboardingUi pref";
 }
 
-void MultidevicePhoneHubHandler::HandleResetHasNotificationSetupUiBeenDismissed(
-    const base::ListValue* args) {
+void MultidevicePhoneHubHandler::
+    HandleResetHasMultideviceFeatureSetupUiBeenDismissed(
+        const base::ListValue* args) {
   PrefService* prefs = Profile::FromWebUI(web_ui())->GetPrefs();
   prefs->SetBoolean(phonehub::prefs::kHasDismissedSetupRequiredUi, false);
   PA_LOG(VERBOSE) << "Reset kHasDismissedSetupRequiredUi pref";
diff --git a/chrome/browser/ui/webui/chromeos/multidevice_internals/multidevice_internals_phone_hub_handler.h b/chrome/browser/ui/webui/chromeos/multidevice_internals/multidevice_internals_phone_hub_handler.h
index 933f876..ed23ae4b 100644
--- a/chrome/browser/ui/webui/chromeos/multidevice_internals/multidevice_internals_phone_hub_handler.h
+++ b/chrome/browser/ui/webui/chromeos/multidevice_internals/multidevice_internals_phone_hub_handler.h
@@ -74,7 +74,7 @@
   void HandleSetFindMyDeviceStatus(const base::ListValue* args);
   void HandleSetTetherStatus(const base::ListValue* args);
   void HandleResetShouldShowOnboardingUi(const base::ListValue* args);
-  void HandleResetHasNotificationSetupUiBeenDismissed(
+  void HandleResetHasMultideviceFeatureSetupUiBeenDismissed(
       const base::ListValue* args);
   void HandleResetCameraRollOnboardingUiDismissed(const base::ListValue* args);
   void HandleSetFakeCameraRoll(const base::ListValue* args);
diff --git a/chrome/browser/ui/webui/settings/chromeos/accessibility_handler.cc b/chrome/browser/ui/webui/settings/chromeos/accessibility_handler.cc
index e60b234..0d0642e 100644
--- a/chrome/browser/ui/webui/settings/chromeos/accessibility_handler.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/accessibility_handler.cc
@@ -130,7 +130,7 @@
 }
 
 void AccessibilityHandler::OnJavascriptDisallowed() {
-  if (features::IsDictationOfflineAvailableAndEnabled())
+  if (features::IsDictationOfflineAvailable())
     soda_observation_.Reset();
 }
 
@@ -151,7 +151,7 @@
 }
 
 void AccessibilityHandler::MaybeAddSodaInstallerObserver() {
-  if (!features::IsDictationOfflineAvailableAndEnabled())
+  if (!features::IsDictationOfflineAvailable())
     return;
 
   speech::SodaInstaller* soda_installer = speech::SodaInstaller::GetInstance();
@@ -234,9 +234,6 @@
 }
 
 void AccessibilityHandler::MaybeAddDictationLocales() {
-  if (!features::IsExperimentalAccessibilityDictationOfflineEnabled())
-    return;
-
   base::flat_map<std::string, ash::Dictation::LocaleData> locales =
       ash::Dictation::GetAllSupportedLocales();
 
diff --git a/chrome/browser/ui/webui/settings/chromeos/accessibility_handler_browsertest.cc b/chrome/browser/ui/webui/settings/chromeos/accessibility_handler_browsertest.cc
index d0991565..9377193 100644
--- a/chrome/browser/ui/webui/settings/chromeos/accessibility_handler_browsertest.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/accessibility_handler_browsertest.cc
@@ -51,9 +51,7 @@
 
   void SetUpCommandLine(base::CommandLine* command_line) override {
     scoped_feature_list_.InitWithFeatures(
-        {::features::kExperimentalAccessibilityDictationOffline,
-         features::kOnDeviceSpeechRecognition},
-        {});
+        {features::kOnDeviceSpeechRecognition}, {});
   }
 
   void SetUpOnMainThread() override {
diff --git a/chrome/browser/ui/webui/settings/chromeos/accessibility_section.cc b/chrome/browser/ui/webui/settings/chromeos/accessibility_section.cc
index 3d05897..4933f39 100644
--- a/chrome/browser/ui/webui/settings/chromeos/accessibility_section.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/accessibility_section.cc
@@ -342,10 +342,6 @@
          ash::TabletMode::IsBoardTypeMarkedAsTabletCapable();
 }
 
-bool AreDictationLocalePrefsAllowed() {
-  return ::features::IsExperimentalAccessibilityDictationOfflineEnabled();
-}
-
 }  // namespace
 
 AccessibilitySection::AccessibilitySection(
@@ -748,9 +744,7 @@
 
   html_source->AddLocalizedString(
       "dictationDescription",
-      ::features::IsExperimentalAccessibilityDictationOfflineEnabled()
-          ? IDS_SETTINGS_ACCESSIBILITY_DICTATION_NEW_DESCRIPTION
-          : IDS_SETTINGS_ACCESSIBILITY_DICTATION_DESCRIPTION);
+      IDS_SETTINGS_ACCESSIBILITY_DICTATION_NEW_DESCRIPTION);
 
   html_source->AddString("a11yLearnMoreUrl",
                          chrome::kChromeAccessibilityHelpURL);
@@ -769,9 +763,6 @@
       "isMagnifierContinuousMouseFollowingModeSettingEnabled",
       IsMagnifierContinuousMouseFollowingModeSettingEnabled());
 
-  html_source->AddBoolean("areDictationLocalePrefsAllowed",
-                          AreDictationLocalePrefsAllowed());
-
   html_source->AddBoolean(
       "isDictationCommandsFeatureEnabled",
       ::features::IsExperimentalAccessibilityDictationCommandsEnabled());
diff --git a/chrome/browser/ui/webui/settings/chromeos/multidevice_handler.cc b/chrome/browser/ui/webui/settings/chromeos/multidevice_handler.cc
index dbbc2ee..f020b1b 100644
--- a/chrome/browser/ui/webui/settings/chromeos/multidevice_handler.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/multidevice_handler.cc
@@ -35,6 +35,7 @@
 
 namespace {
 
+const char kCameraRollAccessStatus[] = "cameraRollAccessStatus";
 const char kPageContentDataModeKey[] = "mode";
 const char kPageContentDataHostDeviceNameKey[] = "hostDeviceName";
 const char kPageContentDataBetterTogetherStateKey[] = "betterTogetherState";
@@ -78,7 +79,8 @@
 MultideviceHandler::MultideviceHandler(
     PrefService* prefs,
     multidevice_setup::MultiDeviceSetupClient* multidevice_setup_client,
-    phonehub::NotificationAccessManager* notification_access_manager,
+    phonehub::MultideviceFeatureAccessManager*
+        multidevice_feature_access_manager,
     multidevice_setup::AndroidSmsPairingStateTracker*
         android_sms_pairing_state_tracker,
     android_sms::AndroidSmsAppManager* android_sms_app_manager,
@@ -86,7 +88,7 @@
     ash::phonehub::CameraRollManager* camera_roll_manager)
     : prefs_(prefs),
       multidevice_setup_client_(multidevice_setup_client),
-      notification_access_manager_(notification_access_manager),
+      multidevice_feature_access_manager_(multidevice_feature_access_manager),
       android_sms_pairing_state_tracker_(android_sms_pairing_state_tracker),
       android_sms_app_manager_(android_sms_app_manager),
       apps_access_manager_(apps_access_manager),
@@ -161,9 +163,9 @@
   if (multidevice_setup_client_)
     multidevice_setup_observation_.Observe(multidevice_setup_client_);
 
-  if (notification_access_manager_)
-    notification_access_manager_observation_.Observe(
-        notification_access_manager_);
+  if (multidevice_feature_access_manager_)
+    multidevice_feature_access_manager_observation_.Observe(
+        multidevice_feature_access_manager_);
 
   if (android_sms_pairing_state_tracker_) {
     android_sms_pairing_state_tracker_observation_.Observe(
@@ -207,10 +209,10 @@
     multidevice_setup_observation_.Reset();
   }
 
-  if (notification_access_manager_) {
-    DCHECK(notification_access_manager_observation_.IsObservingSource(
-        notification_access_manager_));
-    notification_access_manager_observation_.Reset();
+  if (multidevice_feature_access_manager_) {
+    DCHECK(multidevice_feature_access_manager_observation_.IsObservingSource(
+        multidevice_feature_access_manager_));
+    multidevice_feature_access_manager_observation_.Reset();
     notification_access_operation_.reset();
   }
 
@@ -264,6 +266,10 @@
   UpdatePageContent();
 }
 
+void MultideviceHandler::OnCameraRollAccessChanged() {
+  UpdatePageContent();
+}
+
 void MultideviceHandler::OnPairingStateChanged() {
   UpdatePageContent();
   NotifyAndroidSmsInfoChange();
@@ -456,17 +462,19 @@
   DCHECK(features::IsPhoneHubEnabled());
   DCHECK(!notification_access_operation_);
 
-  phonehub::NotificationAccessManager::AccessStatus access_status =
-      notification_access_manager_->GetAccessStatus();
-  if (access_status != phonehub::NotificationAccessManager::AccessStatus::
-                           kAvailableButNotGranted) {
+  phonehub::MultideviceFeatureAccessManager::AccessStatus
+      notification_access_status =
+          multidevice_feature_access_manager_->GetNotificationAccessStatus();
+  if (notification_access_status != phonehub::MultideviceFeatureAccessManager::
+                                        AccessStatus::kAvailableButNotGranted) {
     PA_LOG(WARNING) << "Cannot request notification access setup flow; current "
-                    << "status: " << access_status;
+                    << "status: " << notification_access_status;
     return;
   }
 
   notification_access_operation_ =
-      notification_access_manager_->AttemptNotificationSetup(/*delegate=*/this);
+      multidevice_feature_access_manager_->AttemptNotificationSetup(
+          /*delegate=*/this);
   DCHECK(notification_access_operation_);
 }
 
@@ -603,19 +611,34 @@
           ? android_sms_pairing_state_tracker_->IsAndroidSmsPairingComplete()
           : false);
 
-  phonehub::NotificationAccessManager::AccessStatus access_status = phonehub::
-      NotificationAccessManager::AccessStatus::kAvailableButNotGranted;
-  phonehub::NotificationAccessManager::AccessProhibitedReason reason =
-      phonehub::NotificationAccessManager::AccessProhibitedReason::kUnknown;
-  if (notification_access_manager_) {
-    access_status = notification_access_manager_->GetAccessStatus();
-    reason = notification_access_manager_->GetAccessProhibitedReason();
+  phonehub::MultideviceFeatureAccessManager::AccessStatus
+      notification_access_status = phonehub::MultideviceFeatureAccessManager::
+          AccessStatus::kAvailableButNotGranted;
+  phonehub::MultideviceFeatureAccessManager::AccessProhibitedReason reason =
+      phonehub::MultideviceFeatureAccessManager::AccessProhibitedReason::
+          kUnknown;
+  if (multidevice_feature_access_manager_) {
+    notification_access_status =
+        multidevice_feature_access_manager_->GetNotificationAccessStatus();
+    reason = multidevice_feature_access_manager_
+                 ->GetNotificationAccessProhibitedReason();
   }
-  page_content_dictionary->SetIntKey(kNotificationAccessStatus,
-                                     static_cast<int32_t>(access_status));
+
+  page_content_dictionary->SetInteger(
+      kNotificationAccessStatus,
+      static_cast<int32_t>(notification_access_status));
   page_content_dictionary->SetIntKey(kNotificationAccessProhibitedReason,
                                      static_cast<int32_t>(reason));
 
+  phonehub::MultideviceFeatureAccessManager::AccessStatus
+      camera_roll_access_status = phonehub::MultideviceFeatureAccessManager::
+          AccessStatus::kAvailableButNotGranted;
+  if (multidevice_feature_access_manager_)
+    camera_roll_access_status =
+        multidevice_feature_access_manager_->GetCameraRollAccessStatus();
+  page_content_dictionary->SetInteger(
+      kCameraRollAccessStatus, static_cast<int32_t>(camera_roll_access_status));
+
   ash::eche_app::AppsAccessManager::AccessStatus apps_access_status =
       ash::eche_app::AppsAccessManager::AccessStatus::kAvailableButNotGranted;
   if (apps_access_manager_)
diff --git a/chrome/browser/ui/webui/settings/chromeos/multidevice_handler.h b/chrome/browser/ui/webui/settings/chromeos/multidevice_handler.h
index 2f0ed4b..53dcace 100644
--- a/chrome/browser/ui/webui/settings/chromeos/multidevice_handler.h
+++ b/chrome/browser/ui/webui/settings/chromeos/multidevice_handler.h
@@ -6,7 +6,7 @@
 #define CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_MULTIDEVICE_HANDLER_H_
 
 #include "ash/components/phonehub/camera_roll_manager.h"
-#include "ash/components/phonehub/notification_access_manager.h"
+#include "ash/components/phonehub/multidevice_feature_access_manager.h"
 #include "ash/components/phonehub/notification_access_setup_operation.h"
 #include "ash/services/multidevice_setup/public/cpp/multidevice_setup_client.h"
 #include "ash/services/multidevice_setup/public/mojom/multidevice_setup.mojom-forward.h"
@@ -34,7 +34,7 @@
       public multidevice_setup::MultiDeviceSetupClient::Observer,
       public multidevice_setup::AndroidSmsPairingStateTracker::Observer,
       public android_sms::AndroidSmsAppManager::Observer,
-      public phonehub::NotificationAccessManager::Observer,
+      public phonehub::MultideviceFeatureAccessManager::Observer,
       public phonehub::NotificationAccessSetupOperation::Delegate,
       public ash::eche_app::AppsAccessManager::Observer,
       public ash::eche_app::AppsAccessSetupOperation::Delegate,
@@ -43,7 +43,8 @@
   MultideviceHandler(
       PrefService* prefs,
       multidevice_setup::MultiDeviceSetupClient* multidevice_setup_client,
-      phonehub::NotificationAccessManager* notification_access_manager,
+      phonehub::MultideviceFeatureAccessManager*
+          multidevice_feature_access_manager,
       multidevice_setup::AndroidSmsPairingStateTracker*
           android_sms_pairing_state_tracker,
       android_sms::AndroidSmsAppManager* android_sms_app_manager,
@@ -80,8 +81,9 @@
   void OnAppsStatusChange(
       ash::eche_app::AppsAccessSetupOperation::Status new_status) override;
 
-  // phonehub::NotificationAccessManager::Observer:
+  // phonehub::MultideviceFeatureAccessManager::Observer:
   void OnNotificationAccessChanged() override;
+  void OnCameraRollAccessChanged() override;
 
   // multidevice_setup::AndroidSmsPairingStateTracker::Observer:
   void OnPairingStateChanged() override;
@@ -149,7 +151,8 @@
 
   multidevice_setup::MultiDeviceSetupClient* multidevice_setup_client_;
 
-  phonehub::NotificationAccessManager* notification_access_manager_;
+  phonehub::MultideviceFeatureAccessManager*
+      multidevice_feature_access_manager_;
   std::unique_ptr<phonehub::NotificationAccessSetupOperation>
       notification_access_operation_;
 
@@ -173,9 +176,9 @@
   base::ScopedObservation<android_sms::AndroidSmsAppManager,
                           android_sms::AndroidSmsAppManager::Observer>
       android_sms_app_manager_observation_{this};
-  base::ScopedObservation<phonehub::NotificationAccessManager,
-                          phonehub::NotificationAccessManager::Observer>
-      notification_access_manager_observation_{this};
+  base::ScopedObservation<phonehub::MultideviceFeatureAccessManager,
+                          phonehub::MultideviceFeatureAccessManager::Observer>
+      multidevice_feature_access_manager_observation_{this};
   base::ScopedObservation<ash::eche_app::AppsAccessManager,
                           ash::eche_app::AppsAccessManager::Observer>
       apps_access_manager_observation_{this};
diff --git a/chrome/browser/ui/webui/settings/chromeos/multidevice_handler_unittest.cc b/chrome/browser/ui/webui/settings/chromeos/multidevice_handler_unittest.cc
index 05dbe56..c6f095a 100644
--- a/chrome/browser/ui/webui/settings/chromeos/multidevice_handler_unittest.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/multidevice_handler_unittest.cc
@@ -7,7 +7,7 @@
 #include <memory>
 
 #include "ash/components/phonehub/fake_camera_roll_manager.h"
-#include "ash/components/phonehub/fake_notification_access_manager.h"
+#include "ash/components/phonehub/fake_multidevice_feature_access_manager.h"
 #include "ash/constants/ash_features.h"
 #include "ash/services/multidevice_setup/public/cpp/fake_android_sms_pairing_state_tracker.h"
 #include "ash/services/multidevice_setup/public/cpp/fake_multidevice_setup_client.h"
@@ -43,7 +43,8 @@
   TestMultideviceHandler(
       PrefService* prefs,
       multidevice_setup::MultiDeviceSetupClient* multidevice_setup_client,
-      phonehub::NotificationAccessManager* notification_access_manager,
+      phonehub::MultideviceFeatureAccessManager*
+          multidevice_feature_access_manager,
       multidevice_setup::AndroidSmsPairingStateTracker*
           android_sms_pairing_state_tracker,
       android_sms::AndroidSmsAppManager* android_sms_app_manager,
@@ -51,7 +52,7 @@
       ash::phonehub::CameraRollManager* camera_roll_manager)
       : MultideviceHandler(prefs,
                            multidevice_setup_client,
-                           notification_access_manager,
+                           multidevice_feature_access_manager,
                            android_sms_pairing_state_tracker,
                            android_sms_app_manager,
                            apps_access_manager,
@@ -97,7 +98,8 @@
         feature_states_map,
     bool expected_is_nearby_share_disallowed_by_policy_,
     bool expected_is_phone_hub_apps_access_granted_,
-    bool expected_is_camera_roll_file_permission_granted_) {
+    bool expected_is_camera_roll_file_permission_granted_,
+    bool expected_is_camera_roll_access_status_granted_) {
   const base::DictionaryValue* page_content_dict;
   EXPECT_TRUE(value->GetAsDictionary(&page_content_dict));
 
@@ -192,6 +194,9 @@
   EXPECT_THAT(
       page_content_dict->FindBoolKey("isPhoneHubPermissionsDialogSupported"),
       Optional(true));
+
+  EXPECT_THAT(page_content_dict->FindIntKey("cameraRollAccessStatus"),
+              Optional(expected_is_camera_roll_access_status_granted_ ? 2 : 1));
 }
 
 }  // namespace
@@ -210,9 +215,9 @@
   void SetUp() override {
     fake_multidevice_setup_client_ =
         std::make_unique<multidevice_setup::FakeMultiDeviceSetupClient>();
-    fake_notification_access_manager_ =
-        std::make_unique<phonehub::FakeNotificationAccessManager>(
-            phonehub::NotificationAccessManager::AccessStatus::
+    fake_multidevice_feature_access_manager_ =
+        std::make_unique<phonehub::FakeMultideviceFeatureAccessManager>(
+            phonehub::MultideviceFeatureAccessManager::AccessStatus::
                 kAvailableButNotGranted);
     fake_android_sms_pairing_state_tracker_ = std::make_unique<
         multidevice_setup::FakeAndroidSmsPairingStateTracker>();
@@ -237,7 +242,7 @@
 
     handler_ = std::make_unique<TestMultideviceHandler>(
         prefs_.get(), fake_multidevice_setup_client_.get(),
-        fake_notification_access_manager_.get(),
+        fake_multidevice_feature_access_manager_.get(),
         fake_android_sms_pairing_state_tracker_.get(),
         fake_android_sms_app_manager_.get(), fake_apps_access_manager_.get(),
         fake_camera_roll_manager_.get());
@@ -307,12 +312,14 @@
               call_data.arg3()->FindKey("enabled")->GetBool());
   }
 
-  void CallAttemptNotificationSetup(bool has_access_been_granted) {
-    fake_notification_access_manager()->SetAccessStatusInternal(
-        has_access_been_granted
-            ? phonehub::NotificationAccessManager::AccessStatus::kAccessGranted
-            : phonehub::NotificationAccessManager::AccessStatus::
-                  kAvailableButNotGranted);
+  void CallAttemptNotificationSetup(bool has_notification_access_been_granted) {
+    fake_multidevice_feature_access_manager()
+        ->SetNotificationAccessStatusInternal(
+            has_notification_access_been_granted
+                ? phonehub::MultideviceFeatureAccessManager::AccessStatus::
+                      kAccessGranted
+                : phonehub::MultideviceFeatureAccessManager::AccessStatus::
+                      kAvailableButNotGranted);
     base::ListValue empty_args;
     test_web_ui()->HandleReceivedMessage("attemptNotificationSetup",
                                          &empty_args);
@@ -476,6 +483,30 @@
     VerifyPageContent(call_data.arg2());
   }
 
+  void SimulateCameraRollAccessstatusChanged(
+      bool has_camera_roll_access_status_granted) {
+    size_t call_data_count_before_call = test_web_ui()->call_data().size();
+
+    fake_multidevice_feature_access_manager()
+        ->SetCameraRollAccessStatusInternal(
+            has_camera_roll_access_status_granted
+                ? phonehub::MultideviceFeatureAccessManager::AccessStatus::
+                      kAccessGranted
+                : phonehub::MultideviceFeatureAccessManager::AccessStatus::
+                      kAvailableButNotGranted);
+    expected_is_camera_roll_access_status_granted_ =
+        has_camera_roll_access_status_granted;
+    EXPECT_EQ(call_data_count_before_call + 1u,
+              test_web_ui()->call_data().size());
+
+    const content::TestWebUI::CallData& call_data =
+        CallDataAtIndex(call_data_count_before_call);
+    EXPECT_EQ("cr.webUIListenerCallback", call_data.function_name());
+    EXPECT_EQ("settings.updateMultidevicePageContentData",
+              call_data.arg1()->GetString());
+    VerifyPageContent(call_data.arg2());
+  }
+
   void CallRetryPendingHostSetup(bool success) {
     base::ListValue empty_args;
     test_web_ui()->HandleReceivedMessage("retryPendingHostSetup", &empty_args);
@@ -534,8 +565,9 @@
     return fake_android_sms_app_manager_.get();
   }
 
-  phonehub::FakeNotificationAccessManager* fake_notification_access_manager() {
-    return fake_notification_access_manager_.get();
+  phonehub::FakeMultideviceFeatureAccessManager*
+  fake_multidevice_feature_access_manager() {
+    return fake_multidevice_feature_access_manager_.get();
   }
 
   ash::eche_app::FakeAppsAccessManager* fake_apps_access_manager() {
@@ -550,8 +582,8 @@
       phonehub::NotificationAccessSetupOperation::Status status) {
     size_t call_data_count_before_call = test_web_ui()->call_data().size();
 
-    fake_notification_access_manager()->SetNotificationSetupOperationStatus(
-        status);
+    fake_multidevice_feature_access_manager()
+        ->SetNotificationSetupOperationStatus(status);
 
     bool completed_successfully = status ==
                                   phonehub::NotificationAccessSetupOperation::
@@ -570,7 +602,8 @@
   }
 
   bool IsNotificationAccessSetupOperationInProgress() {
-    return fake_notification_access_manager()->IsSetupOperationInProgress();
+    return fake_multidevice_feature_access_manager()
+        ->IsSetupOperationInProgress();
   }
 
   void SimulateAppsOptInStatusChange(
@@ -604,6 +637,7 @@
   bool expected_is_nearby_share_disallowed_by_policy_ = false;
   bool expected_is_phone_hub_apps_access_granted_ = false;
   bool expected_is_camera_roll_file_permission_granted_ = true;
+  bool expected_is_camera_roll_access_status_granted_ = false;
 
  private:
   void VerifyPageContent(const base::Value* value) {
@@ -613,7 +647,8 @@
         fake_multidevice_setup_client_->GetFeatureStates(),
         expected_is_nearby_share_disallowed_by_policy_,
         expected_is_phone_hub_apps_access_granted_,
-        expected_is_camera_roll_file_permission_granted_);
+        expected_is_camera_roll_file_permission_granted_,
+        expected_is_camera_roll_access_status_granted_);
   }
 
   content::BrowserTaskEnvironment task_environment_;
@@ -623,8 +658,8 @@
   std::unique_ptr<content::TestWebUI> test_web_ui_;
   std::unique_ptr<multidevice_setup::FakeMultiDeviceSetupClient>
       fake_multidevice_setup_client_;
-  std::unique_ptr<phonehub::FakeNotificationAccessManager>
-      fake_notification_access_manager_;
+  std::unique_ptr<phonehub::FakeMultideviceFeatureAccessManager>
+      fake_multidevice_feature_access_manager_;
   std::unique_ptr<multidevice_setup::FakeAndroidSmsPairingStateTracker>
       fake_android_sms_pairing_state_tracker_;
   std::unique_ptr<ash::eche_app::FakeAppsAccessManager>
@@ -783,6 +818,10 @@
   SimulateAppsAccessStatusChanged(/*has_access_been_granted=*/true);
   SimulateCameraRollFilePermissionChanged(/*file_permission_granted=*/false);
   SimulateCameraRollFilePermissionChanged(/*file_permission_granted=*/true);
+  SimulateCameraRollAccessstatusChanged(
+      /*has_camera_roll_access_been_granted=*/true);
+  SimulateCameraRollAccessstatusChanged(
+      /*has_camera_roll_access_been_granted=*/false);
 }
 
 TEST_F(MultideviceHandlerTest, RetryPendingHostSetup) {
diff --git a/chrome/browser/ui/webui/settings/chromeos/multidevice_section.cc b/chrome/browser/ui/webui/settings/chromeos/multidevice_section.cc
index 7eb11432..767f6c34 100644
--- a/chrome/browser/ui/webui/settings/chromeos/multidevice_section.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/multidevice_section.cc
@@ -624,7 +624,7 @@
       std::make_unique<chromeos::settings::MultideviceHandler>(
           pref_service_, multidevice_setup_client_,
           phone_hub_manager_
-              ? phone_hub_manager_->GetNotificationAccessManager()
+              ? phone_hub_manager_->GetMultideviceFeatureAccessManager()
               : nullptr,
           android_sms_service_
               ? android_sms_service_->android_sms_pairing_state_tracker()
diff --git a/chrome/browser/ui/webui/settings/settings_ui.cc b/chrome/browser/ui/webui/settings/settings_ui.cc
index 6e65ed4..73b3bdf3 100644
--- a/chrome/browser/ui/webui/settings/settings_ui.cc
+++ b/chrome/browser/ui/webui/settings/settings_ui.cc
@@ -406,8 +406,9 @@
         profile->GetPrefs(),
         ash::multidevice_setup::MultiDeviceSetupClientFactory::GetForProfile(
             profile),
-        phone_hub_manager ? phone_hub_manager->GetNotificationAccessManager()
-                          : nullptr,
+        phone_hub_manager
+            ? phone_hub_manager->GetMultideviceFeatureAccessManager()
+            : nullptr,
         android_sms_service
             ? android_sms_service->android_sms_pairing_state_tracker()
             : nullptr,
diff --git a/chrome/browser/ui/webui/signin/inline_login_handler_chromeos_browsertest.cc b/chrome/browser/ui/webui/signin/inline_login_handler_chromeos_browsertest.cc
index 83aa67e..626406e 100644
--- a/chrome/browser/ui/webui/signin/inline_login_handler_chromeos_browsertest.cc
+++ b/chrome/browser/ui/webui/signin/inline_login_handler_chromeos_browsertest.cc
@@ -534,7 +534,7 @@
   const base::span<const base::Value> result =
       CallGetAccountsNotAvailableInArc();
   // Two accounts are not available in ARC.
-  EXPECT_EQ(2, result.size());
+  EXPECT_EQ(2u, result.size());
   EXPECT_FALSE(ValuesListContainAccount(result, kSecondaryAccount1Email));
   EXPECT_TRUE(ValuesListContainAccount(result, kSecondaryAccount2Email));
   EXPECT_TRUE(ValuesListContainAccount(result, kSecondaryAccount3Email));
@@ -549,7 +549,7 @@
   const base::span<const base::Value> result =
       CallGetAccountsNotAvailableInArc();
   // One account is not available in ARC.
-  EXPECT_EQ(1, result.size());
+  EXPECT_EQ(1u, result.size());
   EXPECT_FALSE(ValuesListContainAccount(result, kSecondaryAccount1Email));
   EXPECT_TRUE(ValuesListContainAccount(result, kSecondaryAccount2Email));
 
@@ -563,7 +563,7 @@
   const base::span<const base::Value> result_1 =
       CallGetAccountsNotAvailableInArc();
   // Zero accounts are not available in ARC.
-  EXPECT_EQ(0, result_1.size());
+  EXPECT_EQ(0u, result_1.size());
 }
 
 INSTANTIATE_TEST_SUITE_P(InlineLoginHandlerChromeOSTestWithArcRestrictionsSuite,
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index 4e174d9..2474cf2e 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-main-1645703570-e3dc41247690b3148bd56ab144dd57aafed2b321.profdata
+chrome-linux-main-1645725546-b53c30b8c73e1f07b0010ded6d07c7d4d337efe3.profdata
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt
index f1b3383..71ac0011 100644
--- a/chrome/build/mac-arm.pgo.txt
+++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@
-chrome-mac-arm-main-1645703570-27913a62c17a28867c67b3178f2a67269ca1b0df.profdata
+chrome-mac-arm-main-1645725546-ae525cbe5c7e91c6041580b18b86ec2b9bf73f54.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index 969c46a..5d945a3 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-main-1645703570-380be36c93057300ad10506bfbfc83bb46593fab.profdata
+chrome-mac-main-1645725546-ea12a24d5e25cecb1388e4c7f3e4d0b777134634.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index 8774fc2..13304b3 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1645693125-aca2caf32591b84cf45e94f7ace9b8f31b3153c8.profdata
+chrome-win32-main-1645725546-03a3f2a67e880246435a3a270215ca8761470300.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index 79fda2c..40c2ced 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1645714652-db8452d05eb6c96efdd9cb74c0b8f0fa9dac68e4.profdata
+chrome-win64-main-1645725546-41e700cf464c36c3bdc664410054cc53cd6bbb0a.profdata
diff --git a/chrome/common/chrome_features.cc b/chrome/common/chrome_features.cc
index ddc4192..cca33c8 100644
--- a/chrome/common/chrome_features.cc
+++ b/chrome/common/chrome_features.cc
@@ -919,13 +919,6 @@
     "SharesheetCopyToClipboard", base::FEATURE_ENABLED_BY_DEFAULT};
 #endif
 
-#if BUILDFLAG(IS_ANDROID)
-const base::Feature kShareUsageRanking{"ShareUsageRanking",
-                                       base::FEATURE_ENABLED_BY_DEFAULT};
-const base::Feature kShareUsageRankingFixedMore{
-    "ShareUsageRankingFixedMore", base::FEATURE_ENABLED_BY_DEFAULT};
-#endif
-
 #if BUILDFLAG(IS_MAC)
 // Enables the "this OS is obsolete" infobar on Mac 10.10.
 // TODO(ellyjones): Remove this after the last 10.10 release.
diff --git a/chrome/common/chrome_features.h b/chrome/common/chrome_features.h
index 9c376223..1112cf3 100644
--- a/chrome/common/chrome_features.h
+++ b/chrome/common/chrome_features.h
@@ -620,13 +620,6 @@
 extern const base::Feature kSharesheetCopyToClipboard;
 #endif
 
-#if BUILDFLAG(IS_ANDROID)
-COMPONENT_EXPORT(CHROME_FEATURES)
-extern const base::Feature kShareUsageRanking;
-COMPONENT_EXPORT(CHROME_FEATURES)
-extern const base::Feature kShareUsageRankingFixedMore;
-#endif
-
 #if BUILDFLAG(IS_MAC)
 COMPONENT_EXPORT(CHROME_FEATURES)
 extern const base::Feature kShow10_10ObsoleteInfobar;
diff --git a/chrome/common/chromeos/extensions/chromeos_system_extension_info_unittest.cc b/chrome/common/chromeos/extensions/chromeos_system_extension_info_unittest.cc
index 05f824f..9f33ffc 100644
--- a/chrome/common/chromeos/extensions/chromeos_system_extension_info_unittest.cc
+++ b/chrome/common/chromeos/extensions/chromeos_system_extension_info_unittest.cc
@@ -8,7 +8,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 
 TEST(ChromeOSSystemExtensionInfo, AllowlistedExtensionsSizeEqualsToOne) {
-  ASSERT_EQ(2, chromeos::GetChromeOSSystemExtensionInfosSize());
+  ASSERT_EQ(2u, chromeos::GetChromeOSSystemExtensionInfosSize());
 }
 
 TEST(ChromeOSSystemExtensionInfo, GoogleExtension) {
diff --git a/chrome/common/net/x509_certificate_model.cc b/chrome/common/net/x509_certificate_model.cc
index 30fdb8e..86664e7 100644
--- a/chrome/common/net/x509_certificate_model.cc
+++ b/chrome/common/net/x509_certificate_model.cc
@@ -4,15 +4,121 @@
 
 #include "chrome/common/net/x509_certificate_model.h"
 
+#include "base/hash/sha1.h"
 #include "base/i18n/number_formatting.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/grit/generated_resources.h"
 #include "components/url_formatter/url_formatter.h"
+#include "crypto/sha2.h"
+#include "net/cert/internal/cert_errors.h"
+#include "net/cert/x509_util.h"
+#include "net/der/input.h"
 #include "ui/base/l10n/l10n_util.h"
 
 namespace x509_certificate_model {
 
+X509CertificateModel::X509CertificateModel(
+    bssl::UniquePtr<CRYPTO_BUFFER> cert_data,
+    std::string nickname)
+    : nickname_(std::move(nickname)), cert_data_(std::move(cert_data)) {
+  DCHECK(cert_data_);
+
+  net::ParseCertificateOptions options;
+  options.allow_invalid_serial_numbers = true;
+  net::CertErrors unused_errors;
+  if (!net::ParseCertificate(
+          net::der::Input(CRYPTO_BUFFER_data(cert_data_.get()),
+                          CRYPTO_BUFFER_len(cert_data_.get())),
+          &tbs_certificate_tlv_, &signature_algorithm_tlv_, &signature_value_,
+          &unused_errors) ||
+      !ParseTbsCertificate(tbs_certificate_tlv_, options, &tbs_,
+                           &unused_errors) ||
+      !net::ParseName(tbs_.subject_tlv, &subject_rdns_) ||
+      !net::ParseName(tbs_.issuer_tlv, &issuer_rdns_)) {
+    return;
+  }
+  if (tbs_.has_extensions && !ParseExtensions(tbs_.extensions_tlv)) {
+    return;
+  }
+  parsed_successfully_ = true;
+}
+
+X509CertificateModel::~X509CertificateModel() = default;
+
+std::string X509CertificateModel::HashCertSHA256() const {
+  auto hash =
+      crypto::SHA256Hash(net::x509_util::CryptoBufferAsSpan(cert_data_.get()));
+  return ProcessRawBytes(hash.data(), hash.size());
+}
+
+std::string X509CertificateModel::HashCertSHA1() const {
+  auto hash =
+      base::SHA1HashSpan(net::x509_util::CryptoBufferAsSpan(cert_data_.get()));
+  return ProcessRawBytes(hash.data(), hash.size());
+}
+
+std::string X509CertificateModel::GetVersion() const {
+  DCHECK(parsed_successfully_);
+  switch (tbs_.version) {
+    case net::CertificateVersion::V1:
+      return "1";
+    case net::CertificateVersion::V2:
+      return "2";
+    case net::CertificateVersion::V3:
+      return "3";
+  }
+}
+
+std::string X509CertificateModel::GetSerialNumberHexified() const {
+  DCHECK(parsed_successfully_);
+  return ProcessRawBytesWithSeparators(tbs_.serial_number.UnsafeData(),
+                                       tbs_.serial_number.Length(), ':', ':');
+}
+
+bool X509CertificateModel::ParseExtensions(
+    const net::der::Input& extensions_tlv) {
+  net::CertErrors unused_errors;
+  net::der::Parser parser(extensions_tlv);
+
+  //    Extensions  ::=  SEQUENCE SIZE (1..MAX) OF Extension
+  net::der::Parser extensions_parser;
+  if (!parser.ReadSequence(&extensions_parser))
+    return false;
+
+  // The Extensions SEQUENCE must contains at least 1 element (otherwise it
+  // should have been omitted).
+  if (!extensions_parser.HasMore())
+    return false;
+
+  while (extensions_parser.HasMore()) {
+    net::ParsedExtension extension;
+
+    net::der::Input extension_tlv;
+    if (!extensions_parser.ReadRawTLV(&extension_tlv))
+      return false;
+
+    if (!ParseExtension(extension_tlv, &extension))
+      return false;
+
+    extensions_.push_back(extension);
+
+    if (extension.oid == net::der::Input(net::kSubjectAltNameOid)) {
+      subject_alt_names_ =
+          net::GeneralNames::Create(extension.value, &unused_errors);
+      if (!subject_alt_names_)
+        return false;
+    }
+  }
+
+  // By definition the input was a single Extensions sequence, so there
+  // shouldn't be unconsumed data.
+  if (parser.HasMore())
+    return false;
+
+  return true;
+}
+
 // TODO(https://crbug.com/953425): move to anonymous namespace once
 // x509_certificate_model_nss is removed.
 std::string ProcessIDN(const std::string& input) {
diff --git a/chrome/common/net/x509_certificate_model.h b/chrome/common/net/x509_certificate_model.h
index edcf1e1b..c172fc73 100644
--- a/chrome/common/net/x509_certificate_model.h
+++ b/chrome/common/net/x509_certificate_model.h
@@ -6,11 +6,57 @@
 #define CHROME_COMMON_NET_X509_CERTIFICATE_MODEL_H_
 
 #include <string>
+#include <vector>
+
+#include "net/cert/internal/parse_certificate.h"
+#include "net/cert/internal/parse_name.h"
+#include "third_party/boringssl/src/include/openssl/pool.h"
 
 // This namespace defines a set of functions to be used in UI-related bits of
 // X509 certificates.
 namespace x509_certificate_model {
 
+class X509CertificateModel {
+ public:
+  // Construct an X509CertificateModel from |cert_data|, which must must not be
+  // nullptr.  |nickname| may optionally be provided as a platform-specific
+  // nickname for the certificate, if available.
+  X509CertificateModel(bssl::UniquePtr<CRYPTO_BUFFER> cert_data,
+                       std::string nickname);
+  ~X509CertificateModel();
+
+  // These methods are always safe to call even if |cert_data| could not be
+  // parsed.
+  std::string HashCertSHA256() const;
+  std::string HashCertSHA1() const;
+  CRYPTO_BUFFER* cert_buffer() const { return cert_data_.get(); }
+  bool is_valid() const { return parsed_successfully_; }
+
+  // The rest of the methods should only be called if |is_valid()| returns true.
+  std::string GetVersion() const;
+  std::string GetSerialNumberHexified() const;
+
+ private:
+  bool ParseExtensions(const net::der::Input& extensions_tlv);
+
+  // Externally provided "nickname" for the cert.
+  std::string nickname_;
+
+  bool parsed_successfully_ = false;
+  bssl::UniquePtr<CRYPTO_BUFFER> cert_data_;
+  net::der::Input tbs_certificate_tlv_;
+  net::der::Input signature_algorithm_tlv_;
+  net::der::BitString signature_value_;
+  net::ParsedTbsCertificate tbs_;
+
+  net::RDNSequence subject_rdns_;
+  net::RDNSequence issuer_rdns_;
+  std::vector<net::ParsedExtension> extensions_;
+
+  // Parsed SubjectAltName extension.
+  std::unique_ptr<net::GeneralNames> subject_alt_names_;
+};
+
 // For host values, if they contain IDN Punycode-encoded A-labels, this will
 // return a string suitable for display that contains both the original and the
 // decoded U-label form.  Otherwise, the string will be returned as is.
diff --git a/chrome/common/net/x509_certificate_model_unittest.cc b/chrome/common/net/x509_certificate_model_unittest.cc
new file mode 100644
index 0000000..963cc80
--- /dev/null
+++ b/chrome/common/net/x509_certificate_model_unittest.cc
@@ -0,0 +1,64 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/common/net/x509_certificate_model.h"
+
+#include "net/cert/x509_util.h"
+#include "net/test/cert_test_util.h"
+#include "net/test/test_data_directory.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+class X509CertificateModel : public testing::TestWithParam<std::string> {};
+
+TEST_P(X509CertificateModel, InvalidCert) {
+  x509_certificate_model::X509CertificateModel model(
+      net::x509_util::CreateCryptoBuffer(
+          base::span<const uint8_t>({'b', 'a', 'd', '\n'})),
+      GetParam());
+
+  EXPECT_EQ(
+      "1D 7A 36 3C E1 24 30 88 1E C5 6C 9C F1 40 9C 49\nC4 91 04 36 18 E5 98 "
+      "C3 56 E2 95 90 40 87 2F 5A",
+      model.HashCertSHA256());
+  EXPECT_EQ("E9 B3 96 D2 DD DF FD B3 73 BF 2C 6A D0 73 69 6A\nA2 5B 4F 68",
+            model.HashCertSHA1());
+  EXPECT_FALSE(model.is_valid());
+}
+
+TEST_P(X509CertificateModel, GetGoogleCertFields) {
+  auto cert = net::ImportCertFromFile(net::GetTestCertsDirectory(),
+                                      "google.single.pem");
+  ASSERT_TRUE(cert);
+  x509_certificate_model::X509CertificateModel model(
+      bssl::UpRef(cert->cert_buffer()), GetParam());
+  EXPECT_EQ(
+      "F6 41 C3 6C FE F4 9B C0 71 35 9E CF 88 EE D9 31\n7B 73 8B 59 89 41 6A "
+      "D4 01 72 0C 0A 4E 2E 63 52",
+      model.HashCertSHA256());
+  EXPECT_EQ("40 50 62 E5 BE FD E4 AF 97 E9 38 2A F1 6C C8 7C\n8F B7 C4 E2",
+            model.HashCertSHA1());
+  ASSERT_TRUE(model.is_valid());
+
+  EXPECT_EQ("3", model.GetVersion());
+  EXPECT_EQ("2F:DF:BC:F6:AE:91:52:6D:0F:9A:A3:DF:40:34:3E:9A",
+            model.GetSerialNumberHexified());
+}
+
+TEST_P(X509CertificateModel, GetNDNCertFields) {
+  auto cert =
+      net::ImportCertFromFile(net::GetTestCertsDirectory(), "ndn.ca.crt");
+  ASSERT_TRUE(cert);
+  x509_certificate_model::X509CertificateModel model(
+      bssl::UpRef(cert->cert_buffer()), GetParam());
+  ASSERT_TRUE(model.is_valid());
+  EXPECT_EQ("1", model.GetVersion());
+  // The model just returns the hex of the DER bytes, so the leading zeros are
+  // included.
+  EXPECT_EQ("00:DB:B7:C6:06:47:AF:37:A2", model.GetSerialNumberHexified());
+}
+
+INSTANTIATE_TEST_SUITE_P(All,
+                         X509CertificateModel,
+                         testing::Values(std::string(),
+                                         std::string("nickname")));
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index f578c51a..58f23581 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -5258,6 +5258,7 @@
       "../browser/webauthn/authenticator_request_scheduler_unittest.cc",
       "../browser/webauthn/cablev2_devices_unittest.cc",
       "../browser/webauthn/chrome_authenticator_request_delegate_unittest.cc",
+      "../common/net/x509_certificate_model_unittest.cc",
       "../renderer/cart/commerce_hint_agent_unittest.cc",
     ]
   }
diff --git a/chrome/test/data/policy/policy_test_cases.json b/chrome/test/data/policy/policy_test_cases.json
index 458d044..904a121d 100644
--- a/chrome/test/data/policy/policy_test_cases.json
+++ b/chrome/test/data/policy/policy_test_cases.json
@@ -8272,12 +8272,12 @@
         "prefs": {
           "ash.fast_pair.enabled": {
             "location": "user_profile",
-            "default_value": true
+            "value": false
           }
         }
       },
       {
-        "note": "Check simple policy value",
+        "note": "Set policy to false force disabled",
         "policies": {
           "FastPairEnabled": false
         },
@@ -8287,6 +8287,18 @@
             "value": false
           }
         }
+      },
+      {
+        "note": "Set policy to true falls back to default",
+        "policies": {
+          "FastPairEnabled": true
+        },
+        "prefs": {
+          "ash.fast_pair.enabled": {
+            "location": "user_profile",
+            "default_value": true
+          }
+        }
       }
     ]
   },
@@ -15119,6 +15131,7 @@
     ]
   },
   "CloudReportingUploadFrequency": {
+    "reason_for_missing_test": "TODO(http://crbug.com/1300521): flaky test on windows -> fix and remove this comment, which causes the test to be skipped",
     "os": [
       "win",
       "linux",
diff --git a/chrome/test/data/webui/settings/chromeos/manage_accessibility_page_tests.js b/chrome/test/data/webui/settings/chromeos/manage_accessibility_page_tests.js
index edf5c2c..d2bf2449 100644
--- a/chrome/test/data/webui/settings/chromeos/manage_accessibility_page_tests.js
+++ b/chrome/test/data/webui/settings/chromeos/manage_accessibility_page_tests.js
@@ -276,14 +276,9 @@
   });
 
   test('Dictation labels', async () => {
-    // Setup. Ensure that the Dictation locale menu is shown by doing the
-    // following:
-    // 1. Set the dictation pref to true (done in default prefs).
-    // 2. Ensure dictation locale prefs are allowed.
-    // 3. Populate dictation locale options with mock data.
-    loadTimeData.overrideValues({
-      areDictationLocalePrefsAllowed: true,
-    });
+    // Ensure that the Dictation locale menu is shown by setting the dictation
+    // pref to true (done in default prefs) and populating dictation locale
+    // options with mock data.
     initPage();
     const locales = [{
       name: 'English (United States)',
@@ -294,9 +289,6 @@
     }];
     cr.webUIListenerCallback('dictation-locales-set', locales);
     Polymer.dom.flush();
-    // Sanity checks.
-    assertTrue(loadTimeData.getBoolean('areDictationLocalePrefsAllowed'));
-    assertTrue(page.areDictationLocalePrefsAllowed_);
 
     // Dictation toggle.
     const dictationSetting = page.$$('#enableDictation');
diff --git a/chromecast/BUILD.gn b/chromecast/BUILD.gn
index 71e9675..6ba68bf 100644
--- a/chromecast/BUILD.gn
+++ b/chromecast/BUILD.gn
@@ -31,10 +31,13 @@
 group("all") {
   data_deps = []
   deps = [ "//chromecast/build:archive" ]
-  if (is_android && chromecast_branding == "public") {
-    deps += [ ":cast_shell_apk" ]
-  }
-  if (!is_android) {
+  if (is_android) {
+    if (chromecast_branding == "public") {
+      deps += [ ":cast_shell_apk" ]
+    } else {
+      deps += [ ":cast_browser_bundle_module" ]
+    }
+  } else {
     data_deps += [
       ":cast_browser",
       ":cast_shell",
@@ -653,6 +656,9 @@
 if (is_android) {
   generate_jni_registration("cast_shell_jni_registration") {
     targets = [ ":cast_shell_apk" ]
+    if (chromecast_branding != "public") {
+      targets += [ ":cast_browser_bundle_module" ]
+    }
     header_output = "$root_gen_dir/chromecast/android/${target_name}.h"
   }
 
@@ -712,4 +718,33 @@
     command_line_flags_file = "castshell-command-line"
     enable_multidex = true
   }
+
+  if (chromecast_branding != "public") {
+    android_app_bundle_module("cast_browser_bundle_module") {
+      is_base_module = false
+      base_module_target = "//chromecast/internal/service/main/android:cast_service_bundle_module"
+
+      android_manifest =
+          "$root_gen_dir/cast_browser_manifest/AndroidManifest.xml"
+      android_manifest_dep =
+          "//chromecast/browser/android:cast_browser_manifest"
+
+      min_sdk_version = 23
+      target_sdk_version = 31
+
+      shared_libraries = [ "//chromecast/android:libcast_browser_android" ]
+      product_config_java_packages = [ "org.chromium.chromecast.shell" ]
+      deps = [
+        ":cast_shell_apk_assets",
+        "//base:base_java",
+        "//chromecast/android:libcast_browser_android",
+        "//chromecast/browser/android:cast_browser_java",
+        "//components/crash/core/app:chrome_crashpad_handler_named_as_so",
+      ]
+
+      loadable_modules = [ "$root_out_dir/libchrome_crashpad_handler.so" ]
+
+      enable_multidex = true
+    }
+  }
 }
diff --git a/chromecast/android/BUILD.gn b/chromecast/android/BUILD.gn
index 7da7580..47193def 100644
--- a/chromecast/android/BUILD.gn
+++ b/chromecast/android/BUILD.gn
@@ -10,17 +10,11 @@
 # These targets shall only be referenced on Android builds.
 assert(is_android)
 
-cast_shared_library("libcast_shell_android") {
-  # TODO: Remove the ldflags after migrating away from protobuf_lite to
-  # protobuf_full.
-  ldflags = [ "-Wl,-z,muldefs" ]
-
-  sources = [ "//chromecast/app/android/cast_jni_loader.cc" ]
-
-  deps = [
+# Deps shared by libcast_browser_android and libcast_shell_android.
+group("common_apk_deps") {
+  public_deps = [
     "//base",
     "//chromecast:cast_shell_jni_registration",
-    "//chromecast:cast_shell_lib",
     "//chromecast:chromecast_buildflags",
     "//chromecast/app",
     "//chromecast/app:cast_crash_client",
@@ -36,11 +30,11 @@
     "//content/public/browser",
     "//skia",
   ]
-
+  
   # Explicit dependencies required for JNI registration to be able to find the
   # native side functions.
   if (is_component_build) {
-    deps += [
+    public_deps += [
       "//device/bluetooth",
       "//device/gamepad",
       "//media/midi",
@@ -51,6 +45,32 @@
   }
 }
 
+cast_shared_library("libcast_shell_android") {
+  # TODO: Remove the ldflags after migrating away from protobuf_lite to
+  # protobuf_full.
+  ldflags = [ "-Wl,-z,muldefs" ]
+
+  sources = [ "//chromecast/app/android/cast_jni_loader.cc" ]
+
+  deps = [
+    ":common_apk_deps",
+    "//chromecast:cast_shell_lib",
+  ]
+}
+
+cast_shared_library("libcast_browser_android") {
+  # TODO: Remove the ldflags after migrating away from protobuf_lite to
+  # protobuf_full.
+  ldflags = [ "-Wl,-z,muldefs" ]
+
+  sources = [ "//chromecast/app/android/cast_jni_loader.cc" ]
+
+  deps = [
+    ":common_apk_deps",
+    "//chromecast:cast_shell_lib_simple",
+  ]
+}
+
 # These are all known //third_party/android_deps targets that chromecast
 # internal code depends on. Reference these targets so if anyone wants to
 # remove these targets upstream, they need to at least add someone from
diff --git a/chromecast/browser/android/BUILD.gn b/chromecast/browser/android/BUILD.gn
index dcbc5d5d..e80ca4d 100644
--- a/chromecast/browser/android/BUILD.gn
+++ b/chromecast/browser/android/BUILD.gn
@@ -16,6 +16,14 @@
   output = cast_shell_android_manifest
 }
 
+cast_browser_android_manifest =
+    "$root_gen_dir/cast_browser_manifest/AndroidManifest.xml"
+
+jinja_template("cast_browser_manifest") {
+  input = "apk/CastBrowserAndroidManifest.xml.jinja2"
+  output = cast_browser_android_manifest
+}
+
 java_cpp_template("cast_shell_build_config_gen") {
   sources = [ "apk/templates/BuildConfig.template" ]
 
@@ -39,22 +47,30 @@
   "apk/res-values/values/strings.xml",
 ]
 
+extra_resources = [
+  "apk/res/drawable-hdpi/ic_notification_cast.png",
+  "apk/res/drawable-hdpi/ic_settings_cast.png",
+  "apk/res/drawable-mdpi/ic_notification_cast.png",
+  "apk/res/drawable-mdpi/ic_settings_cast.png",
+  "apk/res/drawable-xhdpi/ic_notification_cast.png",
+  "apk/res/drawable-xhdpi/ic_settings_cast.png",
+  "apk/res/drawable-xxhdpi/ic_notification_cast.png",
+  "apk/res/drawable-xxhdpi/ic_settings_cast.png",
+  "apk/res/drawable-xxxhdpi/ic_notification_cast.png",
+  "apk/res/drawable-xxxhdpi/ic_settings_cast.png",
+  "apk/res/layout/cast_web_contents_activity.xml",
+]
+
 android_resources("cast_shell_android_resources") {
   android_manifest = cast_shell_android_manifest
   android_manifest_dep = ":cast_shell_manifest"
-  sources = common_resources + [
-              "apk/res/drawable-hdpi/ic_settings_cast.png",
-              "apk/res/drawable-mdpi/ic_settings_cast.png",
-              "apk/res/drawable-xhdpi/ic_settings_cast.png",
-              "apk/res/drawable-xxhdpi/ic_settings_cast.png",
-              "apk/res/drawable-xxxhdpi/ic_settings_cast.png",
-              "apk/res/drawable-hdpi/ic_notification_cast.png",
-              "apk/res/drawable-mdpi/ic_notification_cast.png",
-              "apk/res/drawable-xhdpi/ic_notification_cast.png",
-              "apk/res/drawable-xxhdpi/ic_notification_cast.png",
-              "apk/res/drawable-xxxhdpi/ic_notification_cast.png",
-              "apk/res/layout/cast_web_contents_activity.xml",
-            ]
+  sources = common_resources + extra_resources
+}
+
+android_resources("cast_browser_android_resources") {
+  android_manifest = cast_browser_android_manifest
+  android_manifest_dep = ":cast_browser_manifest"
+  sources = common_resources + extra_resources
 }
 
 android_resources("cast_shell_android_stub_resources") {
@@ -110,31 +126,59 @@
   java_package = "org.chromium.chromecast.shell"
 }
 
+common_android_library_deps = [
+  ":cast_audio_manager_java",
+  ":cast_intents_java",
+  ":reactive_android_java",
+  "//base:base_java",
+  "//chromecast/base:base_java",
+  "//chromecast/media/cma/backend/android:audio_track_java",
+  "//components/crash/android:java",
+  "//components/embedder_support/android:content_view_java",
+  "//components/embedder_support/android:view_java",
+  "//components/minidump_uploader:minidump_uploader_java",
+  "//content/public/android:content_java",
+  "//media/base/android:media_java",
+  "//net/android:net_java",
+  "//third_party/androidx:androidx_annotation_annotation_java",
+  "//third_party/androidx:androidx_core_core_java",
+
+  # TODO(slan): We may need to pass this in as a parameter.
+  "//third_party/androidx:androidx_localbroadcastmanager_localbroadcastmanager_java",
+  "//third_party/androidx:androidx_slice_slice_builders_java",
+
+  # Used internally for Settings UI.
+  "//third_party/androidx:androidx_slice_slice_core_java",
+  "//ui/android:ui_java",
+]
+
+common_java_src_dir = "//chromecast/browser/android/apk/src"
+common_java_sources = [
+  "$common_java_src_dir/org/chromium/chromecast/shell/AndroidAppLogcatProvider.java",
+  "$common_java_src_dir/org/chromium/chromecast/shell/CastAccessibilityHelper.java",
+  "$common_java_src_dir/org/chromium/chromecast/shell/CastApplication.java",
+  "$common_java_src_dir/org/chromium/chromecast/shell/CastBrowserHelper.java",
+  "$common_java_src_dir/org/chromium/chromecast/shell/CastCommandLineHelper.java",
+  "$common_java_src_dir/org/chromium/chromecast/shell/CastContentWindowAndroid.java",
+  "$common_java_src_dir/org/chromium/chromecast/shell/CastCrashHandler.java",
+  "$common_java_src_dir/org/chromium/chromecast/shell/CastCrashUploader.java",
+  "$common_java_src_dir/org/chromium/chromecast/shell/CastCrashUploaderFactory.java",
+  "$common_java_src_dir/org/chromium/chromecast/shell/CastMetricsHelper.java",
+  "$common_java_src_dir/org/chromium/chromecast/shell/CastWebContentsActivity.java",
+  "$common_java_src_dir/org/chromium/chromecast/shell/CastWebContentsComponent.java",
+  "$common_java_src_dir/org/chromium/chromecast/shell/CastWebContentsIntentUtils.java",
+  "$common_java_src_dir/org/chromium/chromecast/shell/CastWebContentsScopes.java",
+  "$common_java_src_dir/org/chromium/chromecast/shell/CastWebContentsService.java",
+  "$common_java_src_dir/org/chromium/chromecast/shell/CastWebContentsSurfaceHelper.java",
+  "$common_java_src_dir/org/chromium/chromecast/shell/CastWebContentsView.java",
+  "$common_java_src_dir/org/chromium/chromecast/shell/ElidedLogcatProvider.java",
+  "$common_java_src_dir/org/chromium/chromecast/shell/ExternalServiceDeviceLogcatProvider.java",
+  "$common_java_src_dir/org/chromium/chromecast/shell/LogcatElision.java",
+  "$common_java_src_dir/org/chromium/chromecast/shell/WebContentsRegistry.java",
+]
+
 android_library("cast_shell_java") {
-  java_src_dir = "//chromecast/browser/android/apk/src"
-  sources = [
-    "$java_src_dir/org/chromium/chromecast/shell/AndroidAppLogcatProvider.java",
-    "$java_src_dir/org/chromium/chromecast/shell/CastAccessibilityHelper.java",
-    "$java_src_dir/org/chromium/chromecast/shell/CastApplication.java",
-    "$java_src_dir/org/chromium/chromecast/shell/CastBrowserHelper.java",
-    "$java_src_dir/org/chromium/chromecast/shell/CastCommandLineHelper.java",
-    "$java_src_dir/org/chromium/chromecast/shell/CastContentWindowAndroid.java",
-    "$java_src_dir/org/chromium/chromecast/shell/CastCrashHandler.java",
-    "$java_src_dir/org/chromium/chromecast/shell/CastCrashUploader.java",
-    "$java_src_dir/org/chromium/chromecast/shell/CastCrashUploaderFactory.java",
-    "$java_src_dir/org/chromium/chromecast/shell/CastMetricsHelper.java",
-    "$java_src_dir/org/chromium/chromecast/shell/CastWebContentsActivity.java",
-    "$java_src_dir/org/chromium/chromecast/shell/CastWebContentsComponent.java",
-    "$java_src_dir/org/chromium/chromecast/shell/CastWebContentsIntentUtils.java",
-    "$java_src_dir/org/chromium/chromecast/shell/CastWebContentsScopes.java",
-    "$java_src_dir/org/chromium/chromecast/shell/CastWebContentsService.java",
-    "$java_src_dir/org/chromium/chromecast/shell/CastWebContentsSurfaceHelper.java",
-    "$java_src_dir/org/chromium/chromecast/shell/CastWebContentsView.java",
-    "$java_src_dir/org/chromium/chromecast/shell/ElidedLogcatProvider.java",
-    "$java_src_dir/org/chromium/chromecast/shell/ExternalServiceDeviceLogcatProvider.java",
-    "$java_src_dir/org/chromium/chromecast/shell/LogcatElision.java",
-    "$java_src_dir/org/chromium/chromecast/shell/WebContentsRegistry.java",
-  ]
+  sources = common_java_sources
 
   resources_package = "org.chromium.chromecast.shell"
   srcjar_deps = [
@@ -146,33 +190,30 @@
 
   jar_excluded_patterns = [ "*/ProductConfig.class" ]
 
-  deps = [
-    ":cast_audio_manager_java",
-    ":cast_intents_java",
-    ":cast_shell_android_resources",
-    ":cast_shell_manifest",
-    ":reactive_android_java",
-    "//base:base_java",
-    "//chromecast/base:base_java",
-    "//chromecast/media/cma/backend/android:audio_track_java",
-    "//components/crash/android:java",
-    "//components/embedder_support/android:content_view_java",
-    "//components/embedder_support/android:view_java",
-    "//components/minidump_uploader:minidump_uploader_java",
-    "//content/public/android:content_java",
-    "//media/base/android:media_java",
-    "//net/android:net_java",
-    "//third_party/androidx:androidx_annotation_annotation_java",
-    "//third_party/androidx:androidx_core_core_java",
+  deps = common_android_library_deps + [
+           ":cast_shell_android_resources",
+           ":cast_shell_manifest",
+         ]
+  annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
+}
 
-    # TODO(slan): We may need to pass this in as a parameter.
-    "//third_party/androidx:androidx_localbroadcastmanager_localbroadcastmanager_java",
-    "//third_party/androidx:androidx_slice_slice_builders_java",
+android_library("cast_browser_java") {
+  sources = common_java_sources
 
-    # Used internally for Settings UI.
-    "//third_party/androidx:androidx_slice_slice_core_java",
-    "//ui/android:ui_java",
+  resources_package = "org.chromium.chromecast.shell"
+  srcjar_deps = [
+    ":cast_shell_build_config_gen",
+    ":chromecast_product_config",
+    ":logs_provider_aidl",
+    "//chromecast/browser:java_enums",
   ]
+
+  jar_excluded_patterns = [ "*/ProductConfig.class" ]
+
+  deps = common_android_library_deps + [
+           ":cast_browser_android_resources",
+           ":cast_browser_manifest",
+         ]
   annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
 }
 
diff --git a/chromecast/browser/android/apk/CastBrowserAndroidManifest.xml.jinja2 b/chromecast/browser/android/apk/CastBrowserAndroidManifest.xml.jinja2
new file mode 100644
index 0000000..607d794
--- /dev/null
+++ b/chromecast/browser/android/apk/CastBrowserAndroidManifest.xml.jinja2
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!-- Copyright 2022 The Chromium Authors. All rights reserved.
+     Use of this source code is governed by a BSD-style license that can be
+     found in the LICENSE file.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          xmlns:dist="http://schemas.android.com/apk/distribution"
+          package="com.google.android.apps.mediashell"
+          featureSplit="cast_browser" >
+    <dist:module
+        dist:onDemand="false"
+        dist:title="cast_browser">
+        <!-- This will fuse the module into the base APK if a system image
+             APK is built from this bundle. -->
+        <dist:fusing dist:include="true" />
+    </dist:module>
+
+    <uses-sdk android:minSdkVersion="23"/>
+
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
+    <uses-permission android:name="android.permission.INTERNET"/>
+    <uses-permission android:name="android.permission.WAKE_LOCK"/>
+
+    <application android:icon="@drawable/ic_settings_cast">
+        <activity android:name="org.chromium.chromecast.shell.CastWebContentsActivity"
+                  android:theme="@style/CastShellTheme"
+                  android:exported="true"
+                  android:hardwareAccelerated="true"
+                  android:launchMode="standard"
+                  android:screenOrientation="landscape"
+                  android:taskAffinity=".CastWebContentsActivity"
+                  android:supportsPictureInPicture="true"
+                  android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection"
+                  android:excludeFromRecents="false"
+                  android:noHistory="false">
+        </activity>
+
+        <!-- The following service entries exist in order to allow us to
+             start more than one sandboxed process. -->
+        {% set num_sandboxed_services = 40 %}
+        <meta-data android:name="org.chromium.content.browser.NUM_SANDBOXED_SERVICES"
+                   android:value="{{ num_sandboxed_services }}"/>
+
+        {% for i in range(num_sandboxed_services) %}
+        <service android:name="org.chromium.content.app.SandboxedProcessService{{ i }}"
+                 android:process=":sandboxed_process{{ i }}"
+                 android:isolatedProcess="true"
+                 android:exported="false" />
+        {% endfor %}
+
+        {% set num_privileged_services = 5 %}
+        <meta-data android:name="org.chromium.content.browser.NUM_PRIVILEGED_SERVICES"
+                   android:value="{{ num_privileged_services }}"/>
+
+        {% for i in range(num_privileged_services) %}
+        <service android:name="org.chromium.content.app.PrivilegedProcessService{{ i }}"
+                 android:process=":privileged_process{{ i }}"
+                 android:isolatedProcess="false"
+                 android:exported="false" />
+        {% endfor %}
+    </application>
+
+</manifest>
diff --git a/chromeos/dbus/dlcservice/dlcservice_client.cc b/chromeos/dbus/dlcservice/dlcservice_client.cc
index d3756a5..52699c9 100644
--- a/chromeos/dbus/dlcservice/dlcservice_client.cc
+++ b/chromeos/dbus/dlcservice/dlcservice_client.cc
@@ -110,30 +110,31 @@
 
   ~DlcserviceClientImpl() override = default;
 
-  void Install(const std::string& dlc_id,
+  void Install(const dlcservice::InstallRequest& install_request,
                InstallCallback install_callback,
                ProgressCallback progress_callback) override {
     CheckServiceAvailable("Install");
+    const std::string& id = install_request.id();
     // If another installation for the same DLC ID was already called, go ahead
     // and hold the installation fields.
-    if (installation_holder_.find(dlc_id) != installation_holder_.end()) {
-      HoldInstallation(dlc_id, std::move(install_callback),
+    if (installation_holder_.find(id) != installation_holder_.end()) {
+      HoldInstallation(install_request, std::move(install_callback),
                        std::move(progress_callback));
       return;
     }
     if (installing_) {
-      EnqueueTask(base::BindOnce(&DlcserviceClientImpl::Install,
-                                 weak_ptr_factory_.GetWeakPtr(),
-                                 std::move(dlc_id), std::move(install_callback),
-                                 std::move(progress_callback)));
+      EnqueueTask(base::BindOnce(
+          &DlcserviceClientImpl::Install, weak_ptr_factory_.GetWeakPtr(),
+          std::move(install_request), std::move(install_callback),
+          std::move(progress_callback)));
       return;
     }
 
     TaskStarted();
     dbus::MethodCall method_call(dlcservice::kDlcServiceInterface,
-                                 dlcservice::kInstallDlcMethod);
+                                 dlcservice::kInstallMethod);
     dbus::MessageWriter writer(&method_call);
-    writer.AppendString(dlc_id);
+    writer.AppendProtoAsArrayOfBytes(install_request);
 
     VLOG(1) << "Requesting to install DLC(s).";
     // TODO(b/166782419): dlcservice hashes preloadable DLC images which can
@@ -142,9 +143,10 @@
     constexpr int timeout_ms = 5 * 60 * 1000;
     dlcservice_proxy_->CallMethodWithErrorResponse(
         &method_call, timeout_ms,
-        base::BindOnce(
-            &DlcserviceClientImpl::OnInstall, weak_ptr_factory_.GetWeakPtr(),
-            dlc_id, std::move(install_callback), std::move(progress_callback)));
+        base::BindOnce(&DlcserviceClientImpl::OnInstall,
+                       weak_ptr_factory_.GetWeakPtr(), install_request,
+                       std::move(install_callback),
+                       std::move(progress_callback)));
   }
 
   void Uninstall(const std::string& dlc_id,
@@ -235,12 +237,12 @@
   // Fields related to an installation allowing for multiple installations to be
   // in flight concurrently and handled by this dlcservice client. The callbacks
   // are used to report progress and the final installation.
-  struct InstallationCallbacks {
+  struct InstallationHolder {
     InstallCallback install_callback;
     ProgressCallback progress_callback;
 
-    InstallationCallbacks(InstallCallback install_callback,
-                          ProgressCallback progress_callback)
+    InstallationHolder(InstallCallback install_callback,
+                       ProgressCallback progress_callback)
         : install_callback(std::move(install_callback)),
           progress_callback(std::move(progress_callback)) {}
   };
@@ -260,11 +262,11 @@
   // Clears any state an installation had setup while being performed.
   void TaskEnded() { installing_ = false; }
 
-  void HoldInstallation(const std::string& id,
+  void HoldInstallation(const dlcservice::InstallRequest& install_request,
                         InstallCallback install_callback,
                         ProgressCallback progress_callback) {
-    installation_holder_[id].emplace_back(std::move(install_callback),
-                                          std::move(progress_callback));
+    installation_holder_[install_request.id()].emplace_back(
+        std::move(install_callback), std::move(progress_callback));
   }
 
   void ReleaseInstallation(const std::string& id) {
@@ -357,27 +359,29 @@
     LOG_IF(ERROR, !success) << "Failed to connect to DlcStateChanged signal.";
   }
 
-  void OnInstall(const std::string& dlc_id,
+  void OnInstall(const dlcservice::InstallRequest& install_request,
                  InstallCallback install_callback,
                  ProgressCallback progress_callback,
                  dbus::Response* response,
                  dbus::ErrorResponse* err_response) {
+    const std::string& id = install_request.id();
     if (response) {
-      HoldInstallation(dlc_id, std::move(install_callback),
+      HoldInstallation(install_request, std::move(install_callback),
                        std::move(progress_callback));
       return;
     }
 
     const auto err = DlcserviceErrorResponseHandler(err_response).get_err();
     if (err == dlcservice::kErrorBusy) {
-      EnqueueTask(base::BindOnce(
-          &DlcserviceClientImpl::Install, weak_ptr_factory_.GetWeakPtr(),
-          dlc_id, std::move(install_callback), std::move(progress_callback)));
+      EnqueueTask(base::BindOnce(&DlcserviceClientImpl::Install,
+                                 weak_ptr_factory_.GetWeakPtr(),
+                                 install_request, std::move(install_callback),
+                                 std::move(progress_callback)));
     } else {
-      HoldInstallation(dlc_id, std::move(install_callback),
+      HoldInstallation(install_request, std::move(install_callback),
                        std::move(progress_callback));
       dlcservice::DlcState dlc_state;
-      dlc_state.set_id(dlc_id);
+      dlc_state.set_id(id);
       dlc_state.set_last_error_code(err);
       SendCompleted(dlc_state);
     }
@@ -436,9 +440,8 @@
                    << " called when dlcservice is not available.";
   }
 
-  // DLC ID to |InstallationCallbacks| mapping.
-  std::map<std::string, std::vector<InstallationCallbacks>>
-      installation_holder_;
+  // DLC ID to `InstallationHolder` mapping.
+  std::map<std::string, std::vector<InstallationHolder>> installation_holder_;
 
   dbus::ObjectProxy* dlcservice_proxy_;
 
diff --git a/chromeos/dbus/dlcservice/dlcservice_client.h b/chromeos/dbus/dlcservice/dlcservice_client.h
index ed1562a..4be5671 100644
--- a/chromeos/dbus/dlcservice/dlcservice_client.h
+++ b/chromeos/dbus/dlcservice/dlcservice_client.h
@@ -88,7 +88,7 @@
 
   // Installs the DLC passed in while reporting progress through the progress
   // callback and only calls install callback on install success/failure.
-  virtual void Install(const std::string& dlc_id,
+  virtual void Install(const dlcservice::InstallRequest& install_request,
                        InstallCallback callback,
                        ProgressCallback progress_callback) = 0;
 
diff --git a/chromeos/dbus/dlcservice/dlcservice_client_unittest.cc b/chromeos/dbus/dlcservice/dlcservice_client_unittest.cc
index 883bf83..c934428 100644
--- a/chromeos/dbus/dlcservice/dlcservice_client_unittest.cc
+++ b/chromeos/dbus/dlcservice/dlcservice_client_unittest.cc
@@ -95,6 +95,17 @@
   }
 
  protected:
+  dlcservice::InstallRequest CreateInstallRequest(
+      const std::string& id = {},
+      const std::string& omaha_url = {},
+      bool reserve = false) {
+    dlcservice::InstallRequest install_request;
+    install_request.set_id(id);
+    install_request.set_omaha_url(omaha_url);
+    install_request.set_reserve(reserve);
+    return install_request;
+  }
+
   base::test::SingleThreadTaskEnvironment task_environment_;
   DlcserviceClient* client_;
   scoped_refptr<dbus::MockBus> mock_bus_;
@@ -332,7 +343,8 @@
       base::BindOnce([](const DlcserviceClient::InstallResult& install_result) {
         EXPECT_EQ(dlcservice::kErrorNone, install_result.error);
       });
-  client_->Install("foo-dlc", std::move(install_callback), base::DoNothing());
+  client_->Install(CreateInstallRequest("foo-dlc"), std::move(install_callback),
+                   base::DoNothing());
   base::RunLoop().RunUntilIdle();
 }
 
@@ -351,7 +363,8 @@
       base::BindOnce([](const DlcserviceClient::InstallResult& install_result) {
         EXPECT_EQ(dlcservice::kErrorInternal, install_result.error);
       });
-  client_->Install("foo-dlc", std::move(install_callback), base::DoNothing());
+  client_->Install(CreateInstallRequest("foo-dlc"), std::move(install_callback),
+                   base::DoNothing());
   base::RunLoop().RunUntilIdle();
 }
 
@@ -366,7 +379,7 @@
       [](decltype(counter)* counter, double) { ++*counter; }, &counter);
 
   responses_.push_back(dbus::Response::CreateEmpty());
-  client_->Install({}, std::move(install_callback),
+  client_->Install(CreateInstallRequest(), std::move(install_callback),
                    std::move(progress_callback));
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(0u, counter.load());
@@ -389,7 +402,7 @@
   DlcserviceClient::ProgressCallback progress_callback = base::BindRepeating(
       [](decltype(counter)* counter, double) { ++*counter; }, &counter);
 
-  client_->Install({"foo"}, std::move(install_callback),
+  client_->Install(CreateInstallRequest("foo"), std::move(install_callback),
                    std::move(progress_callback));
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(0u, counter.load());
@@ -421,7 +434,8 @@
       base::BindOnce([](const DlcserviceClient::InstallResult& install_result) {
         EXPECT_EQ(dlcservice::kErrorNone, install_result.error);
       });
-  client_->Install("foo-dlc", std::move(install_callback), base::DoNothing());
+  client_->Install(CreateInstallRequest("foo-dlc"), std::move(install_callback),
+                   base::DoNothing());
   base::RunLoop().RunUntilIdle();
 }
 
@@ -442,7 +456,8 @@
         },
         &counter);
     responses_.push_back(dbus::Response::CreateEmpty());
-    client_->Install({}, std::move(install_callback), base::DoNothing());
+    client_->Install(CreateInstallRequest(), std::move(install_callback),
+                     base::DoNothing());
   }
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(0u, counter.load());
diff --git a/chromeos/dbus/dlcservice/fake_dlcservice_client.cc b/chromeos/dbus/dlcservice/fake_dlcservice_client.cc
index a7f888e..f372ddd 100644
--- a/chromeos/dbus/dlcservice/fake_dlcservice_client.cc
+++ b/chromeos/dbus/dlcservice/fake_dlcservice_client.cc
@@ -14,16 +14,18 @@
 
 FakeDlcserviceClient::~FakeDlcserviceClient() = default;
 
-void FakeDlcserviceClient::Install(const std::string& dlc_id,
-                                   InstallCallback callback,
-                                   ProgressCallback progress_callback) {
+void FakeDlcserviceClient::Install(
+    const dlcservice::InstallRequest& install_request,
+    InstallCallback callback,
+    ProgressCallback progress_callback) {
   VLOG(1) << "Requesting to install DLC(s).";
+  const std::string& id = install_request.id();
   InstallResult install_result{
       .error = install_err_,
-      .dlc_id = dlc_id,
+      .dlc_id = id,
       .root_path = install_root_path_,
   };
-  dlcs_with_content_.add_dlc_infos()->set_id(dlc_id);
+  dlcs_with_content_.add_dlc_infos()->set_id(id);
   base::ThreadTaskRunnerHandle::Get()->PostTask(
       FROM_HERE,
       base::BindOnce(std::move(callback), std::move(install_result)));
diff --git a/chromeos/dbus/dlcservice/fake_dlcservice_client.h b/chromeos/dbus/dlcservice/fake_dlcservice_client.h
index 336418c..c8deab4 100644
--- a/chromeos/dbus/dlcservice/fake_dlcservice_client.h
+++ b/chromeos/dbus/dlcservice/fake_dlcservice_client.h
@@ -22,7 +22,7 @@
   ~FakeDlcserviceClient() override;
 
   // DlcserviceClient:
-  void Install(const std::string& dlc_id,
+  void Install(const dlcservice::InstallRequest& install_request,
                InstallCallback callback,
                ProgressCallback progress_callback) override;
   // Uninstalling disables the DLC.
diff --git a/chromeos/language/language_packs/language_pack_manager.cc b/chromeos/language/language_packs/language_pack_manager.cc
index f8bbdb11..8dbd320e 100644
--- a/chromeos/language/language_packs/language_pack_manager.cc
+++ b/chromeos/language/language_packs/language_pack_manager.cc
@@ -209,8 +209,11 @@
     return;
   }
 
+  dlcservice::InstallRequest install_request;
+  install_request.set_id(dlc_id);
   DlcserviceClient::Get()->Install(
-      dlc_id, base::BindOnce(&OnInstallDlcComplete, std::move(callback)),
+      install_request,
+      base::BindOnce(&OnInstallDlcComplete, std::move(callback)),
       base::DoNothing());
 }
 
diff --git a/chromeos/services/machine_learning/cpp/ash/handwriting_model_loader.cc b/chromeos/services/machine_learning/cpp/ash/handwriting_model_loader.cc
index 7d0e581..b59d2094 100644
--- a/chromeos/services/machine_learning/cpp/ash/handwriting_model_loader.cc
+++ b/chromeos/services/machine_learning/cpp/ash/handwriting_model_loader.cc
@@ -93,8 +93,10 @@
   // the handwriting dlc if it is already on device.
   for (const auto& dlc_info : dlcs_with_content.dlc_infos()) {
     if (dlc_info.id() == kLibHandwritingDlcId) {
+      dlcservice::InstallRequest install_request;
+      install_request.set_id(kLibHandwritingDlcId);
       dlc_client->Install(
-          kLibHandwritingDlcId,
+          install_request,
           base::BindOnce(&OnInstallDlcComplete, std::move(spec),
                          std::move(receiver), std::move(callback)),
           base::DoNothing());
diff --git a/components/autofill/core/browser/payments/virtual_card_enrollment_manager.cc b/components/autofill/core/browser/payments/virtual_card_enrollment_manager.cc
index ec19952..f22c9016 100644
--- a/components/autofill/core/browser/payments/virtual_card_enrollment_manager.cc
+++ b/components/autofill/core/browser/payments/virtual_card_enrollment_manager.cc
@@ -287,11 +287,13 @@
             state_.virtual_card_enrollment_fields.credit_card.card_art_url());
   }
 
+#if !BUILDFLAG(IS_ANDROID)
   if (state_.virtual_card_enrollment_fields.virtual_card_enrollment_source ==
           VirtualCardEnrollmentSource::kUpstream &&
       !avatar_animation_complete_) {
     return;
   }
+#endif
 
   if (autofill_client_) {
     ShowVirtualCardEnrollBubble();
diff --git a/components/autofill/core/browser/payments/virtual_card_enrollment_manager_unittest.cc b/components/autofill/core/browser/payments/virtual_card_enrollment_manager_unittest.cc
index cc20c808..7922fb19 100644
--- a/components/autofill/core/browser/payments/virtual_card_enrollment_manager_unittest.cc
+++ b/components/autofill/core/browser/payments/virtual_card_enrollment_manager_unittest.cc
@@ -427,6 +427,7 @@
       /*succeeded=*/false, 1);
 }
 
+#if !BUILDFLAG(IS_ANDROID)
 TEST_F(VirtualCardEnrollmentManagerTest, UpstreamAnimationSync_AnimationFirst) {
   personal_data_manager_->ClearCreditCardArtImages();
   SetUpCard();
@@ -495,5 +496,6 @@
   virtual_card_enrollment_manager_->OnCardSavedAnimationComplete();
   EXPECT_TRUE(virtual_card_enrollment_manager_->GetAvatarAnimationComplete());
 }
+#endif  // !BUILDFLAG(IS_ANDROID)
 
 }  // namespace autofill
diff --git a/components/autofill/core/common/autofill_payments_features.cc b/components/autofill/core/common/autofill_payments_features.cc
index 2a37a580..462f3cc 100644
--- a/components/autofill/core/common/autofill_payments_features.cc
+++ b/components/autofill/core/common/autofill_payments_features.cc
@@ -64,12 +64,6 @@
     "AutofillEnableMerchantBoundVirtualCards",
     base::FEATURE_ENABLED_BY_DEFAULT};
 
-// Controls whether to track the cross-tab-status of the offer notification
-// bubble.
-const base::Feature kAutofillEnableOfferNotificationCrossTabTracking{
-    "AutofillEnableOfferNotificationCrossTabTracking",
-    base::FEATURE_DISABLED_BY_DEFAULT};
-
 // When enabled, a notification will be displayed on page navigation if the
 // domain has an eligible merchant promo code offer or reward.
 const base::Feature kAutofillEnableOfferNotificationForPromoCodes{
@@ -136,10 +130,6 @@
 const base::Feature kAutofillFillMerchantPromoCodeFields{
     "AutofillFillMerchantPromoCodeFields", base::FEATURE_DISABLED_BY_DEFAULT};
 
-// Controls whether to enable the fix for the offer feature in Incognito mode.
-const base::Feature kAutofillFixOfferInIncognito{
-    "AutofillFixOfferInIncognito", base::FEATURE_DISABLED_BY_DEFAULT};
-
 // The merchant bound virtual card feature introduces new customized card art
 // images. This parameter defines the expiration of the fetched image in the
 // disk cache of the image fetcher.
diff --git a/components/autofill/core/common/autofill_payments_features.h b/components/autofill/core/common/autofill_payments_features.h
index 12b062bc..6de63ea 100644
--- a/components/autofill/core/common/autofill_payments_features.h
+++ b/components/autofill/core/common/autofill_payments_features.h
@@ -23,7 +23,6 @@
 extern const base::Feature kAutofillCreditCardUploadFeedback;
 extern const base::Feature kAutofillEnableGoogleIssuedCard;
 extern const base::Feature kAutofillEnableMerchantBoundVirtualCards;
-extern const base::Feature kAutofillEnableOfferNotificationCrossTabTracking;
 extern const base::Feature kAutofillEnableOfferNotificationForPromoCodes;
 extern const base::Feature kAutofillEnableOffersInClankKeyboardAccessory;
 extern const base::Feature kAutofillEnableSendingBcnInGetUploadDetails;
@@ -36,7 +35,6 @@
     kAutofillEnableVirtualCardManagementInDesktopSettingsPage;
 extern const base::Feature kAutofillEnableVirtualCardsRiskBasedAuthentication;
 extern const base::Feature kAutofillFillMerchantPromoCodeFields;
-extern const base::Feature kAutofillFixOfferInIncognito;
 extern const base::FeatureParam<int>
     kAutofillImageFetcherDiskCacheExpirationInMinutes;
 extern const base::Feature kAutofillParseMerchantPromoCodeFields;
diff --git a/components/continuous_search/OWNERS b/components/continuous_search/OWNERS
index fb7061e..4cf7afdf 100644
--- a/components/continuous_search/OWNERS
+++ b/components/continuous_search/OWNERS
@@ -1,3 +1,2 @@
 ckitagawa@chromium.org
 fredmello@chromium.org
-yashard@chromium.org
diff --git a/components/cronet/PRESUBMIT.py b/components/cronet/PRESUBMIT.py
index 62f1fc47..73be027c 100644
--- a/components/cronet/PRESUBMIT.py
+++ b/components/cronet/PRESUBMIT.py
@@ -15,7 +15,8 @@
 
 def _PyLintChecks(input_api, output_api):
   pylint_checks = input_api.canned_checks.GetPylint(input_api, output_api,
-          extra_paths_list=_GetPathsToPrepend(input_api), pylintrc='pylintrc')
+          extra_paths_list=_GetPathsToPrepend(input_api), pylintrc='pylintrc',
+          version='2.6')
   return input_api.RunTests(pylint_checks)
 
 
@@ -72,8 +73,7 @@
         'API classes must be in org.chromium.net package, and implementation\n'
         'classes must not be in org.chromium.net package.',
         problems)]
-  else:
-    return []
+  return []
 
 
 def _RunToolsUnittests(input_api, output_api):
@@ -81,7 +81,8 @@
       input_api, output_api,
       '.',
       [ r'^tools_unittest\.py$'],
-      run_on_python3=True)
+      run_on_python3=True,
+      skip_shebang_check = True)
 
 
 def _ChangeAffectsCronetTools(change):
diff --git a/components/cronet/android/test/cronet_test_util.cc b/components/cronet/android/test/cronet_test_util.cc
index 86de2536..d9f1e20 100644
--- a/components/cronet/android/test/cronet_test_util.cc
+++ b/components/cronet/android/test/cronet_test_util.cc
@@ -48,7 +48,7 @@
 net::URLRequestContext* TestUtil::GetURLRequestContext(jlong jcontext_adapter) {
   CronetURLRequestContextAdapter* context_adapter =
       reinterpret_cast<CronetURLRequestContextAdapter*>(jcontext_adapter);
-  return context_adapter->context_->network_tasks_->context_.get();
+  return context_adapter->context_->network_tasks_->default_context_;
 }
 
 // static
@@ -56,7 +56,8 @@
                                                   base::OnceClosure task) {
   CronetURLRequestContextAdapter* context_adapter =
       reinterpret_cast<CronetURLRequestContextAdapter*>(jcontext_adapter);
-  if (context_adapter->context_->network_tasks_->is_context_initialized_) {
+  if (context_adapter->context_->network_tasks_
+          ->is_default_context_initialized_) {
     std::move(task).Run();
   } else {
     context_adapter->context_->network_tasks_->tasks_waiting_for_context_.push(
diff --git a/components/cronet/android/test/javaperftests/run.py b/components/cronet/android/test/javaperftests/run.py
index bbbace4..2e5959ea 100755
--- a/components/cronet/android/test/javaperftests/run.py
+++ b/components/cronet/android/test/javaperftests/run.py
@@ -41,7 +41,7 @@
 """
 
 import json
-import optparse
+import argparse
 import os
 import shutil
 import sys
@@ -98,14 +98,14 @@
         data=self.url,
         extras=None,
         category=None)
-    super(CronetPerfTestAndroidStory, self).__init__(
+    super().__init__(
         start_intent, name='CronetPerfTest',
         # No reason to wait for app; Run() will wait for results.  By default
         # StartActivity will timeout waiting for CronetPerfTest, so override
         # |is_app_ready_predicate| to not wait.
         is_app_ready_predicate=lambda app: True)
 
-  def Run(self, shared_user_story_state):
+  def Run(self, shared_state):
     while not self._device.FileExists(
         perf_test_utils.GetConfig(self._device)['DONE_FILE']):
       time.sleep(1.0)
@@ -114,7 +114,7 @@
 class CronetPerfTestStorySet(story_module.StorySet):
 
   def __init__(self, device):
-    super(CronetPerfTestStorySet, self).__init__()
+    super().__init__()
     # Create and add Cronet perf test AndroidStory.
     self.AddStory(CronetPerfTestAndroidStory(device))
 
@@ -126,7 +126,7 @@
   # Cronet perf test app.
 
   def __init__(self, device, options):
-    super(CronetPerfTestMeasurement, self).__init__(options)
+    super().__init__(options)
     self._device = device
 
   def WillRunStory(self, platform, story=None):
@@ -153,7 +153,7 @@
   SUPPORTED_PLATFORMS = [story_module.expectations.ALL_ANDROID]
 
   def __init__(self, max_failures=None):
-    super(CronetPerfTestBenchmark, self).__init__(max_failures)
+    super().__init__(max_failures)
     self._device = GetDevice()
 
   def CreatePageTest(self, options):
@@ -164,13 +164,13 @@
 
 
 def main():
-  parser = optparse.OptionParser()
-  parser.add_option('--output-format', default='html',
+  parser = argparse.ArgumentParser()
+  parser.add_argument('--output-format', default='html',
                    help='The output format of the results file.')
-  parser.add_option('--output-dir', default=None,
+  parser.add_argument('--output-dir', default=None,
                    help='The directory for the output file. Default value is '
                         'the base directory of this script.')
-  options, _ = parser.parse_args()
+  args, _ = parser.parse_known_args()
   constants.SetBuildType(perf_test_utils.BUILD_TYPE)
   # Install APK
   device = GetDevice()
@@ -206,9 +206,9 @@
   sys.argv.insert(1, 'run')
   sys.argv.insert(2, 'run.CronetPerfTestBenchmark')
   sys.argv.insert(3, '--browser=android-system-chrome')
-  sys.argv.insert(4, '--output-format=' + options.output_format)
-  if options.output_dir:
-    sys.argv.insert(5, '--output-dir=' + options.output_dir)
+  sys.argv.insert(4, '--output-format=' + args.output_format)
+  if args.output_dir:
+    sys.argv.insert(5, '--output-dir=' + args.output_dir)
   benchmark_runner.main(runner_config)
   # Shutdown.
   quic_server.ShutdownQuicServer()
diff --git a/components/cronet/cronet_url_request_context.cc b/components/cronet/cronet_url_request_context.cc
index c579e54..06bc8362 100644
--- a/components/cronet/cronet_url_request_context.cc
+++ b/components/cronet/cronet_url_request_context.cc
@@ -53,6 +53,7 @@
 #include "net/log/net_log_util.h"
 #include "net/net_buildflags.h"
 #include "net/nqe/network_quality_estimator_params.h"
+#include "net/proxy_resolution/proxy_config_service_fixed.h"
 #include "net/proxy_resolution/proxy_resolution_service.h"
 #include "net/third_party/quiche/src/quic/core/quic_versions.h"
 #include "net/url_request/url_request_context.h"
@@ -138,6 +139,43 @@
   }
 };
 
+// Helper function to make a net::URLRequestContext aware of a QUIC hint.
+void SetQuicHint(net::URLRequestContext* context,
+                 const cronet::URLRequestContextConfig::QuicHint* quic_hint) {
+  if (quic_hint->host.empty()) {
+    LOG(ERROR) << "Empty QUIC hint host: " << quic_hint->host;
+    return;
+  }
+
+  url::CanonHostInfo host_info;
+  std::string canon_host(net::CanonicalizeHost(quic_hint->host, &host_info));
+  if (!host_info.IsIPAddress() &&
+      !net::IsCanonicalizedHostCompliant(canon_host)) {
+    LOG(ERROR) << "Invalid QUIC hint host: " << quic_hint->host;
+    return;
+  }
+
+  if (quic_hint->port <= std::numeric_limits<uint16_t>::min() ||
+      quic_hint->port > std::numeric_limits<uint16_t>::max()) {
+    LOG(ERROR) << "Invalid QUIC hint port: " << quic_hint->port;
+    return;
+  }
+
+  if (quic_hint->alternate_port <= std::numeric_limits<uint16_t>::min() ||
+      quic_hint->alternate_port > std::numeric_limits<uint16_t>::max()) {
+    LOG(ERROR) << "Invalid QUIC hint alternate port: "
+               << quic_hint->alternate_port;
+    return;
+  }
+
+  url::SchemeHostPort quic_server("https", canon_host, quic_hint->port);
+  net::AlternativeService alternative_service(
+      net::kProtoQUIC, "", static_cast<uint16_t>(quic_hint->alternate_port));
+  context->http_server_properties()->SetQuicAlternativeService(
+      quic_server, net::NetworkIsolationKey(), alternative_service,
+      base::Time::Max(), quic::ParsedQuicVersionVector());
+}
+
 }  // namespace
 
 namespace cronet {
@@ -172,7 +210,8 @@
 CronetURLRequestContext::NetworkTasks::NetworkTasks(
     std::unique_ptr<URLRequestContextConfig> context_config,
     std::unique_ptr<CronetURLRequestContext::Callback> callback)
-    : is_context_initialized_(false),
+    : default_context_(nullptr),
+      is_default_context_initialized_(false),
       context_config_(std::move(context_config)),
       callback_(std::move(callback)) {
   DETACH_FROM_THREAD(network_thread_checker_);
@@ -278,24 +317,22 @@
   // Initializing |network_qualities_prefs_manager_| may post a callback to
   // |this|. So, |network_qualities_prefs_manager_| should be initialized after
   // |callback_| has been initialized.
-  DCHECK(is_context_initialized_);
+  DCHECK(is_default_context_initialized_);
   cronet_prefs_manager_->SetupNqePersistence(network_quality_estimator_.get());
 }
 
 std::unique_ptr<net::URLRequestContext>
-CronetURLRequestContext::NetworkTasks::BuildURLRequestContext(
+CronetURLRequestContext::NetworkTasks::BuildDefaultURLRequestContext(
     std::unique_ptr<net::ProxyConfigService> proxy_config_service) {
+  DCHECK(!network_quality_estimator_);
+  DCHECK(!cronet_prefs_manager_);
   net::URLRequestContextBuilder context_builder;
-  context_builder.set_network_delegate(
-      std::make_unique<BasicNetworkDelegate>());
-  context_builder.set_net_log(g_net_log.Get().net_log());
+  SetSharedURLRequestContextBuilderConfig(&context_builder);
+
   context_builder.set_proxy_resolution_service(
       cronet::CreateProxyResolutionService(std::move(proxy_config_service),
                                            g_net_log.Get().net_log()));
 
-  context_config_->ConfigureURLRequestContextBuilder(&context_builder);
-
-  DCHECK(!network_quality_estimator_);
   if (context_config_->enable_network_quality_estimator) {
     std::unique_ptr<net::NetworkQualityEstimatorParams> nqe_params =
         std::make_unique<net::NetworkQualityEstimatorParams>(
@@ -314,7 +351,6 @@
         network_quality_estimator_.get());
   }
 
-  DCHECK(!cronet_prefs_manager_);
   // Set up pref file if storage path is specified.
   if (!context_config_->storage_path.empty()) {
 #if BUILDFLAG(IS_WIN)
@@ -323,7 +359,17 @@
 #else
     base::FilePath storage_path(context_config_->storage_path);
 #endif
-    // Set up the HttpServerPropertiesManager.
+    // Currently only the default context uses a PrefManager, this means that
+    // contexts for specific networks do not maintain state between restarts.
+    // Part of that is by design, part of that is due to CronetPrefsManager's
+    // current interface: it assumes that a single URLRequestContext exists
+    // and, under that assumption, mixes NQE, HostCache, and
+    // HttpServerProperties management persistence. The former two should
+    // apply only to the default context, while the latter could also be
+    // applied to network-bound contexts.
+    // TODO(stefanoduo): Decouple CronetPrefManager management of NQE,
+    // HostCache and HttpServerProperties and apply HttpServerProperties to
+    // network bound contexts.
     cronet_prefs_manager_ = std::make_unique<CronetPrefsManager>(
         context_config_->storage_path, network_task_runner_, file_task_runner_,
         context_config_->enable_network_quality_estimator,
@@ -331,14 +377,6 @@
         g_net_log.Get().net_log(), &context_builder);
   }
 
-  // Explicitly disable the persister for Cronet to avoid persistence of dynamic
-  // HPKP. This is a safety measure ensuring that nobody enables the persistence
-  // of HPKP by specifying transport_security_persister_file_path in the future.
-  context_builder.set_transport_security_persister_file_path(base::FilePath());
-
-  // Disable net::CookieStore.
-  context_builder.SetCookieStore(nullptr);
-
   auto context = context_builder.Build();
 
   // Set up host cache persistence if it's enabled. Happens after building the
@@ -350,46 +388,58 @@
         g_net_log.Get().net_log());
   }
 
+  SetSharedURLRequestContextConfig(context.get());
+  return context;
+}
+
+std::unique_ptr<net::URLRequestContext>
+CronetURLRequestContext::NetworkTasks::BuildNetworkBoundURLRequestContext(
+    net::NetworkChangeNotifier::NetworkHandle network) {
+  net::URLRequestContextBuilder context_builder;
+  SetSharedURLRequestContextBuilderConfig(&context_builder);
+
+  context_builder.BindToNetwork(network);
+  // On Android, Cronet doesn't handle PAC URL processing, instead it defers
+  // that to the OS (which sets up a local proxy configured correctly w.r.t.
+  // Android settings). See crbug.com/432539.
+  // TODO(stefanoduo): Confirm if we can keep using this configuration for
+  // requests bound to a network (otherwise we might have to query that
+  // network's LinkProperties#getHttpProxy).
+  // Until then don't support proxies when a network is specified.
+  context_builder.set_proxy_config_service(
+      std::make_unique<net::ProxyConfigServiceFixed>(
+          net::ProxyConfigWithAnnotation::CreateDirect()));
+
+  auto context = context_builder.Build();
+  SetSharedURLRequestContextConfig(context.get());
+  return context;
+}
+
+void CronetURLRequestContext::NetworkTasks::
+    SetSharedURLRequestContextBuilderConfig(
+        net::URLRequestContextBuilder* context_builder) {
+  context_builder->set_network_delegate(
+      std::make_unique<BasicNetworkDelegate>());
+  context_builder->set_net_log(g_net_log.Get().net_log());
+  context_config_->ConfigureURLRequestContextBuilder(context_builder);
+
+  // Explicitly disable the persister for Cronet to avoid persistence of dynamic
+  // HPKP. This is a safety measure ensuring that nobody enables the persistence
+  // of HPKP by specifying transport_security_persister_file_path in the future.
+  context_builder->set_transport_security_persister_file_path(base::FilePath());
+
+  // Disable net::CookieStore.
+  context_builder->SetCookieStore(nullptr);
+}
+
+void CronetURLRequestContext::NetworkTasks::SetSharedURLRequestContextConfig(
+    net::URLRequestContext* context) {
   context->set_check_cleartext_permitted(true);
   context->set_enable_brotli(context_config_->enable_brotli);
 
   if (context_config_->enable_quic) {
-    for (const auto& quic_hint : context_config_->quic_hints) {
-      if (quic_hint->host.empty()) {
-        LOG(ERROR) << "Empty QUIC hint host: " << quic_hint->host;
-        continue;
-      }
-
-      url::CanonHostInfo host_info;
-      std::string canon_host(
-          net::CanonicalizeHost(quic_hint->host, &host_info));
-      if (!host_info.IsIPAddress() &&
-          !net::IsCanonicalizedHostCompliant(canon_host)) {
-        LOG(ERROR) << "Invalid QUIC hint host: " << quic_hint->host;
-        continue;
-      }
-
-      if (quic_hint->port <= std::numeric_limits<uint16_t>::min() ||
-          quic_hint->port > std::numeric_limits<uint16_t>::max()) {
-        LOG(ERROR) << "Invalid QUIC hint port: " << quic_hint->port;
-        continue;
-      }
-
-      if (quic_hint->alternate_port <= std::numeric_limits<uint16_t>::min() ||
-          quic_hint->alternate_port > std::numeric_limits<uint16_t>::max()) {
-        LOG(ERROR) << "Invalid QUIC hint alternate port: "
-                   << quic_hint->alternate_port;
-        continue;
-      }
-
-      url::SchemeHostPort quic_server("https", canon_host, quic_hint->port);
-      net::AlternativeService alternative_service(
-          net::kProtoQUIC, "",
-          static_cast<uint16_t>(quic_hint->alternate_port));
-      context->http_server_properties()->SetQuicAlternativeService(
-          quic_server, net::NetworkIsolationKey(), alternative_service,
-          base::Time::Max(), quic::ParsedQuicVersionVector());
-    }
+    for (const auto& quic_hint : context_config_->quic_hints)
+      SetQuicHint(context, quic_hint.get());
   }
 
   // Iterate through PKP configuration for every host.
@@ -423,7 +473,6 @@
     }
   }
 #endif  // BUILDFLAG(ENABLE_REPORTING)
-  return context;
 }
 
 void CronetURLRequestContext::NetworkTasks::Initialize(
@@ -431,7 +480,7 @@
     scoped_refptr<base::SequencedTaskRunner> file_task_runner,
     std::unique_ptr<net::ProxyConfigService> proxy_config_service) {
   DCHECK_CALLED_ON_VALID_THREAD(network_thread_checker_);
-  DCHECK(!is_context_initialized_);
+  DCHECK(!is_default_context_initialized_);
 
   network_task_runner_ = network_task_runner;
   file_task_runner_ = file_task_runner;
@@ -442,10 +491,14 @@
   effective_experimental_options_ =
       base::Value(context_config_->effective_experimental_options);
 
-  context_ = BuildURLRequestContext(std::move(proxy_config_service));
+  const net::NetworkChangeNotifier::NetworkHandle default_network =
+      net::NetworkChangeNotifier::kInvalidNetworkHandle;
+  contexts_[default_network] =
+      BuildDefaultURLRequestContext(std::move(proxy_config_service));
+  default_context_ = contexts_[default_network].get();
 
   callback_->OnInitNetworkThread();
-  is_context_initialized_ = true;
+  is_default_context_initialized_ = true;
 
   if (context_config_->enable_network_quality_estimator &&
       cronet_prefs_manager_) {
@@ -466,12 +519,18 @@
 }
 
 net::URLRequestContext*
-CronetURLRequestContext::NetworkTasks::GetURLRequestContext() {
+CronetURLRequestContext::NetworkTasks::GetURLRequestContext(
+    net::NetworkChangeNotifier::NetworkHandle network) {
   DCHECK_CALLED_ON_VALID_THREAD(network_thread_checker_);
-  if (!context_) {
-    LOG(ERROR) << "URLRequestContext is not set up";
-  }
-  return context_.get();
+  DCHECK(is_default_context_initialized_);
+
+  if (network == net::NetworkChangeNotifier::kInvalidNetworkHandle)
+    return default_context_;
+
+  // Non-default contexts are created on the fly.
+  if (contexts_.find(network) == contexts_.end())
+    contexts_[network] = BuildNetworkBoundURLRequestContext(network);
+  return contexts_[network].get();
 }
 
 // Request context getter for CronetURLRequestContext.
@@ -509,9 +568,10 @@
   return new ContextGetter(this);
 }
 
-net::URLRequestContext* CronetURLRequestContext::GetURLRequestContext() {
+net::URLRequestContext* CronetURLRequestContext::GetURLRequestContext(
+    net::NetworkChangeNotifier::NetworkHandle network) {
   DCHECK(IsOnNetworkThread());
-  return network_tasks_->GetURLRequestContext();
+  return network_tasks_->GetURLRequestContext(network);
 }
 
 void CronetURLRequestContext::PostTaskToNetworkThread(
@@ -527,7 +587,7 @@
 void CronetURLRequestContext::NetworkTasks::RunTaskAfterContextInit(
     base::OnceClosure task_to_run_after_context_init) {
   DCHECK_CALLED_ON_VALID_THREAD(network_thread_checker_);
-  if (is_context_initialized_) {
+  if (is_default_context_initialized_) {
     DCHECK(tasks_waiting_for_context_.empty());
     std::move(task_to_run_after_context_init).Run();
     return;
@@ -654,8 +714,10 @@
                            : net::NetLogCaptureMode::kDefault;
   net_log_file_observer_ = net::FileNetLogObserver::CreateUnbounded(
       file_path, capture_mode, /*constants=*/nullptr);
-  CreateNetLogEntriesForActiveObjects({context_.get()},
-                                      net_log_file_observer_.get());
+  std::set<net::URLRequestContext*> contexts;
+  for (auto& iter : contexts_)
+    contexts.insert(iter.second.get());
+  CreateNetLogEntriesForActiveObjects(contexts, net_log_file_observer_.get());
   net_log_file_observer_->StartObserving(g_net_log.Get().net_log());
 }
 
@@ -691,8 +753,10 @@
   net_log_file_observer_ = net::FileNetLogObserver::CreateBounded(
       file_path, size, capture_mode, /*constants=*/nullptr);
 
-  CreateNetLogEntriesForActiveObjects({context_.get()},
-                                      net_log_file_observer_.get());
+  std::set<net::URLRequestContext*> contexts;
+  for (auto& iter : contexts_)
+    contexts.insert(iter.second.get());
+  CreateNetLogEntriesForActiveObjects(contexts, net_log_file_observer_.get());
 
   net_log_file_observer_->StartObserving(g_net_log.Get().net_log());
 }
@@ -716,7 +780,10 @@
 }
 
 base::Value CronetURLRequestContext::NetworkTasks::GetNetLogInfo() const {
-  base::Value net_info = net::GetNetInfo(context_.get());
+  base::Value net_info(base::Value::Type::DICTIONARY);
+  for (auto& iter : contexts_)
+    net_info.SetKey(base::NumberToString(iter.first),
+                    net::GetNetInfo(iter.second.get()));
   if (!effective_experimental_options_.DictEmpty()) {
     net_info.SetKey("cronetExperimentalParams",
                     effective_experimental_options_.Clone());
diff --git a/components/cronet/cronet_url_request_context.h b/components/cronet/cronet_url_request_context.h
index e853029..276390ea 100644
--- a/components/cronet/cronet_url_request_context.h
+++ b/components/cronet/cronet_url_request_context.h
@@ -11,12 +11,14 @@
 #include <string>
 
 #include "base/callback.h"
+#include "base/containers/flat_map.h"
 #include "base/containers/queue.h"
 #include "base/memory/raw_ptr.h"
 #include "base/memory/ref_counted.h"
 #include "base/threading/thread.h"
 #include "base/threading/thread_checker.h"
 #include "components/prefs/json_pref_store.h"
+#include "net/base/network_change_notifier.h"
 #include "net/nqe/effective_connection_type.h"
 #include "net/nqe/effective_connection_type_observer.h"
 #include "net/nqe/network_quality_estimator.h"
@@ -35,6 +37,7 @@
 class ProxyConfigService;
 class NetLog;
 class URLRequestContext;
+class URLRequestContextBuilder;
 class URLRequestContextGetter;
 class FileNetLogObserver;
 }  // namespace net
@@ -118,12 +121,18 @@
   // Returns true if running on network thread.
   bool IsOnNetworkThread() const;
 
-  // Returns net::URLRequestContext owned by |this|.
-  net::URLRequestContext* GetURLRequestContext();
+  // Returns the net::URLRequestContext associated with `network`.
+  // kInvalidNetworkHandle represent the default context: this one will always
+  // be present and used whenever a requests doesn't specify a target network
+  // (currently the only possible behavior).
+  net::URLRequestContext* GetURLRequestContext(
+      net::NetworkChangeNotifier::NetworkHandle network =
+          net::NetworkChangeNotifier::kInvalidNetworkHandle);
 
   // Returns a new instance of net::URLRequestContextGetter.
   // The net::URLRequestContext and base::SingleThreadTaskRunner that
-  // net::URLRequestContextGetter returns are owned by |this|.
+  // net::URLRequestContextGetter returns are owned by `this`.
+  // The returned getter will always return the default context of `this`.
   net::URLRequestContextGetter* CreateURLRequestContextGetter();
 
   // TODO(xunjieli): Keep only one version of StartNetLog().
@@ -230,7 +239,8 @@
         const base::TimeTicks& timestamp,
         net::NetworkQualityObservationSource source) override;
 
-    net::URLRequestContext* GetURLRequestContext();
+    net::URLRequestContext* GetURLRequestContext(
+        net::NetworkChangeNotifier::NetworkHandle network);
 
     // Same as StartNetLogToDisk.
     void StartNetLogToBoundedFile(const std::string& dir_path,
@@ -256,8 +266,23 @@
     friend class TestUtil;
     base::Value GetNetLogInfo() const;
 
-    // Wraps the logic to build the underlying net::URLRequestContext.
-    std::unique_ptr<net::URLRequestContext> BuildURLRequestContext(
+    // Configures `context_builder` with the settings shared between default
+    // context and network bound contexts.
+    void SetSharedURLRequestContextBuilderConfig(
+        net::URLRequestContextBuilder* context_builder);
+
+    // Configures `context` with the settings shared between default context
+    // and network bound contexts.
+    void SetSharedURLRequestContextConfig(net::URLRequestContext* context);
+
+    // Builds a URLRequestContext specifically bound to `network`.
+    std::unique_ptr<net::URLRequestContext> BuildNetworkBoundURLRequestContext(
+        net::NetworkChangeNotifier::NetworkHandle network);
+
+    // Builds a URLRequestContext to be used a default context for `this`.
+    // `proxy_config_service` is injected as it currently cannot be built on the
+    // network thread.
+    std::unique_ptr<net::URLRequestContext> BuildDefaultURLRequestContext(
         std::unique_ptr<net::ProxyConfigService> proxy_config_service);
 
     std::unique_ptr<net::FileNetLogObserver> net_log_file_observer_;
@@ -274,8 +299,19 @@
     // after |context_|.
     std::unique_ptr<CronetPrefsManager> cronet_prefs_manager_;
 
-    std::unique_ptr<net::URLRequestContext> context_;
-    bool is_context_initialized_;
+    // The mapping between networks and their URLRequestContext. The only
+    // context guaranteed to exist for the entire lifetime of `this` is default
+    // one, which is associated to kInvalidNetworkHandle.
+    // For requests not requiring a specific network the default context must be
+    // used.
+    base::flat_map<net::NetworkChangeNotifier::NetworkHandle,
+                   std::unique_ptr<net::URLRequestContext>>
+        contexts_;
+    // Shorthand for the default context (needed by
+    // components/cronet/android/test/cronet_test_util.cc).
+    net::URLRequestContext* default_context_;
+
+    bool is_default_context_initialized_;
 
     // Context config is only valid until context is initialized.
     std::unique_ptr<URLRequestContextConfig> context_config_;
diff --git a/components/cronet/tools/android_rndis_forwarder.py b/components/cronet/tools/android_rndis_forwarder.py
index 4eff5da..2d3d0b34 100644
--- a/components/cronet/tools/android_rndis_forwarder.py
+++ b/components/cronet/tools/android_rndis_forwarder.py
@@ -11,13 +11,13 @@
 import subprocess
 import sys
 
+# pylint: disable=wrong-import-position
 REPOSITORY_ROOT = os.path.abspath(os.path.join(
     os.path.dirname(__file__), '..', '..', '..'))
 sys.path.append(os.path.join(REPOSITORY_ROOT, 'tools', 'perf'))
-from core import path_util  # pylint: disable=wrong-import-position
+from core import path_util
 sys.path.append(path_util.GetTelemetryDir())
 
-# pylint: disable=wrong-import-position
 from telemetry.core import platform
 from telemetry.internal.platform import android_device
 from telemetry.internal.util import binary_manager
@@ -29,7 +29,7 @@
 # pylint: enable=wrong-import-position
 
 
-class AndroidRndisForwarder(object):
+class AndroidRndisForwarder():
   """Forwards traffic using RNDIS. Assumes the device has root access."""
 
   def __init__(self, device, rndis_configurator):
@@ -129,7 +129,7 @@
         ['netcfg', self._device_iface, 'down'], check_return=True)
 
 
-class AndroidRndisConfigurator(object):
+class AndroidRndisConfigurator():
   """Configures a linux host to connect to an android device via RNDIS.
 
   Note that we intentionally leave RNDIS running on the device. This is
@@ -171,6 +171,7 @@
     """Checks that the device has RNDIS support in the kernel."""
     return self._device.FileExists('%s/f_rndis/device' % self._RNDIS_DEVICE)
 
+  # pylint: disable=inconsistent-return-statements
   def _FindDeviceRndisInterface(self):
     """Returns the name of the RNDIS network interface if present."""
     config = self._device.RunShellCommand(
@@ -184,6 +185,7 @@
         return candidates[0]
       assert len(candidates) == 1, 'Found more than one rndis device!'
       return candidates[0]
+  # pylint: enable=inconsistent-return-statements
 
   def _FindDeviceRndisMacAddress(self, interface):
     """Returns the MAC address of the RNDIS network interface if present."""
@@ -199,6 +201,7 @@
       return subprocess.check_output(['ifconfig']).splitlines()
     raise NotImplementedError('Platform %s not supported!' % host_platform)
 
+  # pylint: disable=inconsistent-return-statements
   def _FindHostRndisInterface(self, device_mac_address):
     """Returns the name of the host-side network interface."""
     interface_list = self._EnumerateHostInterfaces()
@@ -224,6 +227,7 @@
       # but just going by the host interface name seems safe enough.
       elif interface_name == 'usb0':
         return interface_name
+  # pylint: enable=inconsistent-return-statements
 
   def _WriteProtectedFile(self, file_path, contents):
     subprocess.check_call(
@@ -403,12 +407,14 @@
     def _IsNetworkUnique(network, addresses):
       return all((addr & mask != network & mask) for addr, mask in addresses)
 
+    # pylint: disable=inconsistent-return-statements
     def _NextUnusedAddress(network, netmask, used_addresses):
       # Excludes '0' and broadcast.
       for suffix in range(1, 0xFFFFFFFF & ~netmask):
         candidate = network | suffix
         if candidate not in used_addresses:
           return candidate
+    # pylint: enable=inconsistent-return-statements
 
     def HasHostAddress():
       _, host_address = self._GetHostAddresses(host_iface)
diff --git a/components/cronet/tools/api_static_checks_unittest.py b/components/cronet/tools/api_static_checks_unittest.py
index 1ca46ec5..4f44e39 100755
--- a/components/cronet/tools/api_static_checks_unittest.py
+++ b/components/cronet/tools/api_static_checks_unittest.py
@@ -102,7 +102,7 @@
 
   def run_check_api_calls(self, api_java, impl_java):
     test = self
-    class MockOpts(object):
+    class MockOpts():
       def __init__(self):
         self.api_jar = test.make_jar(api_java, 'Api')
         self.impl_jar = [test.make_jar(impl_java, 'Impl')]
@@ -148,7 +148,7 @@
     stamp_length = len('Stamp: 78418460c193047980ae9eabb79293f2\n')
     api = api[:-stamp_length]
     api_hash = hashlib.md5()
-    api_hash.update(api)
+    api_hash.update(api.encode())
     self.assertEqual(api_stamp, 'Stamp: %s' % api_hash.hexdigest())
 
     return [return_code == 0, output, api, api_version]
diff --git a/components/cronet/tools/generate_accept_languages.py b/components/cronet/tools/generate_accept_languages.py
index 2db0a44..beb4170 100644
--- a/components/cronet/tools/generate_accept_languages.py
+++ b/components/cronet/tools/generate_accept_languages.py
@@ -13,7 +13,6 @@
 #   * assumes that there is only one relevant element with the
 #     IDS_ACCEPT_LANGUAGES attribute
 
-from __future__ import print_function
 
 import os
 import re
@@ -22,11 +21,13 @@
 
 STRINGS_DIR = sys.argv[2] + 'components/strings/'
 
+# pylint: disable=inconsistent-return-statements
 def extract_accept_langs(filename):
   tree = ElementTree.parse(STRINGS_DIR + filename).getroot()
   for child in tree:
     if child.get('id') == 'IDS_ACCEPT_LANGUAGES':
       return tree.get('lang'), child.text
+# pylint: enable=inconsistent-return-statements
 
 def gen_accept_langs_table():
   accept_langs_list = [extract_accept_langs(filename)
diff --git a/components/cronet/tools/generate_idl_bindings.py b/components/cronet/tools/generate_idl_bindings.py
index 93599da2..fcaeec45 100755
--- a/components/cronet/tools/generate_idl_bindings.py
+++ b/components/cronet/tools/generate_idl_bindings.py
@@ -4,7 +4,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import optparse
+import argparse
 import os
 import shutil
 import sys
@@ -36,12 +36,12 @@
 
 
 def main():
-  parser = optparse.OptionParser()
-  parser.add_option('--output-path',
+  parser = argparse.ArgumentParser()
+  parser.add_argument('--output-path',
         help='Output path for generated bindings')
 
-  options, input_files = parser.parse_args()
-  GenerateIdlBindings(options.output_path, input_files)
+  args, input_files = parser.parse_known_args()
+  GenerateIdlBindings(args.output_path, input_files)
 
 
 if __name__ == '__main__':
diff --git a/components/cronet/tools/generate_javadoc.py b/components/cronet/tools/generate_javadoc.py
index cc878ca..0a6843f 100755
--- a/components/cronet/tools/generate_javadoc.py
+++ b/components/cronet/tools/generate_javadoc.py
@@ -4,7 +4,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import optparse
+import argparse
 import os
 import shutil
 import sys
@@ -45,11 +45,11 @@
                           CronetPostprocessor(md), '_end')
 
 
-def GenerateJavadoc(options, src_dir, output_dir):
-  working_dir = os.path.join(options.input_dir, 'android', 'api')
-  overview_file = os.path.abspath(options.overview_file)
+def GenerateJavadoc(args, src_dir, output_dir):
+  working_dir = os.path.join(args.input_dir, 'android', 'api')
+  overview_file = os.path.abspath(args.overview_file)
 
-  android_sdk_jar = options.android_sdk_jar
+  android_sdk_jar = args.android_sdk_jar
   if not android_sdk_jar:
     android_sdk_jar = os.path.join(
         SDK_DIR, 'platforms', 'android-27', 'android.jar')
@@ -70,7 +70,7 @@
     '-federationapi', 'Android', os.path.join(DOCLAVA_DIR, 'current.txt'),
     '-classpath',
     '%s:%s' % (os.path.abspath(android_sdk_jar),
-               os.path.abspath(options.support_annotations_jar)),
+               os.path.abspath(args.support_annotations_jar)),
   ]
   for subdir, _, files in os.walk(src_dir):
     for filename in files:
@@ -96,44 +96,44 @@
 
 
 def main():
-  parser = optparse.OptionParser()
+  parser = argparse.ArgumentParser()
   build_utils.AddDepfileOption(parser)
-  parser.add_option('--output-dir', help='Directory to put javadoc')
-  parser.add_option('--input-dir', help='Root of cronet source')
-  parser.add_option('--input-src-jar', help='Cronet api source jar')
-  parser.add_option('--overview-file', help='Path of the overview page')
-  parser.add_option('--readme-file', help='Path of the README.md')
-  parser.add_option('--zip-file', help='Path to ZIP archive of javadocs.')
-  parser.add_option('--android-sdk-jar', help='Path to android.jar')
-  parser.add_option('--support-annotations-jar',
+  parser.add_argument('--output-dir', help='Directory to put javadoc')
+  parser.add_argument('--input-dir', help='Root of cronet source')
+  parser.add_argument('--input-src-jar', help='Cronet api source jar')
+  parser.add_argument('--overview-file', help='Path of the overview page')
+  parser.add_argument('--readme-file', help='Path of the README.md')
+  parser.add_argument('--zip-file', help='Path to ZIP archive of javadocs.')
+  parser.add_argument('--android-sdk-jar', help='Path to android.jar')
+  parser.add_argument('--support-annotations-jar',
                     help='Path to support-annotations-$VERSION.jar')
 
-  options, _ = parser.parse_args()
+  args, _ = parser.parse_known_args()
   # A temporary directory to put the output of cronet api source jar files.
-  unzipped_jar_path = tempfile.mkdtemp(dir=options.output_dir)
-  if os.path.exists(options.input_src_jar):
-    jar_cmd = ['jar', 'xf', os.path.abspath(options.input_src_jar)]
+  unzipped_jar_path = tempfile.mkdtemp(dir=args.output_dir)
+  if os.path.exists(args.input_src_jar):
+    jar_cmd = ['jar', 'xf', os.path.abspath(args.input_src_jar)]
     build_utils.CheckOutput(jar_cmd, cwd=unzipped_jar_path)
   else:
-    raise Exception('Jar file does not exist: %s' % options.input_src_jar)
+    raise Exception('Jar file does not exist: %s' % args.input_src_jar)
 
-  net_docs.ProcessDocs([options.readme_file], options.input_dir,
-                       options.output_dir, extensions=[CronetExtension()])
+  net_docs.ProcessDocs([args.readme_file], args.input_dir,
+                       args.output_dir, extensions=[CronetExtension()])
 
-  output_dir = os.path.abspath(os.path.join(options.output_dir, 'javadoc'))
-  GenerateJavadoc(options, os.path.abspath(unzipped_jar_path), output_dir)
+  output_dir = os.path.abspath(os.path.join(args.output_dir, 'javadoc'))
+  GenerateJavadoc(args, os.path.abspath(unzipped_jar_path), output_dir)
 
-  if options.zip_file:
-    assert options.zip_file.endswith('.zip')
-    shutil.make_archive(options.zip_file[:-4], 'zip', output_dir)
-  if options.depfile:
-    assert options.zip_file
+  if args.zip_file:
+    assert args.zip_file.endswith('.zip')
+    shutil.make_archive(args.zip_file[:-4], 'zip', output_dir)
+  if args.depfile:
+    assert args.zip_file
     deps = []
-    for root, _, filenames in os.walk(options.input_dir):
+    for root, _, filenames in os.walk(args.input_dir):
       # Ignore .pyc files here, it might be re-generated during build.
       deps.extend(os.path.join(root, f) for f in filenames
                   if not f.endswith('.pyc'))
-    build_utils.WriteDepfile(options.depfile, options.zip_file, deps)
+    build_utils.WriteDepfile(args.depfile, args.zip_file, deps)
   # Clean up temporary output directory.
   build_utils.DeleteDirectory(unzipped_jar_path)
 
diff --git a/components/cronet/tools/generate_proguard_file.py b/components/cronet/tools/generate_proguard_file.py
index 8ff42cf..dcb8ad8 100755
--- a/components/cronet/tools/generate_proguard_file.py
+++ b/components/cronet/tools/generate_proguard_file.py
@@ -10,7 +10,7 @@
 # The final output file is formed by concatenating all of the
 # input proguard files.
 
-import optparse
+import argparse
 import sys
 
 
@@ -20,14 +20,14 @@
 
 
 def main():
-  parser = optparse.OptionParser()
-  parser.add_option('--output-file',
+  parser = argparse.ArgumentParser()
+  parser.add_argument('--output-file',
           help='Output file for the generated proguard file')
 
-  options, input_files = parser.parse_args()
+  args, input_files = parser.parse_known_args()
 
   # Concatenate all the proguard files.
-  with open(options.output_file, 'wb') as target:
+  with open(args.output_file, 'wb') as target:
     for input_file in input_files:
       target.write(ReadFile(input_file))
 
diff --git a/components/cronet/tools/generators/cronet_bindings_generator.py b/components/cronet/tools/generators/cronet_bindings_generator.py
index 9e880c5..e3af8a5d4 100755
--- a/components/cronet/tools/generators/cronet_bindings_generator.py
+++ b/components/cronet/tools/generators/cronet_bindings_generator.py
@@ -79,7 +79,7 @@
                     zip(imported_filename_stack[1:], imported_filename_stack)]))
 
 
-class RelativePath(object):
+class RelativePath():
   """Represents a path relative to the source tree."""
   def __init__(self, path, source_root):
     self.path = path
@@ -139,7 +139,7 @@
     return f.read()
 
 
-class MojomProcessor(object):
+class MojomProcessor():
   """Parses mojom files and creates ASTs for them.
 
   Attributes:
diff --git a/components/cronet/tools/generators/cronet_c_generator.py b/components/cronet/tools/generators/cronet_c_generator.py
index 3c80f08..01ce499 100644
--- a/components/cronet/tools/generators/cronet_c_generator.py
+++ b/components/cronet/tools/generators/cronet_c_generator.py
@@ -40,7 +40,7 @@
 
 ATTRIBUTE_ABSTRACT = "Abstract"
 
-class _NameFormatter(object):
+class _NameFormatter():
   """A formatter for the names of kinds or values."""
 
   def __init__(self, token, variant):
@@ -118,9 +118,12 @@
   def _ShouldIncludeNamespace(self, omit_namespace_for_module):
     return self._token.module
 
+
+  # pylint: disable=inconsistent-return-statements
   def _GetNamespace(self):
     if self._token.module:
       return NamespaceToArray(self._token.module.namespace)
+  # pylint: enable=inconsistent-return-statements
 
 
 def NamespaceToArray(namespace):
@@ -183,7 +186,7 @@
   return kind.attributes.get(ATTRIBUTE_ABSTRACT, False) \
       if kind.attributes else False
 
-class StructConstructor(object):
+class StructConstructor():
   """Represents a constructor for a generated struct.
 
   Fields:
@@ -206,8 +209,6 @@
 
 
 class Generator(generator.Generator):
-  def __init__(self, *args, **kwargs):
-    super(Generator, self).__init__(*args, **kwargs)
 
   def _GetExtraTraitsHeaders(self):
     extra_headers = set()
@@ -507,30 +508,29 @@
       checked.add(kind.spec)
       if mojom.IsNullableKind(kind):
         return False
-      elif mojom.IsStructKind(kind):
+      if mojom.IsStructKind(kind):
         if kind.native_only:
           return False
         if (self._IsTypemappedKind(kind) and
             not self.typemap[self._GetFullMojomNameForKind(kind)]["hashable"]):
           return False
         return all(Check(field.kind) for field in kind.fields)
-      elif mojom.IsEnumKind(kind):
+      if mojom.IsEnumKind(kind):
         return not self._IsTypemappedKind(kind) or self.typemap[
             self._GetFullMojomNameForKind(kind)]["hashable"]
-      elif mojom.IsUnionKind(kind):
+      if mojom.IsUnionKind(kind):
         return all(Check(field.kind) for field in kind.fields)
-      elif mojom.IsAnyHandleKind(kind):
+      if mojom.IsAnyHandleKind(kind):
         return False
-      elif mojom.IsAnyInterfaceKind(kind):
+      if mojom.IsAnyInterfaceKind(kind):
         return False
       # TODO(crbug.com/735301): Arrays and maps could be made hashable. We just
       # don't have a use case yet.
-      elif mojom.IsArrayKind(kind):
+      if mojom.IsArrayKind(kind):
         return False
-      elif mojom.IsMapKind(kind):
+      if mojom.IsMapKind(kind):
         return False
-      else:
-        return True
+      return True
     return Check(kind)
 
   def _GetNativeTypeName(self, typemapped_kind):
@@ -809,10 +809,8 @@
         return "%d, %s, %s" % (expected_num_elements,
                                "true" if element_is_nullable else "false",
                                element_validate_params)
-      else:
-        return "%s, %s" % (key_validate_params, element_validate_params)
-    else:
-      return "%d, %s" % (expected_num_elements, enum_validate_func)
+      return "%s, %s" % (key_validate_params, element_validate_params)
+    return "%d, %s" % (expected_num_elements, enum_validate_func)
 
   def _GetNewContainerValidateParams(self, kind):
     if (not mojom.IsArrayKind(kind) and not mojom.IsMapKind(kind) and
diff --git a/components/cronet/tools/hide_symbols.py b/components/cronet/tools/hide_symbols.py
index 8bc50c3..8608dfe 100755
--- a/components/cronet/tools/hide_symbols.py
+++ b/components/cronet/tools/hide_symbols.py
@@ -15,7 +15,7 @@
 
 
 import glob
-import optparse
+import argparse
 import os
 import subprocess
 import sys
@@ -34,32 +34,32 @@
 
 
 def main():
-  parser = optparse.OptionParser()
-  parser.add_option(
+  parser = argparse.ArgumentParser()
+  parser.add_argument(
       '--input_libs',
       help='Comma-separated paths to input .a files which contain symbols '
            'which must be always linked.')
-  parser.add_option(
+  parser.add_argument(
       '--deps_lib',
       help='The path to a complete static library (.a file) which contains all '
            'dependencies of --input_libs. .o files in this library are also '
            'added to the output library, but only if they are referred from '
            '--input_libs.')
-  parser.add_option(
+  parser.add_argument(
       '--output_obj',
       help='Outputs the generated .o file here. This is an intermediate file.')
-  parser.add_option(
+  parser.add_argument(
       '--output_lib',
       help='Outputs the generated .a file here.')
-  parser.add_option(
+  parser.add_argument(
       '--current_cpu',
       help='The current processor architecture in the format of the target_cpu '
            'attribute in GN.')
-  parser.add_option(
+  parser.add_argument(
       '--use_custom_libcxx', default=False, action='store_true',
       help='Confirm there is a custom libc++ linked in.')
-  (options, args) = parser.parse_args()
-  assert not args
+  args, unknownargs = parser.parse_known_args()
+  assert not unknownargs
 
   developer_dir = subprocess.check_output(
       ['xcode-select', '--print-path'], universal_newlines=True).strip()
@@ -73,49 +73,49 @@
   # while "hiding" symbols not marked as visible.
   command = [
     'xcrun', 'ld',
-    '-arch', GN_CPU_TO_LD_ARCH[options.current_cpu],
+    '-arch', GN_CPU_TO_LD_ARCH[args.current_cpu],
     '-r',
   ]
-  for input_lib in options.input_libs.split(','):
+  for input_lib in args.input_libs.split(','):
     # By default, ld only pulls .o files out of a static library if needed to
     # resolve some symbol reference. We apply -force_load option to input_lib
     # (but not to deps_lib) to force pulling all .o files.
     command += ['-force_load', input_lib]
   command += xctoolchain_libs
   command += [
-    options.deps_lib,
-    '-o', options.output_obj
+    args.deps_lib,
+    '-o', args.output_obj
   ]
   try:
     subprocess.check_call(command)
   except subprocess.CalledProcessError:
     # Work around LD failure for x86 Debug buiilds when it fails with error:
     # ld: scattered reloc r_address too large for architecture i386
-    if options.current_cpu == "x86":
+    if args.current_cpu == "x86":
       # Combmine input lib with dependencies into output lib.
       command = [
         'xcrun', 'libtool', '-static', '-no_warning_for_no_symbols',
-        '-o', options.output_lib,
-        options.input_libs, options.deps_lib,
+        '-o', args.output_lib,
+        args.input_libs, args.deps_lib,
       ]
       subprocess.check_call(command)
       # Strip debug info from output lib so its size doesn't exceed 512mb.
       command = [
-        'xcrun', 'strip', '-S', options.output_lib,
+        'xcrun', 'strip', '-S', args.output_lib,
       ]
       subprocess.check_call(command)
       return
-    else:
-      exit(1)
 
-  if os.path.exists(options.output_lib):
-    os.remove(options.output_lib)
+    sys.exit(1)
+
+  if os.path.exists(args.output_lib):
+    os.remove(args.output_lib)
 
   # Creates a .a file which contains a single .o file.
   command = [
     'xcrun', 'ar', '-r',
-    options.output_lib,
-    options.output_obj,
+    args.output_lib,
+    args.output_obj,
   ]
 
   # When compiling for 64bit targets, the symbols in call_with_eh_frame.o are
@@ -125,13 +125,13 @@
   # other parts of cronet. Instead, simply add a second .o file with the
   # personality routine. Note that this issue was not caught by Chrome tests,
   # it was only detected when apps tried to link the resulting .a file.
-  if options.current_cpu == 'x64' or options.current_cpu == 'arm64':
+  if args.current_cpu in ('x64', 'arm64'):
     command += [ 'obj/base/base/call_with_eh_frame.o' ]
 
   subprocess.check_call(command)
 
-  if options.use_custom_libcxx:
-    ret = os.system('xcrun nm -u "' + options.output_obj +
+  if args.use_custom_libcxx:
+    ret = os.system('xcrun nm -u "' + args.output_obj +
                     '" | grep ___cxa_pure_virtual')
     if ret == 0:
       print("ERROR: Found undefined libc++ symbols, "
diff --git a/components/cronet/tools/perf_test_utils.py b/components/cronet/tools/perf_test_utils.py
index b1b5d9b..23b4a71 100755
--- a/components/cronet/tools/perf_test_utils.py
+++ b/components/cronet/tools/perf_test_utils.py
@@ -62,11 +62,11 @@
 # Add benchmark config to global state for easy access.
 globals().update(DEFAULT_BENCHMARK_CONFIG)
 # Pylint doesn't really interpret the file, so it won't find the definitions
-# added from DEFAULT_BENCHMARK_CONFIG, so suppress the undefined variable
-# warning.
-#pylint: disable=undefined-variable
+# added from DEFAULT_BENCHMARK_CONFIG, so suppress the undefined variable and
+# bad string format type warnings.
+#pylint: disable=undefined-variable,bad-string-format-type
 
-class NativeDevice(object):
+class NativeDevice():
   def GetExternalStoragePath(self):
     return '/tmp'
 
@@ -108,7 +108,7 @@
   return 'http://%s:%d/%s' % (GetServersHost(device), HTTP_PORT, resource)
 
 
-class QuicServer(object):
+class QuicServer():
 
   def __init__(self, quic_server_doc_root):
     self._process = None
@@ -122,7 +122,7 @@
            '--port=%d' % QUIC_PORT]
     logging.info("Starting Quic Server: %s", cmd)
     self._process = subprocess.Popen(cmd)
-    assert self._process != None
+    assert self._process is not None
     # Wait for quic_server to start serving.
     waited_s = 0
     while subprocess.call(['lsof', '-i', 'udp:%d' % QUIC_PORT, '-p',
diff --git a/components/dom_distiller/core/url_utils_unittest.cc b/components/dom_distiller/core/url_utils_unittest.cc
index bac4843..388b46b7 100644
--- a/components/dom_distiller/core/url_utils_unittest.cc
+++ b/components/dom_distiller/core/url_utils_unittest.cc
@@ -47,7 +47,7 @@
 }
 
 void AssertEqualExceptHost(const GURL& a, const GURL& b) {
-  url::Replacements<char> no_host;
+  GURL::Replacements no_host;
   no_host.ClearHost();
   EXPECT_EQ(a.ReplaceComponents(no_host), b.ReplaceComponents(no_host));
 }
diff --git a/components/exo/data_offer_unittest.cc b/components/exo/data_offer_unittest.cc
index 2b29b6cb..2783dda 100644
--- a/components/exo/data_offer_unittest.cc
+++ b/components/exo/data_offer_unittest.cc
@@ -500,7 +500,7 @@
   {
     ui::ScopedClipboardWriter writer(ui::ClipboardBuffer::kCopyPaste);
     writer.SetDataSource(std::make_unique<ui::DataTransferEndpoint>(
-        url::Origin::Create(GURL("https://www.google.com"))));
+        (GURL("https://www.google.com"))));
     writer.WriteText(u"Test data");
   }
 
@@ -532,8 +532,9 @@
                      std::move(write_pipe));
   std::string dte_json_result;
   ASSERT_TRUE(ReadString(std::move(read_pipe), &dte_json_result));
-  EXPECT_EQ(R"({"endpoint_type":"url","url_origin":"https://www.google.com"})",
-            dte_json_result);
+  EXPECT_EQ(
+      R"({"endpoint_type":"url","url":"https://www.google.com/","url_origin":"https://www.google.com"})",
+      dte_json_result);
 }
 
 TEST_F(DataOfferTest, SetClipboardDataDoNotOfferDteToNonLacros) {
@@ -545,7 +546,7 @@
   {
     ui::ScopedClipboardWriter writer(ui::ClipboardBuffer::kCopyPaste);
     writer.SetDataSource(std::make_unique<ui::DataTransferEndpoint>(
-        url::Origin::Create(GURL("https://www.google.com"))));
+        (GURL("https://www.google.com"))));
     writer.WriteText(u"Test data");
   }
 
@@ -589,7 +590,7 @@
   ui::OSExchangeData data;
   data.SetString(std::u16string(u"Test data"));
   data.SetSource(std::make_unique<ui::DataTransferEndpoint>(
-      url::Origin::Create(GURL("https://www.google.com"))));
+      (GURL("https://www.google.com"))));
 
   TestDataOfferDelegate delegate;
   DataOffer data_offer(&delegate);
@@ -630,8 +631,9 @@
                      std::move(write_pipe));
   std::string dte_json_result;
   ASSERT_TRUE(ReadString(std::move(read_pipe), &dte_json_result));
-  EXPECT_EQ(R"({"endpoint_type":"url","url_origin":"https://www.google.com"})",
-            dte_json_result);
+  EXPECT_EQ(
+      R"({"endpoint_type":"url","url":"https://www.google.com/","url_origin":"https://www.google.com"})",
+      dte_json_result);
 }
 
 TEST_F(DataOfferTest, SetDropDataDoNotOfferDteToNonLacros) {
@@ -642,7 +644,7 @@
   ui::OSExchangeData data;
   data.SetString(std::u16string(u"Test data"));
   data.SetSource(std::make_unique<ui::DataTransferEndpoint>(
-      url::Origin::Create(GURL("https://www.google.com"))));
+      (GURL("https://www.google.com"))));
 
   TestDataOfferDelegate delegate;
   DataOffer data_offer(&delegate);
diff --git a/components/exo/seat_unittest.cc b/components/exo/seat_unittest.cc
index be34e9dc6..3b31514c 100644
--- a/components/exo/seat_unittest.cc
+++ b/components/exo/seat_unittest.cc
@@ -167,7 +167,7 @@
 
   const std::string kTestText = "TestData";
   const std::string kEncodedTestDte =
-      R"({"endpoint_type":"url","url_origin":"https://www.google.com"})";
+      R"({"endpoint_type":"url","url":"https://www.google.com"})";
 
   const std::string kTextMimeType = "text/plain;charset=utf-8";
   const std::string kDteMimeType = "chromium/x-data-transfer-endpoint";
@@ -198,10 +198,9 @@
   ASSERT_TRUE(source_dte);
   EXPECT_EQ(ui::EndpointType::kUrl, source_dte->type());
 
-  const ui::DataTransferEndpoint expected_dte = ui::DataTransferEndpoint(
-      url::Origin::Create(GURL("https://www.google.com")));
-  EXPECT_TRUE(
-      expected_dte.GetOrigin()->IsSameOriginWith(*source_dte->GetOrigin()));
+  const ui::DataTransferEndpoint expected_dte =
+      ui::DataTransferEndpoint((GURL("https://www.google.com")));
+  EXPECT_EQ(*expected_dte.GetURL(), *source_dte->GetURL());
 }
 
 TEST_F(SeatTest, SetSelectionIgnoreDteFromNonLacros) {
@@ -214,7 +213,7 @@
 
   const std::string kTestText = "TestData";
   const std::string kEncodedTestDte =
-      R"({"endpoint_type":"url","url_origin":"https://www.google.com"})";
+      R"({"endpoint_type":"url","url":"https://www.google.com"})";
 
   const std::string kTextMimeType = "text/plain;charset=utf-8";
   const std::string kDteMimeType = "chromium/x-data-transfer-endpoint";
diff --git a/components/history_clusters/core/history_clusters_service.cc b/components/history_clusters/core/history_clusters_service.cc
index eaa02d5..2ac8254 100644
--- a/components/history_clusters/core/history_clusters_service.cc
+++ b/components/history_clusters/core/history_clusters_service.cc
@@ -221,6 +221,10 @@
   observers_.RemoveObserver(obs);
 }
 
+bool HistoryClustersService::ShouldNotifyDebugMessage() const {
+  return !observers_.empty();
+}
+
 void HistoryClustersService::NotifyDebugMessage(
     const std::string& message) const {
   for (Observer& obs : observers_) {
@@ -276,13 +280,15 @@
     base::Time end_time,
     QueryClustersCallback callback,
     base::CancelableTaskTracker* task_tracker) {
-  NotifyDebugMessage("HistoryClustersService::QueryClusters()");
-  NotifyDebugMessage(
-      "  begin_time = " +
-      (begin_time.is_null() ? "null" : base::TimeToISO8601(begin_time)));
-  NotifyDebugMessage("  end_time = " + (end_time.is_null()
-                                            ? "null"
-                                            : base::TimeToISO8601(end_time)));
+  if (ShouldNotifyDebugMessage()) {
+    NotifyDebugMessage("HistoryClustersService::QueryClusters()");
+    NotifyDebugMessage(
+        "  begin_time = " +
+        (begin_time.is_null() ? "null" : base::TimeToISO8601(begin_time)));
+    NotifyDebugMessage("  end_time = " + (end_time.is_null()
+                                              ? "null"
+                                              : base::TimeToISO8601(end_time)));
+  }
 
   if (!backend_) {
     NotifyDebugMessage(
@@ -452,8 +458,10 @@
   // via the constructor for efficiency (as recommended by the flat_set docs).
   // De-duplication is handled by the flat_set itself.
   *cache = KeywordSet(*keyword_accumulator);
-  NotifyDebugMessage("Cache construction complete:");
-  NotifyDebugMessage(GetDebugJSONForKeywordSet(*cache));
+  if (ShouldNotifyDebugMessage()) {
+    NotifyDebugMessage("Cache construction complete:");
+    NotifyDebugMessage(GetDebugJSONForKeywordSet(*cache));
+  }
 
   // Record keyword phrase & keyword counts for the appropriate cache.
   if (cache == &all_keywords_cache_) {
@@ -484,13 +492,15 @@
     QueryClustersCallback callback,
     std::vector<history::AnnotatedVisit> annotated_visits,
     base::Time continuation_end_time) const {
-  NotifyDebugMessage("HistoryClustersService::OnGotHistoryVisits()");
-  NotifyDebugMessage(base::StringPrintf("  annotated_visits.size() = %zu",
-                                        annotated_visits.size()));
-  NotifyDebugMessage("  continuation_end_time = " +
-                     (continuation_end_time.is_null()
-                          ? "null (i.e. exhausted history)"
-                          : base::TimeToISO8601(continuation_end_time)));
+  if (ShouldNotifyDebugMessage()) {
+    NotifyDebugMessage("HistoryClustersService::OnGotHistoryVisits()");
+    NotifyDebugMessage(base::StringPrintf("  annotated_visits.size() = %zu",
+                                          annotated_visits.size()));
+    NotifyDebugMessage("  continuation_end_time = " +
+                       (continuation_end_time.is_null()
+                            ? "null (i.e. exhausted history)"
+                            : base::TimeToISO8601(continuation_end_time)));
+  }
 
   base::UmaHistogramTimes(
       "Histogram.Clusters.Backend.QueryAnnotatedVisitsLatency",
@@ -502,10 +512,11 @@
     return;
   }
 
-  NotifyDebugMessage("  Visits JSON follows:");
-  NotifyDebugMessage(GetDebugJSONForVisits(annotated_visits));
-
-  NotifyDebugMessage("Calling backend_->GetClusters()");
+  if (ShouldNotifyDebugMessage()) {
+    NotifyDebugMessage("  Visits JSON follows:");
+    NotifyDebugMessage(GetDebugJSONForVisits(annotated_visits));
+    NotifyDebugMessage("Calling backend_->GetClusters()");
+  }
   base::UmaHistogramCounts1000("History.Clusters.Backend.NumVisitsToCluster",
                                static_cast<int>(annotated_visits.size()));
 
@@ -522,15 +533,16 @@
     base::TimeTicks cluster_start_time,
     QueryClustersCallback callback,
     std::vector<history::Cluster> clusters) const {
-  NotifyDebugMessage("HistoryClustersService::OnGotRawClusters()");
-
   base::UmaHistogramTimes("History.Clusters.Backend.GetClustersLatency",
                           base::TimeTicks::Now() - cluster_start_time);
   base::UmaHistogramCounts1000("History.Clusters.Backend.NumClustersReturned",
                                clusters.size());
 
-  NotifyDebugMessage("  Raw Clusters from Backend JSON follows:");
-  NotifyDebugMessage(GetDebugJSONForClusters(clusters));
+  if (ShouldNotifyDebugMessage()) {
+    NotifyDebugMessage("HistoryClustersService::OnGotRawClusters()");
+    NotifyDebugMessage("  Raw Clusters from Backend JSON follows:");
+    NotifyDebugMessage(GetDebugJSONForClusters(clusters));
+  }
 
   std::move(callback).Run(clusters, continuation_end_time);
 }
diff --git a/components/history_clusters/core/history_clusters_service.h b/components/history_clusters/core/history_clusters_service.h
index 3292c67..f502362 100644
--- a/components/history_clusters/core/history_clusters_service.h
+++ b/components/history_clusters/core/history_clusters_service.h
@@ -110,6 +110,9 @@
   void AddObserver(Observer* obs);
   void RemoveObserver(Observer* obs);
 
+  // Returns whether observers are registered to notify the debug messages.
+  bool ShouldNotifyDebugMessage() const;
+
   // Notifies the observers of a debug message being available.
   void NotifyDebugMessage(const std::string& message) const;
 
diff --git a/components/nacl/browser/nacl_browser.cc b/components/nacl/browser/nacl_browser.cc
index c817bf4..02aebb4 100644
--- a/components/nacl/browser/nacl_browser.cc
+++ b/components/nacl/browser/nacl_browser.cc
@@ -57,7 +57,7 @@
   else
     irt_name.append(FILE_PATH_LITERAL("x86_32"));
 
-#elif defined(ARCH_CPU_ARMEL)
+#elif defined(ARCH_CPU_ARM_FAMILY)
   irt_name.append(FILE_PATH_LITERAL("arm"));
 #elif defined(ARCH_CPU_MIPSEL)
   irt_name.append(FILE_PATH_LITERAL("mips32"));
diff --git a/components/paint_preview/OWNERS b/components/paint_preview/OWNERS
index ee7ed1e..c637e40 100644
--- a/components/paint_preview/OWNERS
+++ b/components/paint_preview/OWNERS
@@ -1,7 +1,6 @@
 ckitagawa@chromium.org
 fredmello@chromium.org
 yfriedman@chromium.org
-yashard@chromium.org
 
 # HarfBuzz subsetting integration
 per-file common/subset-font*=file://third_party/harfbuzz-ng/OWNERS
diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json
index badc734..a0d33cbd 100644
--- a/components/policy/resources/policy_templates.json
+++ b/components/policy/resources/policy_templates.json
@@ -19816,7 +19816,7 @@
       'tags': [],
       'desc': '''Setting the policy to Enabled allows policies associated with a <ph name="GOOGLE_WORKSPACE_PRODUCT_NAME">Google Workspace</ph> account to take precedence if they conflict with <ph name="CHROME_BROWSER_CLOUD_MANAGEMENT_NAME">Chrome Browser Cloud Management</ph> policies.
 
-      Only policies originating from secure users can be merged. A secure user is affiliated with the organization that manages their browser using <ph name="CHROME_BROWSER_CLOUD_MANAGEMENT_NAME">Chrome Browser Cloud Management</ph>. All other user-level policies will have default precedence.
+      Only policies originating from secure users can take precedence. A secure user is affiliated with the organization that manages their browser using <ph name="CHROME_BROWSER_CLOUD_MANAGEMENT_NAME">Chrome Browser Cloud Management</ph>. All other user-level policies will have default precedence.
 
       The policy can be combined with <ph name="POLICY_CLOUDPOLICYOVERRIDESPLATFORMPOLICY">CloudPolicyOverridesPlatformPolicy</ph>. If both policies are enabled, user cloud policies will also take precedence over conflicting platform policies.
 
diff --git a/components/policy/tools/generate_policy_source.py b/components/policy/tools/generate_policy_source.py
index 63170e1..6d0182fd 100755
--- a/components/policy/tools/generate_policy_source.py
+++ b/components/policy/tools/generate_policy_source.py
@@ -1088,10 +1088,10 @@
 
 #include <algorithm>
 #include <climits>
+#include <iterator>
 #include <memory>
 
 #include "base/check_op.h"
-#include "base/stl_util.h"  // base::size()
 #include "base/values.h"
 #include "build/branding_buildflags.h"
 #include "components/policy/core/common/policy_types.h"
@@ -1260,7 +1260,7 @@
   // Offsetting |it| from |begin| here obtains the index we're
   // looking for.
   size_t index = it - begin;
-  CHECK_LT(index, base::size(kChromePolicyDetails));
+  CHECK_LT(index, std::size(kChromePolicyDetails));
   return kChromePolicyDetails + index;
 ''')
   else:
diff --git a/components/policy/tools/generate_policy_source_test_data.py b/components/policy/tools/generate_policy_source_test_data.py
index 6a523b3..742b7d7 100644
--- a/components/policy/tools/generate_policy_source_test_data.py
+++ b/components/policy/tools/generate_policy_source_test_data.py
@@ -327,10 +327,10 @@
 
 #include <algorithm>
 #include <climits>
+#include <iterator>
 #include <memory>
 
 #include "base/check_op.h"
-#include "base/stl_util.h"  // base::size()
 #include "base/values.h"
 #include "build/branding_buildflags.h"
 #include "components/policy/core/common/policy_types.h"
@@ -437,7 +437,7 @@
   // Offsetting |it| from |begin| here obtains the index we're
   // looking for.
   size_t index = it - begin;
-  CHECK_LT(index, base::size(kChromePolicyDetails));
+  CHECK_LT(index, std::size(kChromePolicyDetails));
   return kChromePolicyDetails + index;
 }
 
diff --git a/components/safe_browsing/core/browser/realtime/url_lookup_service_base.cc b/components/safe_browsing/core/browser/realtime/url_lookup_service_base.cc
index a5af234f..fe620aa 100644
--- a/components/safe_browsing/core/browser/realtime/url_lookup_service_base.cc
+++ b/components/safe_browsing/core/browser/realtime/url_lookup_service_base.cc
@@ -315,8 +315,8 @@
   if (response.threat_info_size() > 0) {
     base::SequencedTaskRunnerHandle::Get()->PostTask(
         FROM_HERE, base::BindOnce(&VerdictCacheManager::CacheRealTimeUrlVerdict,
-                                  base::Unretained(cache_manager_), url,
-                                  response, base::Time::Now()));
+                                  cache_manager_->GetWeakPtr(), url, response,
+                                  base::Time::Now()));
   }
 }
 
diff --git a/components/safe_browsing/core/browser/realtime/url_lookup_service_unittest.cc b/components/safe_browsing/core/browser/realtime/url_lookup_service_unittest.cc
index 51f745f1..41d6d887 100644
--- a/components/safe_browsing/core/browser/realtime/url_lookup_service_unittest.cc
+++ b/components/safe_browsing/core/browser/realtime/url_lookup_service_unittest.cc
@@ -136,7 +136,9 @@
 
   void TearDown() override {
     cache_manager_.reset();
-    content_setting_map_->ShutdownOnUIThread();
+    if (content_setting_map_) {
+      content_setting_map_->ShutdownOnUIThread();
+    }
     rt_service_->Shutdown();
   }
 
@@ -1141,6 +1143,24 @@
   task_environment_.RunUntilIdle();
 }
 
+TEST_F(RealTimeUrlLookupServiceTest, TestShutdown_CacheManagerReset) {
+  GURL url("https://a.example.test/path1/path2");
+  // Post a task to cache_manager_ to cache the verdict.
+  MayBeCacheRealTimeUrlVerdict(url, RTLookupResponse::ThreatInfo::DANGEROUS,
+                               RTLookupResponse::ThreatInfo::SOCIAL_ENGINEERING,
+                               60, "a.example.test/path1/path2",
+                               RTLookupResponse::ThreatInfo::COVERING_MATCH);
+
+  // Shutdown and delete depending objects.
+  rt_service()->Shutdown();
+  cache_manager_.reset();
+  content_setting_map_->ShutdownOnUIThread();
+  content_setting_map_.reset();
+
+  // The task to cache_manager_ should be cancelled and not cause crash.
+  task_environment_.RunUntilIdle();
+}
+
 TEST_F(RealTimeUrlLookupServiceTest,
        TestShutdown_SendRequestNotCalledOnShutdown) {
   // Never send the request if shutdown is triggered before OnGetAccessToken().
diff --git a/components/security_interstitials/content/ssl_error_handler.cc b/components/security_interstitials/content/ssl_error_handler.cc
index 9707131..2814eca 100644
--- a/components/security_interstitials/content/ssl_error_handler.cc
+++ b/components/security_interstitials/content/ssl_error_handler.cc
@@ -685,6 +685,17 @@
     return;
   }
 
+  bool is_captive_portal_login_tab = false;
+#if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION)
+  // Check whether this SSL Error is for a Captive Portal Login tab. If so, we
+  // must not show another Captive Portal interstitial because doing so will
+  // remove a user's ability to "ignore" the cert error to log onto the portal.
+  captive_portal::CaptivePortalTabHelper* captive_portal_tab_helper =
+      captive_portal::CaptivePortalTabHelper::FromWebContents(web_contents());
+  if (captive_portal_tab_helper && captive_portal_tab_helper->IsLoginTab())
+    is_captive_portal_login_tab = true;
+#endif
+
   // Ideally, a captive portal interstitial should only be displayed if the only
   // SSL error is a name mismatch error. However, captive portal detector always
   // opens a new tab if it detects a portal ignoring the types of SSL errors. To
@@ -696,8 +707,11 @@
     delegate_->ReportNetworkConnectivity(
         g_config.Pointer()->report_network_connectivity_callback());
     RecordUMA(OS_REPORTS_CAPTIVE_PORTAL);
-    ShowCaptivePortalInterstitial(GURL());
-    return;
+
+    if (!is_captive_portal_login_tab) {
+      ShowCaptivePortalInterstitial(GURL());
+      return;
+    }
   }
 
   const bool only_error_is_name_mismatch =
@@ -712,7 +726,8 @@
         g_config.Pointer()->report_network_connectivity_callback());
 
     if (base::FeatureList::IsEnabled(kCaptivePortalCertificateList) &&
-        g_config.Pointer()->IsKnownCaptivePortalCertificate(ssl_info_)) {
+        g_config.Pointer()->IsKnownCaptivePortalCertificate(ssl_info_) &&
+        !is_captive_portal_login_tab) {
       RecordUMA(CAPTIVE_PORTAL_CERT_FOUND);
       ShowCaptivePortalInterstitial(GURL());
       return;
@@ -745,9 +760,10 @@
       RecordUMA(WWW_MISMATCH_FOUND_IN_SAN);
 
       // Show the SSL interstitial if |CERT_STATUS_COMMON_NAME_INVALID| is not
-      // the only error. Need not check for captive portal in this case.
-      // (See the comment below).
-      if (!only_error_is_name_mismatch) {
+      // the only error. Need not check for captive portal in this case (See
+      // the comment below). Also show the SSL interstitial if we're already
+      // on a login tab launched by the Captive Portal interstitial.
+      if (!only_error_is_name_mismatch || is_captive_portal_login_tab) {
         ShowSSLInterstitial();
         return;
       }
@@ -772,13 +788,11 @@
   subscription_ = captive_portal_service_->RegisterCallback(
       base::BindRepeating(&SSLErrorHandler::Observe, base::Unretained(this)));
 
-  captive_portal::CaptivePortalTabHelper* captive_portal_tab_helper =
-      captive_portal::CaptivePortalTabHelper::FromWebContents(web_contents());
   if (captive_portal_tab_helper) {
     captive_portal_tab_helper->OnSSLCertError(ssl_info_);
   }
 
-  if (IsCaptivePortalInterstitialEnabled()) {
+  if (IsCaptivePortalInterstitialEnabled() && !is_captive_portal_login_tab) {
     delegate_->CheckForCaptivePortal();
     timer_.Start(FROM_HERE, g_config.Pointer()->interstitial_delay(), this,
                  &SSLErrorHandler::ShowSSLInterstitial);
diff --git a/components/soda/BUILD.gn b/components/soda/BUILD.gn
index bc859e7..1912b23 100644
--- a/components/soda/BUILD.gn
+++ b/components/soda/BUILD.gn
@@ -38,7 +38,8 @@
 
     deps += [
       "//ash/constants",
-      "//chromeos/dbus/dlcservice",
+      "//chromeos/dbus/dlcservice:dlcservice",
+      "//chromeos/dbus/dlcservice:dlcservice_proto",
       "//ui/base",
     ]
   }
diff --git a/components/soda/soda_installer_impl_chromeos.cc b/components/soda/soda_installer_impl_chromeos.cc
index 7582987c..c36bda0 100644
--- a/components/soda/soda_installer_impl_chromeos.cc
+++ b/components/soda/soda_installer_impl_chromeos.cc
@@ -8,6 +8,7 @@
 #include "base/feature_list.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/numerics/safe_conversions.h"
+#include "chromeos/dbus/dlcservice/dlcservice.pb.h"
 #include "chromeos/dbus/dlcservice/dlcservice_client.h"
 #include "components/live_caption/pref_names.h"
 #include "components/prefs/pref_service.h"
@@ -52,8 +53,10 @@
   soda_progress_ = 0.0;
 
   // Install SODA DLC.
+  dlcservice::InstallRequest install_request;
+  install_request.set_id(kSodaDlcName);
   chromeos::DlcserviceClient::Get()->Install(
-      kSodaDlcName,
+      install_request,
       base::BindOnce(&SodaInstallerImplChromeOS::OnSodaInstalled,
                      base::Unretained(this), base::Time::Now()),
       base::BindRepeating(&SodaInstallerImplChromeOS::OnSodaProgress,
@@ -78,8 +81,10 @@
 
   language_pack_progress_.insert({LanguageCode::kEnUs, 0.0});
 
+  dlcservice::InstallRequest install_request;
+  install_request.set_id(kSodaEnglishUsDlcName);
   chromeos::DlcserviceClient::Get()->Install(
-      kSodaEnglishUsDlcName,
+      install_request,
       base::BindOnce(&SodaInstallerImplChromeOS::OnLanguageInstalled,
                      base::Unretained(this), LanguageCode::kEnUs,
                      base::Time::Now()),
diff --git a/components/sync/base/DEPS b/components/sync/base/DEPS
index 2483803f..395d1e5 100644
--- a/components/sync/base/DEPS
+++ b/components/sync/base/DEPS
@@ -8,4 +8,7 @@
   "+net/base/net_errors.h",
   "+net/http/http_status_code.h",
   "+ui/base",
+  # TODO(crbug.com/1300525): Remove this once ordinal_unittest.cc is moved to
+  # model/.
+  "+components/sync/model/string_ordinal.h",
 ]
diff --git a/components/sync/base/ordinal_unittest.cc b/components/sync/base/ordinal_unittest.cc
index 3ad7b661..5e782fcb 100644
--- a/components/sync/base/ordinal_unittest.cc
+++ b/components/sync/base/ordinal_unittest.cc
@@ -2,125 +2,63 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+// TODO(crbug.com/1300525): Move this file to model/ and remove this include
+// (it's kept now to avoid a presubmit error). None of that is done now to allow
+// for a friendlier diff.
 #include "components/sync/base/ordinal.h"
 
 #include <cctype>
 #include <vector>
 
 #include "base/rand_util.h"
+#include "components/sync/model/string_ordinal.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace syncer {
 
 namespace {
 
-struct TestOrdinalTraits {
-  static const uint8_t kZeroDigit = '0';
-  static const uint8_t kMaxDigit = '3';
-  static const size_t kMinLength = 1;
-};
-
-struct LongOrdinalTraits {
-  static const uint8_t kZeroDigit = '0';
-  static const uint8_t kMaxDigit = '9';
-  static const size_t kMinLength = 5;
-};
-
-struct LargeOrdinalTraits {
-  static const uint8_t kZeroDigit = 0;
-  static const uint8_t kMaxDigit = UINT8_MAX;
-  static const size_t kMinLength = 1;
-};
-
-using TestOrdinal = Ordinal<TestOrdinalTraits>;
-using LongOrdinal = Ordinal<LongOrdinalTraits>;
-using LargeOrdinal = Ordinal<LargeOrdinalTraits>;
-
-static_assert(TestOrdinal::kZeroDigit == '0',
-              "incorrect TestOrdinal zero digit");
-static_assert(TestOrdinal::kOneDigit == '1', "incorrect TestOrdinal one digit");
-static_assert(TestOrdinal::kMidDigit == '2', "incorrect TestOrdinal min digit");
-static_assert(TestOrdinal::kMaxDigit == '3', "incorrect TestOrdinal max digit");
-static_assert(TestOrdinal::kMidDigitValue == 2,
-              "incorrect TestOrdinal mid digit value");
-static_assert(TestOrdinal::kMaxDigitValue == 3,
-              "incorrect TestOrdinal max digit value");
-static_assert(TestOrdinal::kRadix == 4, "incorrect TestOrdinal radix");
-
-static_assert(LongOrdinal::kZeroDigit == '0',
-              "incorrect LongOrdinal zero digit");
-static_assert(LongOrdinal::kOneDigit == '1', "incorrect LongOrdinal one digit");
-static_assert(LongOrdinal::kMidDigit == '5', "incorrect LongOrdinal mid digit");
-static_assert(LongOrdinal::kMaxDigit == '9', "incorrect LongOrdinal max digit");
-static_assert(LongOrdinal::kMidDigitValue == 5,
-              "incorrect LongOrdinal mid digit value");
-static_assert(LongOrdinal::kMaxDigitValue == 9,
-              "incorrect LongOrdinal max digit value");
-static_assert(LongOrdinal::kRadix == 10, "incorrect LongOrdinal radix");
-
-static_assert(static_cast<char>(LargeOrdinal::kZeroDigit) == '\x00',
-              "incorrect LargeOrdinal zero digit");
-static_assert(static_cast<char>(LargeOrdinal::kOneDigit) == '\x01',
-              "incorrect LargeOrdinal one digit");
-static_assert(static_cast<char>(LargeOrdinal::kMidDigit) == '\x80',
-              "incorrect LargeOrdinal mid digit");
-static_assert(static_cast<char>(LargeOrdinal::kMaxDigit) == '\xff',
-              "incorrect LargeOrdinal max digit");
-static_assert(LargeOrdinal::kMidDigitValue == 128,
-              "incorrect LargeOrdinal mid digit value");
-static_assert(LargeOrdinal::kMaxDigitValue == 255,
-              "incorrect LargeOrdinal max digit value");
-static_assert(LargeOrdinal::kRadix == 256, "incorrect LargeOrdinal radix");
-
-// Create Ordinals that satisfy all but one criterion for validity.
+// Create StringOrdinals that satisfy all but one criterion for validity.
 // IsValid() should return false for all of them.
-TEST(Ordinal, Invalid) {
+TEST(StringOrdinalTest, Invalid) {
   // Length criterion.
-  EXPECT_FALSE(TestOrdinal(std::string()).IsValid());
-  EXPECT_FALSE(LongOrdinal("0001").IsValid());
+  EXPECT_FALSE(StringOrdinal(std::string()).IsValid());
 
-  const char kBeforeZero[] = {'0' - 1, '\0'};
-  const char kAfterNine[] = {'9' + 1, '\0'};
+  const char kBeforeA[] = {'a' - 1, '\0'};
+  const char kAfterZ[] = {'z' + 1, '\0'};
 
   // Character criterion.
-  EXPECT_FALSE(TestOrdinal(kBeforeZero).IsValid());
-  EXPECT_FALSE(TestOrdinal("4").IsValid());
-  EXPECT_FALSE(LongOrdinal(std::string("0000") + kBeforeZero).IsValid());
-  EXPECT_FALSE(LongOrdinal(std::string("0000") + kAfterNine).IsValid());
+  EXPECT_FALSE(StringOrdinal(kBeforeA).IsValid());
+  EXPECT_FALSE(StringOrdinal(kAfterZ).IsValid());
 
   // Zero criterion.
-  EXPECT_FALSE(TestOrdinal("0").IsValid());
-  EXPECT_FALSE(TestOrdinal("00000").IsValid());
+  EXPECT_FALSE(StringOrdinal("a").IsValid());
 
   // Trailing zero criterion.
-  EXPECT_FALSE(TestOrdinal("10").IsValid());
-  EXPECT_FALSE(TestOrdinal("111110").IsValid());
+  EXPECT_FALSE(StringOrdinal("ba").IsValid());
 }
 
-// Create Ordinals that satisfy all criteria for validity.
+// Create StringOrdinals that satisfy all criteria for validity.
 // IsValid() should return true for all of them.
-TEST(Ordinal, Valid) {
+TEST(StringOrdinalTest, Valid) {
   // Length criterion.
-  EXPECT_TRUE(TestOrdinal("1").IsValid());
-  EXPECT_TRUE(LongOrdinal("10000").IsValid());
+  EXPECT_TRUE(StringOrdinal("b").IsValid());
 }
 
-// Create Ordinals from CreateInitialOrdinal.  They should be valid
+// Create StringOrdinals from CreateInitialOrdinal.  They should be valid
 // and close to the middle of the range.
-TEST(Ordinal, CreateInitialOrdinal) {
-  const TestOrdinal& ordinal1 = TestOrdinal::CreateInitialOrdinal();
-  const LongOrdinal& ordinal2 = LongOrdinal::CreateInitialOrdinal();
-  ASSERT_TRUE(ordinal1.IsValid());
-  ASSERT_TRUE(ordinal2.IsValid());
-  EXPECT_TRUE(ordinal1.Equals(TestOrdinal("2")));
-  EXPECT_TRUE(ordinal2.Equals(LongOrdinal("50000")));
+TEST(StringOrdinalTest, CreateInitialOrdinal) {
+  const StringOrdinal& ordinal = StringOrdinal::CreateInitialOrdinal();
+  ASSERT_TRUE(ordinal.IsValid());
+  // "n" is the midpoint letter of the alphabet.
+  EXPECT_TRUE(ordinal.Equals(StringOrdinal("n")));
 }
 
-// Create an invalid and a valid Ordinal.  EqualsOrBothInvalid should
+// Create an invalid and a valid StringOrdinal.  EqualsOrBothInvalid should
 // return true if called reflexively and false otherwise.
-TEST(Ordinal, EqualsOrBothInvalid) {
-  const TestOrdinal& valid_ordinal = TestOrdinal::CreateInitialOrdinal();
-  const TestOrdinal invalid_ordinal;
+TEST(StringOrdinalTest, EqualsOrBothInvalid) {
+  const StringOrdinal& valid_ordinal = StringOrdinal::CreateInitialOrdinal();
+  const StringOrdinal invalid_ordinal;
 
   EXPECT_TRUE(valid_ordinal.EqualsOrBothInvalid(valid_ordinal));
   EXPECT_TRUE(invalid_ordinal.EqualsOrBothInvalid(invalid_ordinal));
@@ -128,12 +66,12 @@
   EXPECT_FALSE(valid_ordinal.EqualsOrBothInvalid(invalid_ordinal));
 }
 
-// Create three Ordinals in order.  LessThan should return values
+// Create three StringOrdinals in order.  LessThan should return values
 // consistent with that order.
-TEST(Ordinal, LessThan) {
-  const TestOrdinal small_ordinal("1");
-  const TestOrdinal middle_ordinal("2");
-  const TestOrdinal big_ordinal("3");
+TEST(StringOrdinalTest, LessThan) {
+  const StringOrdinal small_ordinal("b");
+  const StringOrdinal middle_ordinal("c");
+  const StringOrdinal big_ordinal("d");
 
   EXPECT_FALSE(small_ordinal.LessThan(small_ordinal));
   EXPECT_TRUE(small_ordinal.LessThan(middle_ordinal));
@@ -148,22 +86,12 @@
   EXPECT_FALSE(big_ordinal.LessThan(big_ordinal));
 }
 
-// Create two single-digit ordinals with byte values 0 and 255.  The
-// former should compare as less than the latter, even though the
-// native char type may be signed.
-TEST(Ordinal, LessThanLarge) {
-  const LargeOrdinal small_ordinal("\x01");
-  const LargeOrdinal big_ordinal("\xff");
-
-  EXPECT_TRUE(small_ordinal.LessThan(big_ordinal));
-}
-
-// Create three Ordinals in order.  GreaterThan should return values
+// Create three StringOrdinals in order.  GreaterThan should return values
 // consistent with that order.
-TEST(Ordinal, GreaterThan) {
-  const LongOrdinal small_ordinal("10000");
-  const LongOrdinal middle_ordinal("55555");
-  const LongOrdinal big_ordinal("99999");
+TEST(StringOrdinalTest, GreaterThan) {
+  const StringOrdinal small_ordinal("b");
+  const StringOrdinal middle_ordinal("c");
+  const StringOrdinal big_ordinal("d");
 
   EXPECT_FALSE(small_ordinal.GreaterThan(small_ordinal));
   EXPECT_FALSE(small_ordinal.GreaterThan(middle_ordinal));
@@ -178,11 +106,11 @@
   EXPECT_FALSE(big_ordinal.GreaterThan(big_ordinal));
 }
 
-// Create two valid Ordinals.  Equals should return true only when
+// Create two valid StringOrdinals.  Equals should return true only when
 // called reflexively.
-TEST(Ordinal, Equals) {
-  const TestOrdinal ordinal1("1");
-  const TestOrdinal ordinal2("2");
+TEST(StringOrdinalTest, Equals) {
+  const StringOrdinal ordinal1("b");
+  const StringOrdinal ordinal2("c");
 
   EXPECT_TRUE(ordinal1.Equals(ordinal1));
   EXPECT_FALSE(ordinal1.Equals(ordinal2));
@@ -193,10 +121,9 @@
 
 // Create some valid ordinals from some byte strings.
 // ToInternalValue() should return the original byte string.
-TEST(OrdinalTest, ToInternalValue) {
-  EXPECT_EQ("2", TestOrdinal("2").ToInternalValue());
-  EXPECT_EQ("12345", LongOrdinal("12345").ToInternalValue());
-  EXPECT_EQ("\1\2\3\4\5", LargeOrdinal("\1\2\3\4\5").ToInternalValue());
+TEST(StringOrdinalTest, ToInternalValue) {
+  EXPECT_EQ("c", StringOrdinal("c").ToInternalValue());
+  EXPECT_EQ("bcdef", StringOrdinal("bcdef").ToInternalValue());
 }
 
 bool IsNonEmptyPrintableString(const std::string& str) {
@@ -211,24 +138,23 @@
 
 // Create some invalid/valid ordinals.  ToDebugString() should always
 // return a non-empty printable string.
-TEST(OrdinalTest, ToDebugString) {
-  EXPECT_TRUE(IsNonEmptyPrintableString(TestOrdinal().ToDebugString()));
+TEST(StringOrdinalTest, ToDebugString) {
+  EXPECT_TRUE(IsNonEmptyPrintableString(StringOrdinal().ToDebugString()));
+  EXPECT_TRUE(IsNonEmptyPrintableString(
+      StringOrdinal("invalid string").ToDebugString()));
+  EXPECT_TRUE(IsNonEmptyPrintableString(StringOrdinal("c").ToDebugString()));
   EXPECT_TRUE(
-      IsNonEmptyPrintableString(TestOrdinal("invalid string").ToDebugString()));
-  EXPECT_TRUE(IsNonEmptyPrintableString(TestOrdinal("2").ToDebugString()));
-  EXPECT_TRUE(IsNonEmptyPrintableString(LongOrdinal("12345").ToDebugString()));
-  EXPECT_TRUE(
-      IsNonEmptyPrintableString(LargeOrdinal("\1\2\3\4\5").ToDebugString()));
+      IsNonEmptyPrintableString(StringOrdinal("bcdef").ToDebugString()));
 }
 
-// Create three Ordinals in order.  LessThanFn should return values
+// Create three StringOrdinals in order.  LessThanFn should return values
 // consistent with that order.
-TEST(Ordinal, LessThanFn) {
-  const TestOrdinal small_ordinal("1");
-  const TestOrdinal middle_ordinal("2");
-  const TestOrdinal big_ordinal("3");
+TEST(StringOrdinalTest, LessThanFn) {
+  const StringOrdinal small_ordinal("b");
+  const StringOrdinal middle_ordinal("c");
+  const StringOrdinal big_ordinal("d");
 
-  const TestOrdinal::LessThanFn less_than;
+  const StringOrdinal::LessThanFn less_than;
 
   EXPECT_FALSE(less_than(small_ordinal, small_ordinal));
   EXPECT_TRUE(less_than(small_ordinal, middle_ordinal));
@@ -243,91 +169,78 @@
   EXPECT_FALSE(less_than(big_ordinal, big_ordinal));
 }
 
-template <typename Traits>
 std::string GetBetween(const std::string& ordinal_string1,
                        const std::string& ordinal_string2) {
-  const Ordinal<Traits> ordinal1(ordinal_string1);
-  const Ordinal<Traits> ordinal2(ordinal_string2);
-  const Ordinal<Traits> between1 = ordinal1.CreateBetween(ordinal2);
-  const Ordinal<Traits> between2 = ordinal2.CreateBetween(ordinal1);
+  const StringOrdinal ordinal1(ordinal_string1);
+  const StringOrdinal ordinal2(ordinal_string2);
+  const StringOrdinal between1 = ordinal1.CreateBetween(ordinal2);
+  const StringOrdinal between2 = ordinal2.CreateBetween(ordinal1);
   EXPECT_TRUE(between1.Equals(between2));
   return between1.ToInternalValue();
 }
 
-// Create some Ordinals from single-digit strings.  Given two strings
-// from this set, CreateBetween should return an Ordinal roughly between
+// Create some StringOrdinals from single-digit strings.  Given two strings
+// from this set, CreateBetween should return a StringOrdinal roughly between
 // them that are also single-digit when possible.
-TEST(Ordinal, CreateBetweenSingleDigit) {
-  EXPECT_EQ("2", GetBetween<TestOrdinal>("1", "3"));
-  EXPECT_EQ("12", GetBetween<TestOrdinal>("1", "2"));
-  EXPECT_EQ("22", GetBetween<TestOrdinal>("2", "3"));
+TEST(StringOrdinalTest, CreateBetweenSingleDigit) {
+  EXPECT_EQ("c", GetBetween("b", "d"));
+  EXPECT_EQ("bn", GetBetween("b", "c"));
+  EXPECT_EQ("cn", GetBetween("c", "d"));
 }
 
-// Create some Ordinals from strings of various lengths.  Given two
-// strings from this set, CreateBetween should return an Ordinal roughly
+// Create some StringOrdinals from strings of various lengths.  Given two
+// strings from this set, CreateBetween should return an StringOrdinal roughly
 // between them that have as few digits as possible.
-TEST(Ordinal, CreateBetweenDifferentLengths) {
-  EXPECT_EQ("102", GetBetween<TestOrdinal>("1", "11"));
-  EXPECT_EQ("2", GetBetween<TestOrdinal>("1", "31"));
-  EXPECT_EQ("132", GetBetween<TestOrdinal>("13", "2"));
-  EXPECT_EQ("2", GetBetween<TestOrdinal>("10001", "3"));
-  EXPECT_EQ("20000", GetBetween<LongOrdinal>("10001", "30000"));
-  EXPECT_EQ("2", GetBetween<TestOrdinal>("10002", "3"));
-  EXPECT_EQ("20001", GetBetween<LongOrdinal>("10002", "30000"));
-  EXPECT_EQ("2", GetBetween<TestOrdinal>("1", "30002"));
-  EXPECT_EQ("20001", GetBetween<LongOrdinal>("10000", "30002"));
+TEST(StringOrdinalTest, CreateBetweenDifferentLengths) {
+  EXPECT_EQ("ban", GetBetween("b", "bb"));
+  EXPECT_EQ("c", GetBetween("b", "db"));
+  EXPECT_EQ("bo", GetBetween("bd", "c"));
+  EXPECT_EQ("c", GetBetween("baaab", "d"));
+  EXPECT_EQ("c", GetBetween("baaac", "d"));
+  EXPECT_EQ("c", GetBetween("b", "daaac"));
 }
 
-// Create some Ordinals specifically designed to trigger overflow
+// Create some StringOrdinals specifically designed to trigger overflow
 // cases.  Given two strings from this set, CreateBetween should
-// return an Ordinal roughly between them that have as few digits as
+// return a StringOrdinal roughly between them that have as few digits as
 // possible.
-TEST(Ordinal, CreateBetweenOverflow) {
-  EXPECT_EQ("03", GetBetween<TestOrdinal>("01", "11"));
-  EXPECT_EQ("13", GetBetween<TestOrdinal>("11", "21"));
-  EXPECT_EQ("113", GetBetween<TestOrdinal>("111", "121"));
-  EXPECT_EQ("2", GetBetween<TestOrdinal>("001", "333"));
-  EXPECT_EQ("31", GetBetween<TestOrdinal>("222", "333"));
-  EXPECT_EQ("3", GetBetween<TestOrdinal>("201", "333"));
-  EXPECT_EQ("2", GetBetween<TestOrdinal>("003", "333"));
-  EXPECT_EQ("2", GetBetween<TestOrdinal>("2223", "1113"));
+TEST(StringOrdinalTest, CreateBetweenOverflow) {
+  EXPECT_EQ("ao", GetBetween("ab", "bb"));
+  EXPECT_EQ("bo", GetBetween("bb", "cb"));
+  EXPECT_EQ("bbo", GetBetween("bbb", "bcb"));
+  EXPECT_EQ("bo", GetBetween("aab", "ddd"));
+  EXPECT_EQ("cpp", GetBetween("ccc", "ddd"));
+  EXPECT_EQ("co", GetBetween("cab", "ddd"));
+  EXPECT_EQ("bo", GetBetween("aad", "ddd"));
+  EXPECT_EQ("boo", GetBetween("cccd", "bbbd"));
 }
 
-// Create some Ordinals specifically designed to trigger digit
-// overflow cases.  Given two strings from this set, CreateBetween
-// should return an Ordinal roughly between them that have as few digits
-// as possible.
-TEST(Ordinal, CreateBetweenOverflowLarge) {
-  EXPECT_EQ("\x80", GetBetween<LargeOrdinal>("\x01\xff", "\xff\xff"));
-  EXPECT_EQ("\xff\xfe\x80", GetBetween<LargeOrdinal>("\xff\xfe", "\xff\xff"));
+// Create some StringOrdinals.  CreateBefore should return a StringOrdinal
+// roughly halfway towards "a".
+TEST(StringOrdinalTest, CreateBefore) {
+  EXPECT_EQ("an", StringOrdinal("b").CreateBefore().ToInternalValue());
+  EXPECT_EQ("ao", StringOrdinal("bb").CreateBefore().ToInternalValue());
+  EXPECT_EQ("ao", StringOrdinal("bc").CreateBefore().ToInternalValue());
+  EXPECT_EQ("ap", StringOrdinal("bd").CreateBefore().ToInternalValue());
 }
 
-// Create some Ordinals.  CreateBefore should return an Ordinal
-// roughly halfway towards 0.
-TEST(Ordinal, CreateBefore) {
-  EXPECT_EQ("02", TestOrdinal("1").CreateBefore().ToInternalValue());
-  EXPECT_EQ("03", TestOrdinal("11").CreateBefore().ToInternalValue());
-  EXPECT_EQ("03", TestOrdinal("12").CreateBefore().ToInternalValue());
-  EXPECT_EQ("1", TestOrdinal("13").CreateBefore().ToInternalValue());
+// Create some StringOrdinals.  CreateAfter should return a StringOrdinal
+// roughly halfway towards "a".
+TEST(StringOrdinalTest, CreateAfter) {
+  EXPECT_EQ("o", StringOrdinal("d").CreateAfter().ToInternalValue());
+  EXPECT_EQ("on", StringOrdinal("dc").CreateAfter().ToInternalValue());
+  EXPECT_EQ("ooon", StringOrdinal("dddc").CreateAfter().ToInternalValue());
+  EXPECT_EQ("o", StringOrdinal("cc").CreateAfter().ToInternalValue());
+  EXPECT_EQ("o", StringOrdinal("cd").CreateAfter().ToInternalValue());
 }
 
-// Create some Ordinals.  CreateAfter should return an Ordinal
-// roughly halfway towards 0.
-TEST(Ordinal, CreateAfter) {
-  EXPECT_EQ("31", TestOrdinal("3").CreateAfter().ToInternalValue());
-  EXPECT_EQ("322", TestOrdinal("32").CreateAfter().ToInternalValue());
-  EXPECT_EQ("33322", TestOrdinal("3332").CreateAfter().ToInternalValue());
-  EXPECT_EQ("3", TestOrdinal("22").CreateAfter().ToInternalValue());
-  EXPECT_EQ("3", TestOrdinal("23").CreateAfter().ToInternalValue());
-}
-
-// Create two valid Ordinals.  EqualsFn should return true only when
+// Create two valid StringOrdinals.  EqualsFn should return true only when
 // called reflexively.
-TEST(Ordinal, EqualsFn) {
-  const TestOrdinal ordinal1("1");
-  const TestOrdinal ordinal2("2");
+TEST(StringOrdinalTest, EqualsFn) {
+  const StringOrdinal ordinal1("b");
+  const StringOrdinal ordinal2("c");
 
-  const TestOrdinal::EqualsFn equals;
+  const StringOrdinal::EqualsFn equals;
 
   EXPECT_TRUE(equals(ordinal1, ordinal1));
   EXPECT_FALSE(equals(ordinal1, ordinal2));
@@ -336,25 +249,25 @@
   EXPECT_TRUE(equals(ordinal2, ordinal2));
 }
 
-// Create some Ordinals and shuffle them.  Sorting them using
+// Create some StringOrdinals and shuffle them.  Sorting them using
 // LessThanFn should produce the correct order.
-TEST(Ordinal, Sort) {
-  const LongOrdinal ordinal1("12345");
-  const LongOrdinal ordinal2("54321");
-  const LongOrdinal ordinal3("87654");
-  const LongOrdinal ordinal4("98765");
+TEST(StringOrdinalTest, Sort) {
+  const StringOrdinal ordinal1("bcefg");
+  const StringOrdinal ordinal2("gfecb");
+  const StringOrdinal ordinal3("ihgfe");
+  const StringOrdinal ordinal4("jihgf");
 
-  std::vector<LongOrdinal> sorted_ordinals;
+  std::vector<StringOrdinal> sorted_ordinals;
   sorted_ordinals.push_back(ordinal1);
   sorted_ordinals.push_back(ordinal2);
   sorted_ordinals.push_back(ordinal3);
   sorted_ordinals.push_back(ordinal4);
 
-  std::vector<LongOrdinal> ordinals = sorted_ordinals;
+  std::vector<StringOrdinal> ordinals = sorted_ordinals;
   base::RandomShuffle(ordinals.begin(), ordinals.end());
-  std::sort(ordinals.begin(), ordinals.end(), LongOrdinal::LessThanFn());
+  std::sort(ordinals.begin(), ordinals.end(), StringOrdinal::LessThanFn());
   EXPECT_TRUE(std::equal(ordinals.begin(), ordinals.end(),
-                         sorted_ordinals.begin(), LongOrdinal::EqualsFn()));
+                         sorted_ordinals.begin(), StringOrdinal::EqualsFn()));
 }
 
 }  // namespace
diff --git a/components/sync/model/client_tag_based_model_type_processor.cc b/components/sync/model/client_tag_based_model_type_processor.cc
index 1832568..b3bb0d37 100644
--- a/components/sync/model/client_tag_based_model_type_processor.cc
+++ b/components/sync/model/client_tag_based_model_type_processor.cc
@@ -429,7 +429,7 @@
       metadata_change_list->ClearMetadata(entity->storage_key());
       entity_tracker_->UpdateOrOverrideStorageKey(data->client_tag_hash,
                                                   storage_key);
-      entity->MakeLocalChange(std::move(data));
+      entity->RecordLocalUpdate(std::move(data));
     } else {
       if (data->creation_time.is_null())
         data->creation_time = base::Time::Now();
@@ -441,7 +441,7 @@
     // Ignore changes that don't actually change anything.
     return;
   } else {
-    entity->MakeLocalChange(std::move(data));
+    entity->RecordLocalUpdate(std::move(data));
   }
 
   DCHECK(entity->IsUnsynced());
@@ -469,7 +469,7 @@
     return;
   }
 
-  if (entity->Delete())
+  if (entity->RecordLocalDeletion())
     metadata_change_list->UpdateMetadata(storage_key, entity->metadata());
   else
     RemoveEntity(entity->storage_key(), metadata_change_list);
@@ -899,7 +899,7 @@
     ProcessorEntity* entity = entity_tracker_->AddRemote(
         storage_key, update.entity, update.response_version);
     // TODO(crbug.com/1296159): Remove once create flow is refactored.
-    entity->RecordAcceptedUpdate(update);
+    entity->RecordAcceptedRemoteUpdate(update);
     entity_data.push_back(
         EntityChange::CreateAdd(storage_key, std::move(update.entity)));
     if (!storage_key.empty())
diff --git a/components/sync/model/client_tag_based_remote_update_handler.cc b/components/sync/model/client_tag_based_remote_update_handler.cc
index 557cccf..94082824 100644
--- a/components/sync/model/client_tag_based_remote_update_handler.cc
+++ b/components/sync/model/client_tag_based_remote_update_handler.cc
@@ -163,7 +163,7 @@
     DCHECK(!data.is_deleted());
     entity = CreateEntity(data, update.response_version);
     // TODO(crbug.com/1296159): Remove this call once create flow is updated.
-    entity->RecordAcceptedUpdate(update);
+    entity->RecordAcceptedRemoteUpdate(update);
     entity_changes->push_back(EntityChange::CreateAdd(
         entity->storage_key(), std::move(update.entity)));
   } else if (entity->IsUnsynced()) {
@@ -174,15 +174,15 @@
     // Remote deletion. Note that the local data cannot be already deleted,
     // because it would have been treated as a conflict earlier above.
     DCHECK(!entity->metadata().is_deleted());
-    entity->RecordAcceptedUpdate(update);
+    entity->RecordAcceptedRemoteUpdate(update);
     entity_changes->push_back(
         EntityChange::CreateDelete(entity->storage_key()));
   } else if (entity->MatchesData(data)) {
     // Remote update that is a no-op and can be ignored.
-    entity->RecordIgnoredUpdate(update);
+    entity->RecordIgnoredRemoteUpdate(update);
   } else {
     // Remote update.
-    entity->RecordAcceptedUpdate(update);
+    entity->RecordAcceptedRemoteUpdate(update);
     entity_changes->push_back(EntityChange::CreateUpdate(
         entity->storage_key(), std::move(update.entity)));
   }
@@ -238,13 +238,13 @@
   switch (resolution_type) {
     case ConflictResolution::kChangesMatch:
       // Record the update and squash the pending commit.
-      entity->RecordForcedUpdate(update);
+      entity->RecordForcedRemoteUpdate(update);
       break;
     case ConflictResolution::kUseLocal:
     case ConflictResolution::kIgnoreRemoteEncryption:
       // Record that we received the update from the server but leave the
       // pending commit intact.
-      entity->RecordIgnoredUpdate(update);
+      entity->RecordIgnoredRemoteUpdate(update);
       break;
     case ConflictResolution::kUseRemote:
     case ConflictResolution::kIgnoreLocalEncryption:
@@ -252,11 +252,11 @@
       if (update.entity.is_deleted()) {
         DCHECK(!entity->metadata().is_deleted());
         // Squash the pending commit.
-        entity->RecordForcedUpdate(update);
+        entity->RecordForcedRemoteUpdate(update);
         changes->push_back(EntityChange::CreateDelete(entity->storage_key()));
       } else if (!entity->metadata().is_deleted()) {
         // Squash the pending commit.
-        entity->RecordForcedUpdate(update);
+        entity->RecordForcedRemoteUpdate(update);
         changes->push_back(EntityChange::CreateUpdate(
             entity->storage_key(), std::move(update.entity)));
       } else {
@@ -268,7 +268,7 @@
           DCHECK(entity->storage_key().empty());
         }
         // Squash the pending commit.
-        entity->RecordForcedUpdate(update);
+        entity->RecordForcedRemoteUpdate(update);
         changes->push_back(EntityChange::CreateAdd(entity->storage_key(),
                                                    std::move(update.entity)));
       }
diff --git a/components/sync/model/processor_entity.cc b/components/sync/model/processor_entity.cc
index 16bf800..0c560e1 100644
--- a/components/sync/model/processor_entity.cc
+++ b/components/sync/model/processor_entity.cc
@@ -147,7 +147,8 @@
   return metadata_.server_version() >= update_version;
 }
 
-void ProcessorEntity::RecordIgnoredUpdate(const UpdateResponseData& update) {
+void ProcessorEntity::RecordIgnoredRemoteUpdate(
+    const UpdateResponseData& update) {
   DCHECK(metadata_.server_id().empty() ||
          metadata_.server_id() == update.entity.id);
   metadata_.set_server_id(update.entity.id);
@@ -163,9 +164,10 @@
   }
 }
 
-void ProcessorEntity::RecordAcceptedUpdate(const UpdateResponseData& update) {
+void ProcessorEntity::RecordAcceptedRemoteUpdate(
+    const UpdateResponseData& update) {
   DCHECK(!IsUnsynced());
-  RecordIgnoredUpdate(update);
+  RecordIgnoredRemoteUpdate(update);
   metadata_.set_is_deleted(update.entity.is_deleted());
   metadata_.set_modification_time(
       TimeToProtoTime(update.entity.modification_time));
@@ -176,16 +178,17 @@
   }
 }
 
-void ProcessorEntity::RecordForcedUpdate(const UpdateResponseData& update) {
+void ProcessorEntity::RecordForcedRemoteUpdate(
+    const UpdateResponseData& update) {
   DCHECK(IsUnsynced());
   // There was a conflict and the server just won it. Explicitly ack all
   // pending commits so they are never enqueued again.
   metadata_.set_acked_sequence_number(metadata_.sequence_number());
   commit_data_.reset();
-  RecordAcceptedUpdate(update);
+  RecordAcceptedRemoteUpdate(update);
 }
 
-void ProcessorEntity::MakeLocalChange(std::unique_ptr<EntityData> data) {
+void ProcessorEntity::RecordLocalUpdate(std::unique_ptr<EntityData> data) {
   DCHECK(!metadata_.client_tag_hash().empty());
 
   // Update metadata fields from updated data.
@@ -209,7 +212,7 @@
   SetCommitData(std::move(data));
 }
 
-bool ProcessorEntity::Delete() {
+bool ProcessorEntity::RecordLocalDeletion() {
   IncrementSequenceNumber(base::Time::Now());
   metadata_.set_modification_time(TimeToProtoTime(base::Time::Now()));
   metadata_.set_is_deleted(true);
diff --git a/components/sync/model/processor_entity.h b/components/sync/model/processor_entity.h
index 94952ada..028e82bf2 100644
--- a/components/sync/model/processor_entity.h
+++ b/components/sync/model/processor_entity.h
@@ -76,21 +76,21 @@
   bool UpdateIsReflection(int64_t update_version) const;
 
   // Records that an update from the server was received but ignores its data.
-  void RecordIgnoredUpdate(const UpdateResponseData& response_data);
+  void RecordIgnoredRemoteUpdate(const UpdateResponseData& response_data);
 
   // Records an update from the server assuming its data is the new data for
   // this entity.
-  void RecordAcceptedUpdate(const UpdateResponseData& response_data);
+  void RecordAcceptedRemoteUpdate(const UpdateResponseData& response_data);
 
   // Squashes a pending commit with an update from the server.
-  void RecordForcedUpdate(const UpdateResponseData& response_data);
+  void RecordForcedRemoteUpdate(const UpdateResponseData& response_data);
 
   // Applies a local change to this item.
-  void MakeLocalChange(std::unique_ptr<EntityData> data);
+  void RecordLocalUpdate(std::unique_ptr<EntityData> data);
 
   // Applies a local deletion to this item. Returns true if entity was
   // previously committed to server and tombstone should be sent.
-  bool Delete();
+  bool RecordLocalDeletion();
 
   // Initializes a message representing this item's uncommitted state
   // and assumes that it is forwarded to the sync engine for commiting.
diff --git a/components/sync/model/processor_entity_tracker.cc b/components/sync/model/processor_entity_tracker.cc
index 9cd17544..f894673 100644
--- a/components/sync/model/processor_entity_tracker.cc
+++ b/components/sync/model/processor_entity_tracker.cc
@@ -73,7 +73,7 @@
 
   ProcessorEntity* entity =
       AddInternal(storage_key, *data, kUncommittedVersion);
-  entity->MakeLocalChange(std::move(data));
+  entity->RecordLocalUpdate(std::move(data));
   return entity;
 }
 
diff --git a/components/sync/model/processor_entity_tracker_unittest.cc b/components/sync/model/processor_entity_tracker_unittest.cc
index d4064a1..f06b6d3e 100644
--- a/components/sync/model/processor_entity_tracker_unittest.cc
+++ b/components/sync/model/processor_entity_tracker_unittest.cc
@@ -206,7 +206,7 @@
   ASSERT_EQ(kStorageKey1, entity->storage_key());
 
   // Mark the entity as removed.
-  entity->Delete();
+  entity->RecordLocalDeletion();
   ASSERT_EQ(1u, entity_tracker_.size());
   ASSERT_EQ(0u, entity_tracker_.CountNonTombstoneEntries());
 
@@ -226,7 +226,7 @@
   ASSERT_EQ(kStorageKey1, entity->storage_key());
 
   // Mark the entity as removed.
-  entity->Delete();
+  entity->RecordLocalDeletion();
   ASSERT_EQ(1u, entity_tracker_.size());
   ASSERT_EQ(0u, entity_tracker_.CountNonTombstoneEntries());
 
@@ -290,7 +290,7 @@
       entity_tracker_.GetEntitiesWithLocalChanges(/*max_entries=*/1).empty());
 
   // Make some local changes.
-  entity->MakeLocalChange(std::make_unique<EntityData>(
+  entity->RecordLocalUpdate(std::make_unique<EntityData>(
       GenerateEntityData(kStorageKey1, kClientTagHash1)));
   entity_tracker_.IncrementSequenceNumberForAllExcept({});
   EXPECT_TRUE(entity->IsUnsynced());
diff --git a/components/sync/model/processor_entity_unittest.cc b/components/sync/model/processor_entity_unittest.cc
index a08d72df..388d5d4 100644
--- a/components/sync/model/processor_entity_unittest.cc
+++ b/components/sync/model/processor_entity_unittest.cc
@@ -121,7 +121,7 @@
     std::unique_ptr<ProcessorEntity> entity = CreateNew();
     UpdateResponseData update =
         GenerateUpdate(*entity, kHash, kId, kName, kValue1, ctime_, 1);
-    entity->RecordAcceptedUpdate(update);
+    entity->RecordAcceptedRemoteUpdate(update);
     DCHECK(!entity->IsUnsynced());
     return entity;
   }
@@ -162,7 +162,7 @@
 // Test creating and commiting a new local item.
 TEST_F(ProcessorEntityTest, NewLocalItem) {
   std::unique_ptr<ProcessorEntity> entity = CreateNew();
-  entity->MakeLocalChange(GenerateEntityData(kHash, kName, kValue1));
+  entity->RecordLocalUpdate(GenerateEntityData(kHash, kName, kValue1));
 
   EXPECT_EQ("", entity->metadata().server_id());
   EXPECT_FALSE(entity->metadata().is_deleted());
@@ -237,7 +237,7 @@
   const base::Time mtime = base::Time::Now();
   UpdateResponseData update =
       GenerateUpdate(*entity, kHash, kId, kName, kValue1, mtime, 10);
-  entity->RecordAcceptedUpdate(update);
+  entity->RecordAcceptedRemoteUpdate(update);
 
   EXPECT_EQ(kId, entity->metadata().server_id());
   EXPECT_FALSE(entity->metadata().is_deleted());
@@ -268,7 +268,7 @@
   const base::Time mtime = base::Time::Now();
   UpdateResponseData update =
       GenerateUpdate(*entity, kHash, kId, kName, kValue1, mtime, 10);
-  entity->RecordAcceptedUpdate(update);
+  entity->RecordAcceptedRemoteUpdate(update);
   entity->SetStorageKey(kKey);
   EXPECT_EQ(kKey, entity->storage_key());
 }
@@ -280,7 +280,7 @@
   const base::Time mtime = base::Time::Now();
   UpdateResponseData tombstone =
       GenerateTombstone(*entity, kHash, kId, kName, mtime, 1);
-  entity->RecordAcceptedUpdate(tombstone);
+  entity->RecordAcceptedRemoteUpdate(tombstone);
 
   EXPECT_EQ(kId, entity->metadata().server_id());
   EXPECT_TRUE(entity->metadata().is_deleted());
@@ -308,7 +308,7 @@
   const base::Time mtime = base::Time::Now();
   UpdateResponseData tombstone =
       GenerateTombstone(*entity, kHash, kId, kName, mtime, 2);
-  entity->RecordAcceptedUpdate(tombstone);
+  entity->RecordAcceptedRemoteUpdate(tombstone);
 
   EXPECT_TRUE(entity->metadata().is_deleted());
   EXPECT_EQ(0, entity->metadata().sequence_number());
@@ -334,7 +334,7 @@
   const std::string specifics_hash_v0 = entity->metadata().specifics_hash();
 
   // Make a local change with different specifics.
-  entity->MakeLocalChange(GenerateEntityData(kHash, kName, kValue2));
+  entity->RecordLocalUpdate(GenerateEntityData(kHash, kName, kValue2));
 
   const int64_t mtime_v1 = entity->metadata().modification_time();
   const std::string specifics_hash_v1 = entity->metadata().specifics_hash();
@@ -384,7 +384,7 @@
   const std::string specifics_hash = entity->metadata().specifics_hash();
 
   // Make a local delete.
-  entity->Delete();
+  entity->RecordLocalDeletion();
 
   EXPECT_TRUE(entity->metadata().is_deleted());
   EXPECT_EQ(1, entity->metadata().sequence_number());
@@ -449,13 +449,13 @@
   std::unique_ptr<ProcessorEntity> entity = CreateSynced();
   const std::string specifics_hash = entity->metadata().specifics_hash();
 
-  entity->Delete();
+  entity->RecordLocalDeletion();
   ASSERT_TRUE(entity->metadata().is_deleted());
   ASSERT_TRUE(entity->IsUnsynced());
   ASSERT_EQ(1, entity->metadata().sequence_number());
 
   // Undelete the entity with different specifics.
-  entity->MakeLocalChange(GenerateEntityData(kHash, kName, kValue2));
+  entity->RecordLocalUpdate(GenerateEntityData(kHash, kName, kValue2));
 
   const std::string specifics_hash_v1 = entity->metadata().specifics_hash();
   ASSERT_NE(specifics_hash_v1, specifics_hash);
@@ -501,7 +501,7 @@
   const std::string specifics_hash_v0 = entity->metadata().specifics_hash();
 
   // Make the first change.
-  entity->MakeLocalChange(GenerateEntityData(kHash, kName, kValue2));
+  entity->RecordLocalUpdate(GenerateEntityData(kHash, kName, kValue2));
   const std::string specifics_hash_v1 = entity->metadata().specifics_hash();
 
   EXPECT_EQ(1, entity->metadata().sequence_number());
@@ -514,7 +514,7 @@
   entity->InitializeCommitRequestData(&request_v1);
 
   // Make the second change.
-  entity->MakeLocalChange(GenerateEntityData(kHash, kName, kValue3));
+  entity->RecordLocalUpdate(GenerateEntityData(kHash, kName, kValue3));
   const std::string specifics_hash_v2 = entity->metadata().specifics_hash();
 
   EXPECT_EQ(2, entity->metadata().sequence_number());
@@ -568,14 +568,14 @@
 TEST_F(ProcessorEntityTest, NewLocalChangeUpdatedId) {
   std::unique_ptr<ProcessorEntity> entity = CreateNew();
   // Create new local change. Make sure initial id is empty.
-  entity->MakeLocalChange(GenerateEntityData(kHash, kName, kValue1));
+  entity->RecordLocalUpdate(GenerateEntityData(kHash, kName, kValue1));
 
   CommitRequestData request;
   entity->InitializeCommitRequestData(&request);
   EXPECT_TRUE(request.entity->id.empty());
 
   // Before receiving commit response make local modification to the entity.
-  entity->MakeLocalChange(GenerateEntityData(kHash, kName, kValue2));
+  entity->RecordLocalUpdate(GenerateEntityData(kHash, kName, kValue2));
   entity->ReceiveCommitResponse(GenerateAckData(request, kId, 1), false);
 
   // Receiving commit response with valid id should update
@@ -587,11 +587,11 @@
 }
 
 // Tests that entity restored after restart accepts specifics that don't match
-// the ones passed originally to MakeLocalChange.
+// the ones passed originally to RecordLocalUpdate.
 TEST_F(ProcessorEntityTest, RestoredLocalChangeWithUpdatedSpecifics) {
   // Create new entity and preserver its metadata.
   std::unique_ptr<ProcessorEntity> entity = CreateNew();
-  entity->MakeLocalChange(GenerateEntityData(kHash, kName, kValue1));
+  entity->RecordLocalUpdate(GenerateEntityData(kHash, kName, kValue1));
   sync_pb::EntityMetadata entity_metadata = entity->metadata();
 
   // Restore entity from metadata and emulate bridge passing different specifics
@@ -609,7 +609,7 @@
 // should be ignored but the server IDs should be updated.
 TEST_F(ProcessorEntityTest, LocalCreationConflictsWithServerTombstone) {
   std::unique_ptr<ProcessorEntity> entity = CreateNew();
-  entity->MakeLocalChange(GenerateEntityData(kHash, kName, kValue1));
+  entity->RecordLocalUpdate(GenerateEntityData(kHash, kName, kValue1));
 
   ASSERT_TRUE(entity->IsUnsynced());
   ASSERT_TRUE(entity->RequiresCommitRequest());
@@ -622,7 +622,7 @@
   // would usually win so the remote update is ignored.
   UpdateResponseData tombstone =
       GenerateTombstone(*entity, kHash, kId, kName, base::Time::Now(), 2);
-  entity->RecordIgnoredUpdate(tombstone);
+  entity->RecordIgnoredRemoteUpdate(tombstone);
 
   EXPECT_EQ(kId, entity->metadata().server_id());
   EXPECT_TRUE(entity->IsUnsynced());
@@ -646,7 +646,7 @@
   const base::Time mtime = base::Time::Now();
   UpdateResponseData update =
       GenerateUpdate(*entity, kHash, kId, kName, kValue1, mtime, 10);
-  entity->RecordAcceptedUpdate(update);
+  entity->RecordAcceptedRemoteUpdate(update);
   EXPECT_EQ(
       entity->metadata().possibly_trimmed_base_specifics().preference().name(),
       kName);
@@ -660,7 +660,7 @@
   feature_list.InitAndEnableFeature(kCacheBaseEntitySpecificsInMetadata);
 
   std::unique_ptr<ProcessorEntity> entity = CreateNew();
-  entity->MakeLocalChange(GenerateEntityData(kHash, kName, kValue1));
+  entity->RecordLocalUpdate(GenerateEntityData(kHash, kName, kValue1));
   EXPECT_EQ(
       entity->metadata().possibly_trimmed_base_specifics().preference().name(),
       kName);
diff --git a/components/sync/nigori/nigori_model_type_processor.cc b/components/sync/nigori/nigori_model_type_processor.cc
index 83416613..3244d59 100644
--- a/components/sync/nigori/nigori_model_type_processor.cc
+++ b/components/sync/nigori/nigori_model_type_processor.cc
@@ -144,7 +144,7 @@
       entity_ = ProcessorEntity::CreateNew(
           kNigoriStorageKey, ClientTagHash::FromHashed(kRawNigoriClientTagHash),
           updates[0].entity.id, updates[0].entity.creation_time);
-      entity_->RecordAcceptedUpdate(updates[0]);
+      entity_->RecordAcceptedRemoteUpdate(updates[0]);
       error = bridge_->MergeSyncData(std::move(updates[0].entity));
     }
     if (error) {
@@ -172,11 +172,11 @@
   if (entity_->IsUnsynced()) {
     // Remote update always win in case of conflict, because bridge takes care
     // of reapplying pending local changes after processing the remote update.
-    entity_->RecordForcedUpdate(updates[0]);
+    entity_->RecordForcedRemoteUpdate(updates[0]);
     error = bridge_->ApplySyncChanges(std::move(updates[0].entity));
   } else if (!entity_->MatchesData(updates[0].entity)) {
     // Inform the bridge of the new or updated data.
-    entity_->RecordAcceptedUpdate(updates[0]);
+    entity_->RecordAcceptedRemoteUpdate(updates[0]);
     error = bridge_->ApplySyncChanges(std::move(updates[0].entity));
   }
 
@@ -339,7 +339,7 @@
     return;
   }
 
-  entity_->MakeLocalChange(std::move(entity_data));
+  entity_->RecordLocalUpdate(std::move(entity_data));
   NudgeForCommitIfNeeded();
 }
 
diff --git a/components/vector_icons/aggregate_vector_icons.py b/components/vector_icons/aggregate_vector_icons.py
index dbe60ad..72a8cf2 100644
--- a/components/vector_icons/aggregate_vector_icons.py
+++ b/components/vector_icons/aggregate_vector_icons.py
@@ -182,7 +182,7 @@
         output_cc.write("VECTOR_ICON_REP_TEMPLATE({}, {})\n".format(
             icon_path_name, vector_commands))
         icon_representation_strings.append(
-            "{{{0}, base::size({0})}}".format(icon_path_name))
+            "{{{0}, std::size({0})}}".format(icon_path_name))
 
       # Another temporary variable kFooBarRepList is used to create all the
       # VectorIconReps inline, with a pointer to it in the final VectorIcon.
diff --git a/components/vector_icons/cc_macros.h b/components/vector_icons/cc_macros.h
index 2a587ee..7100e5d 100644
--- a/components/vector_icons/cc_macros.h
+++ b/components/vector_icons/cc_macros.h
@@ -5,7 +5,7 @@
 #ifndef COMPONENTS_VECTOR_ICONS_CC_MACROS_H_
 #define COMPONENTS_VECTOR_ICONS_CC_MACROS_H_
 
-#include "base/cxx17_backports.h"  // For base::size().
+#include <iterator>
 
 // This file holds macros that are common to each vector icon target's
 // vector_icons.cc.template file.
@@ -30,7 +30,7 @@
   constexpr char icon_name##Id[] = VECTOR_ICON_ID_PREFIX #icon_name;   \
   static constexpr gfx::VectorIconRep rep_list_name[] = {__VA_ARGS__}; \
   VECTOR_ICON_EXPORT constexpr gfx::VectorIcon icon_name = {           \
-      rep_list_name, base::size(rep_list_name), icon_name##Id};
+      rep_list_name, std::size(rep_list_name), icon_name##Id};
 
 #else  // !COMPONENTS_VECTOR_ICONS_CC_MACROS_H_
 #error This file should only be included once.
diff --git a/components/viz/client/client_resource_provider.cc b/components/viz/client/client_resource_provider.cc
index 3293cd0..4786d65a 100644
--- a/components/viz/client/client_resource_provider.cc
+++ b/components/viz/client/client_resource_provider.cc
@@ -8,7 +8,6 @@
 #include <utility>
 
 #include "base/bind.h"
-#include "base/bits.h"
 #include "base/containers/cxx20_erase.h"
 #include "base/debug/stack_trace.h"
 #include "base/threading/thread_task_runner_handle.h"
diff --git a/components/viz/service/display/scoped_gpu_memory_buffer_texture.cc b/components/viz/service/display/scoped_gpu_memory_buffer_texture.cc
index 31c0a021..e7968ed7 100644
--- a/components/viz/service/display/scoped_gpu_memory_buffer_texture.cc
+++ b/components/viz/service/display/scoped_gpu_memory_buffer_texture.cc
@@ -4,7 +4,6 @@
 
 #include "components/viz/service/display/scoped_gpu_memory_buffer_texture.h"
 
-#include "base/bits.h"
 #include "base/check.h"
 #include "components/viz/common/gpu/context_provider.h"
 #include "components/viz/common/resources/resource_format.h"
diff --git a/components/viz/service/display/skia_renderer.cc b/components/viz/service/display/skia_renderer.cc
index fb34504..5ae3189f 100644
--- a/components/viz/service/display/skia_renderer.cc
+++ b/components/viz/service/display/skia_renderer.cc
@@ -10,7 +10,6 @@
 
 #include "base/auto_reset.h"
 #include "base/bind.h"
-#include "base/bits.h"
 #include "base/command_line.h"
 #include "base/logging.h"
 #include "base/memory/raw_ptr.h"
diff --git a/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.cc b/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.cc
index 5b8c82c..df92bab 100644
--- a/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.cc
+++ b/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.cc
@@ -551,13 +551,10 @@
 
 void RootCompositorFrameSinkImpl::DisplayDidReceiveCALayerParams(
     const gfx::CALayerParams& ca_layer_params) {
-  if (last_ca_layer_params_ == ca_layer_params)
-    return;
 #if BUILDFLAG(IS_APPLE)
   // If |ca_layer_params| should have content only when there exists a client
   // to send it to.
   DCHECK(ca_layer_params.is_empty || display_client_);
-  last_ca_layer_params_ = ca_layer_params;
   if (display_client_)
     display_client_->OnDisplayReceivedCALayerParams(ca_layer_params);
 #else
diff --git a/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.h b/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.h
index dbb85479..a6e4ebbf 100644
--- a/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.h
+++ b/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.h
@@ -200,8 +200,6 @@
   gfx::Size last_swap_pixel_size_;
 #endif
 
-  gfx::CALayerParams last_ca_layer_params_;
-
 #if BUILDFLAG(IS_ANDROID)
   // Let client control whether it wants `DidCompleteSwapWithSize`.
   bool enable_swap_competion_callback_ = false;
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index 4e9dd52..d71fa98d 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -2045,8 +2045,6 @@
     "webauth/webauth_request_security_checker.h",
     "webid/fedcm_metrics.cc",
     "webid/fedcm_metrics.h",
-    "webid/federated_auth_navigation_throttle.cc",
-    "webid/federated_auth_navigation_throttle.h",
     "webid/federated_auth_request_impl.cc",
     "webid/federated_auth_request_impl.h",
     "webid/federated_auth_request_service.cc",
diff --git a/content/browser/accessibility/ax_platform_node_textrangeprovider_win_browsertest.cc b/content/browser/accessibility/ax_platform_node_textrangeprovider_win_browsertest.cc
index 24b95ba..1762302ac 100644
--- a/content/browser/accessibility/ax_platform_node_textrangeprovider_win_browsertest.cc
+++ b/content/browser/accessibility/ax_platform_node_textrangeprovider_win_browsertest.cc
@@ -1399,6 +1399,10 @@
         <div style="font-weight: bold">bold 2</div>
         <div style="font-family: sans-serif">font-family 1</div>
         <div style="font-family: sans-serif">font-family 2</div>
+        <div aria-invalid="spelling">spelling 1</div>
+        <div aria-invalid="spelling">spelling two</div> <!-- different length string on purpose -->
+        <div aria-invalid="grammar">grammar 1</div>
+        <div aria-invalid="grammar">grammar two</div> <!-- different length string on purpose -->
       </body>
       </html>)HTML");
   auto* node = FindNode(ax::mojom::Role::kStaticText, "plain 1");
@@ -1515,6 +1519,41 @@
       L"1\ncolor 2\noverline 1\noverline 2\nline-through 1\nline-through "
       L"2\nsup 1\nsup 2\nbold 1\nbold 2",
       /*expected_count*/ -1);
+  EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT(
+      text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Format,
+      /*count*/ 2,
+      /*expected_text*/
+      L"plain 1\nplain 2\nbackground-color 1\nbackground-color 2\ncolor "
+      L"1\ncolor 2\noverline 1\noverline 2\nline-through 1\nline-through "
+      L"2\nsup 1\nsup 2\nbold 1\nbold 2\nfont-family 1\nfont-family "
+      L"2\nspelling 1\nspelling two",
+      /*expected_count*/ 2);
+  EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT(
+      text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Format,
+      /*count*/ -1,
+      /*expected_text*/
+      L"plain 1\nplain 2\nbackground-color 1\nbackground-color 2\ncolor "
+      L"1\ncolor 2\noverline 1\noverline 2\nline-through 1\nline-through "
+      L"2\nsup 1\nsup 2\nbold 1\nbold 2\nfont-family 1\nfont-family 2",
+      /*expected_count*/ -1);
+  EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT(
+      text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Format,
+      /*count*/ 2,
+      /*expected_text*/
+      L"plain 1\nplain 2\nbackground-color 1\nbackground-color 2\ncolor "
+      L"1\ncolor 2\noverline 1\noverline 2\nline-through 1\nline-through "
+      L"2\nsup 1\nsup 2\nbold 1\nbold 2\nfont-family 1\nfont-family "
+      L"2\nspelling 1\nspelling two\ngrammar 1\ngrammar two",
+      /*expected_count*/ 2);
+  EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT(
+      text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Format,
+      /*count*/ -1,
+      /*expected_text*/
+      L"plain 1\nplain 2\nbackground-color 1\nbackground-color 2\ncolor "
+      L"1\ncolor 2\noverline 1\noverline 2\nline-through 1\nline-through "
+      L"2\nsup 1\nsup 2\nbold 1\nbold 2\nfont-family 1\nfont-family "
+      L"2\nspelling 1\nspelling two",
+      /*expected_count*/ -1);
 }
 
 IN_PROC_BROWSER_TEST_F(AXPlatformNodeTextRangeProviderWinBrowserTest,
diff --git a/content/browser/accessibility/browser_accessibility_manager_fuchsia.cc b/content/browser/accessibility/browser_accessibility_manager_fuchsia.cc
index d3c8c9b..c3337ef 100644
--- a/content/browser/accessibility/browser_accessibility_manager_fuchsia.cc
+++ b/content/browser/accessibility/browser_accessibility_manager_fuchsia.cc
@@ -42,7 +42,7 @@
       inspector.GetRoot().CreateString(ax_tree_id().ToString(),
                                        ax_tree()->ToString(), &inspector);
 
-      return fit::make_ok_promise(inspector);
+      return fpromise::make_ok_promise(inspector);
     });
   }
 }
diff --git a/content/browser/attribution_reporting/attribution_data_host_manager_impl.cc b/content/browser/attribution_reporting/attribution_data_host_manager_impl.cc
index eaa8242..2764de6 100644
--- a/content/browser/attribution_reporting/attribution_data_host_manager_impl.cc
+++ b/content/browser/attribution_reporting/attribution_data_host_manager_impl.cc
@@ -18,10 +18,35 @@
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/content_browser_client.h"
 #include "content/public/common/content_client.h"
+#include "third_party/blink/public/common/attribution_reporting/constants.h"
 #include "url/origin.h"
 
 namespace content {
 
+namespace {
+
+bool IsFilterDataValid(const blink::mojom::AttributionFilterData& filter_data) {
+  if (filter_data.filter_values.size() > blink::kMaxAttributionFiltersPerSource)
+    return false;
+
+  for (const auto& [filter, values] : filter_data.filter_values) {
+    if (filter.size() > blink::kMaxBytesPerAttributionFilterString)
+      return false;
+
+    if (values.size() > blink::kMaxValuesPerAttributionFilter)
+      return false;
+
+    for (const auto& value : values) {
+      if (value.size() > blink::kMaxBytesPerAttributionFilterString)
+        return false;
+    }
+  }
+
+  return true;
+}
+
+}  // namespace
+
 AttributionDataHostManagerImpl::AttributionDataHostManagerImpl(
     BrowserContext* browser_context,
     AttributionManager* attribution_manager)
@@ -80,6 +105,9 @@
     return;
   }
 
+  if (!IsFilterDataValid(*data->filter_data))
+    return;
+
   StorableSource storable_source(CommonSourceInfo(
       data->source_event_id, context.context_origin, data->destination,
       reporting_origin, source_time,
diff --git a/content/browser/attribution_reporting/attribution_data_host_manager_impl_unittest.cc b/content/browser/attribution_reporting/attribution_data_host_manager_impl_unittest.cc
index a1e1af0..eb4dd2d5 100644
--- a/content/browser/attribution_reporting/attribution_data_host_manager_impl_unittest.cc
+++ b/content/browser/attribution_reporting/attribution_data_host_manager_impl_unittest.cc
@@ -75,6 +75,7 @@
   source_data->reporting_origin = reporting_origin;
   source_data->priority = 20;
   source_data->debug_key = blink::mojom::AttributionDebugKey::New(789);
+  source_data->filter_data = blink::mojom::AttributionFilterData::New();
   data_host_remote->SourceDataAvailable(std::move(source_data));
   data_host_remote.FlushForTesting();
 }
@@ -128,6 +129,32 @@
         url::Origin::Create(GURL(test_case.destination_origin));
     source_data->reporting_origin =
         url::Origin::Create(GURL(test_case.reporting_origin));
+    source_data->filter_data = blink::mojom::AttributionFilterData::New();
+    data_host_remote->SourceDataAvailable(std::move(source_data));
+    data_host_remote.FlushForTesting();
+
+    Mock::VerifyAndClear(&mock_manager_);
+  }
+}
+
+TEST_F(AttributionDataHostManagerImplTest,
+       SourceDataHost_FilterSizeCheckPerformed) {
+  for (const auto& test_case : kAttributionFilterSizeTestCases) {
+    SCOPED_TRACE(test_case.description);  // EXPECT_CALL doesn't support <<
+    EXPECT_CALL(mock_manager_, HandleSource).Times(test_case.valid);
+
+    mojo::Remote<blink::mojom::AttributionDataHost> data_host_remote;
+    data_host_manager_.RegisterDataHost(
+        data_host_remote.BindNewPipeAndPassReceiver(),
+        url::Origin::Create(GURL("https://page.example")));
+
+    auto source_data = blink::mojom::AttributionSourceData::New();
+    source_data->destination =
+        url::Origin::Create(GURL("https://trigger.example"));
+    source_data->reporting_origin =
+        url::Origin::Create(GURL("https://reporter.example"));
+    source_data->filter_data =
+        blink::mojom::AttributionFilterData::New(test_case.AsMap());
     data_host_remote->SourceDataAvailable(std::move(source_data));
     data_host_remote.FlushForTesting();
 
@@ -160,6 +187,7 @@
   source_data->source_event_id = 10;
   source_data->destination = destination_origin;
   source_data->reporting_origin = reporting_origin;
+  source_data->filter_data = blink::mojom::AttributionFilterData::New();
   data_host_remote->SourceDataAvailable(std::move(source_data));
   data_host_remote.FlushForTesting();
 }
@@ -191,6 +219,7 @@
   auto source_data = blink::mojom::AttributionSourceData::New();
   source_data->destination = destination_origin;
   source_data->reporting_origin = reporting_origin;
+  source_data->filter_data = blink::mojom::AttributionFilterData::New();
   data_host_remote->SourceDataAvailable(source_data.Clone());
   data_host_remote.FlushForTesting();
 
diff --git a/content/browser/attribution_reporting/attribution_report.cc b/content/browser/attribution_reporting/attribution_report.cc
index 3597320..df8bede 100644
--- a/content/browser/attribution_reporting/attribution_report.cc
+++ b/content/browser/attribution_reporting/attribution_report.cc
@@ -107,8 +107,8 @@
   };
 
   const char* path = absl::visit(Visitor{}, data_);
-  url::Replacements<char> replacements;
-  replacements.SetPath(path, url::Component(0, strlen(path)));
+  GURL::Replacements replacements;
+  replacements.SetPathStr(path);
   return attribution_info_.source.common_info()
       .reporting_origin()
       .GetURL()
diff --git a/content/browser/attribution_reporting/attribution_storage_sql.cc b/content/browser/attribution_reporting/attribution_storage_sql.cc
index ef6fd13..085f044 100644
--- a/content/browser/attribution_reporting/attribution_storage_sql.cc
+++ b/content/browser/attribution_reporting/attribution_storage_sql.cc
@@ -1719,12 +1719,13 @@
   // Origins usually aren't _that_ big compared to a 64 bit integer(8 bytes).
   //
   // All of the columns in this table are designed to be "const" except for
-  // |num_conversions| and |active| which are updated when a new report is
-  // received. |num_conversions| is the number of times a report has
-  // been created for a given source. |delegate_| can choose to enforce a
-  // maximum limit on this. |active| indicates whether a source is able to
-  // create new associated reports. |active| can be unset on a number
-  // of conditions:
+  // |num_conversions|, |aggregatable_budget_consumed| and |active| which are
+  // updated when a new trigger is received. |num_conversions| is the number of
+  // times an event-level report has been created for a given source.
+  // |aggregatable_budget_consumed| is the aggregatable budget that has been
+  // consumed for a given source. |delegate_| can choose to enforce a maximum
+  // limit on them. |active| indicates whether a source is able to create new
+  // associated reports. |active| can be unset on a number of conditions:
   //   - A source converted too many times.
   //   - A new source was stored after a source converted, making it
   //     ineligible for new sources due to the attribution model documented
diff --git a/content/browser/attribution_reporting/attribution_test_utils.cc b/content/browser/attribution_reporting/attribution_test_utils.cc
index be8f9ff..9c56b04 100644
--- a/content/browser/attribution_reporting/attribution_test_utils.cc
+++ b/content/browser/attribution_reporting/attribution_test_utils.cc
@@ -12,6 +12,7 @@
 #include "base/bind.h"
 #include "base/callback.h"
 #include "base/callback_helpers.h"
+#include "base/check_op.h"
 #include "base/ranges/algorithm.h"
 #include "base/run_loop.h"
 #include "base/strings/string_number_conversions.h"
@@ -823,6 +824,21 @@
   }
 }
 
+AttributionFilterSizeTestCase::Map AttributionFilterSizeTestCase::AsMap()
+    const {
+  Map map;
+
+  for (size_t i = 0; i < filter_count; i++) {
+    // Give each filter a unique name while respecting the desired size.
+    std::string filter(filter_size, 'A' + i);
+    std::vector<std::string> values(value_count, std::string(value_size, '*'));
+    map.emplace(std::move(filter), std::move(values));
+  }
+
+  DCHECK_EQ(map.size(), filter_count);
+  return map;
+}
+
 std::vector<AttributionReport> GetAttributionsToReportForTesting(
     AttributionManagerImpl* manager,
     base::Time max_report_time) {
diff --git a/content/browser/attribution_reporting/attribution_test_utils.h b/content/browser/attribution_reporting/attribution_test_utils.h
index 03a4ee83..0caff1cd 100644
--- a/content/browser/attribution_reporting/attribution_test_utils.h
+++ b/content/browser/attribution_reporting/attribution_test_utils.h
@@ -9,8 +9,10 @@
 
 #include <iosfwd>
 #include <limits>
+#include <string>
 #include <vector>
 
+#include "base/containers/flat_map.h"
 #include "base/guid.h"
 #include "base/memory/raw_ptr.h"
 #include "base/memory/scoped_refptr.h"
@@ -34,6 +36,7 @@
 #include "net/base/schemeful_site.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/blink/public/common/attribution_reporting/constants.h"
 #include "third_party/blink/public/mojom/conversions/attribution_data_host.mojom.h"
 #include "url/origin.h"
 
@@ -662,6 +665,38 @@
                             result_listener);
 }
 
+struct AttributionFilterSizeTestCase {
+  const char* description;
+  bool valid;
+
+  size_t filter_count;
+  size_t filter_size;
+  size_t value_count;
+  size_t value_size;
+
+  using Map = base::flat_map<std::string, std::vector<std::string>>;
+
+  Map AsMap() const;
+};
+
+constexpr AttributionFilterSizeTestCase kAttributionFilterSizeTestCases[] = {
+    {"empty", true, 0, 0, 0, 0},
+    {"max_filters", true, blink::kMaxAttributionFiltersPerSource, 1, 0, 0},
+    {"too_many_filters", false, blink::kMaxAttributionFiltersPerSource + 1, 1,
+     0, 0},
+    {"max_filter_size", true, 1, blink::kMaxBytesPerAttributionFilterString, 0,
+     0},
+    {"excessive_filter_size", false, 1,
+     blink::kMaxBytesPerAttributionFilterString + 1, 0, 0},
+    {"max_values", true, 1, 0, blink::kMaxValuesPerAttributionFilter, 0},
+    {"too_many_values", false, 1, 0, blink::kMaxValuesPerAttributionFilter + 1,
+     0},
+    {"max_value_size", true, 1, 0, 1,
+     blink::kMaxBytesPerAttributionFilterString},
+    {"excessive_value_size", false, 1, 0, 1,
+     blink::kMaxBytesPerAttributionFilterString + 1},
+};
+
 }  // namespace content
 
 #endif  // CONTENT_BROWSER_ATTRIBUTION_REPORTING_ATTRIBUTION_TEST_UTILS_H_
diff --git a/content/browser/attribution_reporting/source_declaration_browsertest.cc b/content/browser/attribution_reporting/source_declaration_browsertest.cc
index b3ce290..f30afaa 100644
--- a/content/browser/attribution_reporting/source_declaration_browsertest.cc
+++ b/content/browser/attribution_reporting/source_declaration_browsertest.cc
@@ -4,8 +4,11 @@
 
 #include <memory>
 
+#include "base/json/json_writer.h"
 #include "base/run_loop.h"
+#include "base/strings/strcat.h"
 #include "base/test/metrics/histogram_tester.h"
+#include "base/values.h"
 #include "build/build_config.h"
 #include "content/browser/attribution_reporting/attribution_manager_impl.h"
 #include "content/browser/attribution_reporting/attribution_test_utils.h"
@@ -32,7 +35,11 @@
 
 namespace {
 
+using ::testing::ElementsAre;
 using ::testing::Field;
+using ::testing::IsEmpty;
+using ::testing::Pair;
+using ::testing::UnorderedElementsAre;
 
 std::unique_ptr<MockDataHost> GetRegisteredDataHost(
     mojo::PendingReceiver<blink::mojom::AttributionDataHost> data_host) {
@@ -193,6 +200,7 @@
   EXPECT_EQ(source_data.front()->priority, 0);
   EXPECT_EQ(source_data.front()->expiry, absl::nullopt);
   EXPECT_FALSE(source_data.front()->debug_key);
+  EXPECT_THAT(source_data.front()->filter_data->filter_values, IsEmpty());
 }
 
 IN_PROC_BROWSER_TEST_F(AttributionSourceDeclarationBrowserTest,
@@ -231,6 +239,9 @@
   EXPECT_EQ(source_data.front()->expiry, base::Seconds(1000));
   EXPECT_EQ(source_data.front()->debug_key,
             blink::mojom::AttributionDebugKey::New(789));
+  EXPECT_THAT(source_data.front()->filter_data->filter_values,
+              UnorderedElementsAre(Pair("a", IsEmpty()),
+                                   Pair("b", ElementsAre("1", "2"))));
 }
 
 IN_PROC_BROWSER_TEST_F(AttributionSourceDeclarationBrowserTest,
@@ -1196,4 +1207,171 @@
   EXPECT_TRUE(source_observer.WaitForNavigationWithNoImpression());
 }
 
+class AttributionSourceDeclarationInvalidFiltersBrowserTest
+    : public AttributionSourceDeclarationBrowserTest,
+      public ::testing::WithParamInterface<const char*> {};
+
+IN_PROC_BROWSER_TEST_P(AttributionSourceDeclarationInvalidFiltersBrowserTest,
+                       AttributionSrcImgFiltersInvalid_SourceDropped) {
+  // Create a separate server as we cannot register a `ControllableHttpResponse`
+  // after the server starts.
+  auto https_server = std::make_unique<net::EmbeddedTestServer>(
+      net::EmbeddedTestServer::TYPE_HTTPS);
+  https_server->SetSSLConfig(net::EmbeddedTestServer::CERT_TEST_NAMES);
+  net::test_server::RegisterDefaultHandlers(https_server.get());
+  https_server->ServeFilesFromSourceDirectory(
+      "content/test/data/attribution_reporting");
+  https_server->ServeFilesFromSourceDirectory("content/test/data");
+  SetupCrossSiteRedirector(https_server.get());
+
+  auto register_response =
+      std::make_unique<net::test_server::ControllableHttpResponse>(
+          https_server.get(), "/register_source");
+  ASSERT_TRUE(https_server->Start());
+
+  GURL page_url =
+      https_server->GetURL("b.test", "/page_with_impression_creator.html");
+  EXPECT_TRUE(NavigateToURL(web_contents(), page_url));
+
+  MockAttributionHost host(web_contents());
+  std::unique_ptr<MockDataHost> data_host;
+  base::RunLoop loop;
+  EXPECT_CALL(host, RegisterDataHost)
+      .WillOnce(
+          [&](mojo::PendingReceiver<blink::mojom::AttributionDataHost> host) {
+            data_host = GetRegisteredDataHost(std::move(host));
+            loop.Quit();
+          });
+
+  GURL register_url = https_server->GetURL("d.test", "/register_source");
+  EXPECT_TRUE(
+      ExecJs(web_contents(),
+             JsReplace("createAttributionSourceImg($1);", register_url)));
+
+  register_response->WaitForRequest();
+  auto http_response = std::make_unique<net::test_server::BasicHttpResponse>();
+  http_response->set_code(net::HTTP_MOVED_PERMANENTLY);
+  http_response->AddCustomHeader(
+      "Attribution-Reporting-Register-Source",
+      base::StrCat(
+          {R"({"source_event_id":"9", "destination":"https://advertiser.example", "filter_data":)",
+           GetParam(), "}"}));
+  http_response->AddCustomHeader("Location", "/register_source_headers.html");
+  register_response->Send(http_response->ToResponseString());
+  register_response->Done();
+
+  if (!data_host)
+    loop.Run();
+  data_host->WaitForSourceData(/*num_source_data=*/1);
+  const auto& source_data = data_host->source_data();
+
+  // Only the second source is registered.
+  EXPECT_EQ(source_data.size(), 1u);
+  EXPECT_EQ(source_data.back()->source_event_id, 5UL);
+  EXPECT_EQ(source_data.back()->destination,
+            url::Origin::Create(GURL("https://advertiser.example")));
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    AttributionSourceDeclarationInvalidFilters,
+    AttributionSourceDeclarationInvalidFiltersBrowserTest,
+    ::testing::Values(R"("x")",        // not a dictionary
+                      R"({"a":"y"})",  // dictionary value isn't an array
+                      R"({"b":[8]})"   // array value isn't a string
+                      ));
+
+class AttributionSourceDeclarationFilterSizeBrowserTest
+    : public AttributionSourceDeclarationBrowserTest,
+      public ::testing::WithParamInterface<AttributionFilterSizeTestCase> {};
+
+IN_PROC_BROWSER_TEST_P(AttributionSourceDeclarationFilterSizeBrowserTest,
+                       AttributionSrcImgExcessiveFilterSize_SourceDropped) {
+  const AttributionFilterSizeTestCase& test_case = GetParam();
+
+  // Create a separate server as we cannot register a `ControllableHttpResponse`
+  // after the server starts.
+  auto https_server = std::make_unique<net::EmbeddedTestServer>(
+      net::EmbeddedTestServer::TYPE_HTTPS);
+  https_server->SetSSLConfig(net::EmbeddedTestServer::CERT_TEST_NAMES);
+  net::test_server::RegisterDefaultHandlers(https_server.get());
+  https_server->ServeFilesFromSourceDirectory(
+      "content/test/data/attribution_reporting");
+  https_server->ServeFilesFromSourceDirectory("content/test/data");
+  SetupCrossSiteRedirector(https_server.get());
+
+  auto register_response =
+      std::make_unique<net::test_server::ControllableHttpResponse>(
+          https_server.get(), "/register_source");
+  ASSERT_TRUE(https_server->Start());
+
+  GURL page_url =
+      https_server->GetURL("b.test", "/page_with_impression_creator.html");
+  EXPECT_TRUE(NavigateToURL(web_contents(), page_url));
+
+  MockAttributionHost host(web_contents());
+  std::unique_ptr<MockDataHost> data_host;
+  base::RunLoop loop;
+  EXPECT_CALL(host, RegisterDataHost)
+      .WillOnce(
+          [&](mojo::PendingReceiver<blink::mojom::AttributionDataHost> host) {
+            data_host = GetRegisteredDataHost(std::move(host));
+            loop.Quit();
+          });
+
+  GURL register_url = https_server->GetURL("d.test", "/register_source");
+  EXPECT_TRUE(
+      ExecJs(web_contents(),
+             JsReplace("createAttributionSourceImg($1);", register_url)));
+
+  register_response->WaitForRequest();
+  auto http_response = std::make_unique<net::test_server::BasicHttpResponse>();
+  http_response->set_code(net::HTTP_MOVED_PERMANENTLY);
+
+  base::Value dict(base::Value::Type::DICTIONARY);
+  dict.SetStringKey("source_event_id", "9");
+  dict.SetStringKey("destination", "https://advertiser.example");
+
+  base::Value filter_data(base::Value::Type::DICTIONARY);
+  for (auto [filter, values] : test_case.AsMap()) {
+    base::Value list(base::Value::Type::LIST);
+    for (auto value : values) {
+      list.Append(std::move(value));
+    }
+    filter_data.SetKey(std::move(filter), std::move(list));
+  }
+  dict.SetKey("filter_data", std::move(filter_data));
+
+  std::string json;
+  EXPECT_TRUE(base::JSONWriter::Write(dict, &json));
+
+  http_response->AddCustomHeader("Attribution-Reporting-Register-Source",
+                                 std::move(json));
+  http_response->AddCustomHeader("Location", "/register_source_headers.html");
+  register_response->Send(http_response->ToResponseString());
+  register_response->Done();
+
+  if (!data_host)
+    loop.Run();
+
+  const size_t expected_sources = test_case.valid ? 2 : 1;
+  data_host->WaitForSourceData(/*num_source_data=*/expected_sources);
+  const auto& source_data = data_host->source_data();
+
+  EXPECT_EQ(source_data.size(), expected_sources);
+  EXPECT_EQ(source_data.back()->source_event_id, 5UL);
+  EXPECT_EQ(source_data.back()->destination,
+            url::Origin::Create(GURL("https://advertiser.example")));
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    AttributionSourceDeclarationFilterSizes,
+    AttributionSourceDeclarationFilterSizeBrowserTest,
+    ::testing::ValuesIn(kAttributionFilterSizeTestCases),
+    /*name_generator=*/
+    [](const ::testing::TestParamInfo<AttributionFilterSizeTestCase>& info) {
+      return info.param.description;
+    });
+
+// TODO(apaseltiner): Add tests for overlong filters.
+
 }  // namespace content
diff --git a/content/browser/cache_storage/legacy/legacy_cache_storage_cache.cc b/content/browser/cache_storage/legacy/legacy_cache_storage_cache.cc
index 04b2a2c..1d5e661e 100644
--- a/content/browser/cache_storage/legacy/legacy_cache_storage_cache.cc
+++ b/content/browser/cache_storage/legacy/legacy_cache_storage_cache.cc
@@ -333,7 +333,7 @@
 }
 
 GURL RemoveQueryParam(const GURL& url) {
-  url::Replacements<char> replacements;
+  GURL::Replacements replacements;
   replacements.ClearQuery();
   return url.ReplaceComponents(replacements);
 }
diff --git a/content/browser/fenced_frame/fenced_frame.cc b/content/browser/fenced_frame/fenced_frame.cc
index e32a97ab..7c07d709 100644
--- a/content/browser/fenced_frame/fenced_frame.cc
+++ b/content/browser/fenced_frame/fenced_frame.cc
@@ -171,7 +171,9 @@
   RenderViewHost* rvh =
       inner_render_manager->current_frame_host()->GetRenderViewHost();
   if (!inner_render_manager->InitRenderView(
-          inner_render_manager->current_frame_host()->GetSiteInstance(),
+          inner_render_manager->current_frame_host()
+              ->GetSiteInstance()
+              ->group(),
           static_cast<RenderViewHostImpl*>(rvh), nullptr)) {
     return;
   }
diff --git a/content/browser/quota/quota_change_dispatcher.cc b/content/browser/quota/quota_change_dispatcher.cc
index 65feb42..66b4153 100644
--- a/content/browser/quota/quota_change_dispatcher.cc
+++ b/content/browser/quota/quota_change_dispatcher.cc
@@ -14,7 +14,6 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/task/post_task.h"
 #include "base/threading/sequenced_task_runner_handle.h"
-#include "base/time/time.h"
 #include "content/browser/quota/quota_manager_host.h"
 #include "content/public/common/content_switches.h"
 #include "mojo/public/cpp/bindings/receiver.h"
diff --git a/content/browser/quota/quota_change_dispatcher.h b/content/browser/quota/quota_change_dispatcher.h
index 811dd5d..ba0b5c3 100644
--- a/content/browser/quota/quota_change_dispatcher.h
+++ b/content/browser/quota/quota_change_dispatcher.h
@@ -6,19 +6,17 @@
 #define CONTENT_BROWSER_QUOTA_QUOTA_CHANGE_DISPATCHER_H_
 
 #include <map>
-#include <utility>
 
 #include "base/memory/ref_counted_delete_on_sequence.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/memory/weak_ptr.h"
 #include "base/sequence_checker.h"
+#include "base/time/time.h"
 #include "content/common/content_export.h"
 #include "mojo/public/cpp/bindings/remote_set.h"
 #include "third_party/blink/public/common/storage_key/storage_key.h"
 #include "third_party/blink/public/mojom/quota/quota_manager_host.mojom.h"
 
-class TimeDelta;
-
 namespace content {
 
 // Dispatches a storage pressure event to listeners across multiple storage
diff --git a/content/browser/renderer_host/clipboard_host_impl.cc b/content/browser/renderer_host/clipboard_host_impl.cc
index c4b5dd7a..1931c51 100644
--- a/content/browser/renderer_host/clipboard_host_impl.cc
+++ b/content/browser/renderer_host/clipboard_host_impl.cc
@@ -117,7 +117,7 @@
       render_frame_host->GetBrowserContext()->IsOffTheRecord()
           ? nullptr
           : std::make_unique<ui::DataTransferEndpoint>(
-                render_frame_host->GetMainFrame()->GetLastCommittedOrigin()));
+                render_frame_host->GetMainFrame()->GetLastCommittedURL()));
 }
 
 void ClipboardHostImpl::Create(
@@ -645,7 +645,7 @@
     return nullptr;
   }
   return std::make_unique<ui::DataTransferEndpoint>(
-      render_frame_host()->GetMainFrame()->GetLastCommittedOrigin(),
+      render_frame_host()->GetMainFrame()->GetLastCommittedURL(),
       render_frame_host()->HasTransientUserActivation());
 }
 }  // namespace content
diff --git a/content/browser/renderer_host/clipboard_host_impl_unittest.cc b/content/browser/renderer_host/clipboard_host_impl_unittest.cc
index e16a35c..57b945a 100644
--- a/content/browser/renderer_host/clipboard_host_impl_unittest.cc
+++ b/content/browser/renderer_host/clipboard_host_impl_unittest.cc
@@ -38,7 +38,6 @@
 #include "ui/gfx/image/image_unittest_util.h"
 #include "ui/gfx/skia_util.h"
 #include "url/gurl.h"
-#include "url/origin.h"
 
 namespace ui {
 class DataTransferEndpoint;
@@ -482,7 +481,7 @@
   EXPECT_TRUE(is_policy_callback_called);
 }
 
-TEST_F(ClipboardHostImplScanTest, MainFrameOrigin) {
+TEST_F(ClipboardHostImplScanTest, MainFrameURL) {
   GURL gurl1("https://example.com");
   GURL gurl2("http://test.org");
   GURL gurl3("http://google.com");
@@ -514,8 +513,7 @@
                    content::RenderFrameHost* rfh,
                    base::OnceCallback<void(bool)> callback) {
             ASSERT_TRUE(data_dst);
-            EXPECT_TRUE(data_dst->GetOrigin()->IsSameOriginWith(
-                url::Origin::Create(gurl1)));
+            EXPECT_EQ(*data_dst->GetURL(), gurl1);
             std::move(callback).Run(true);
           }));
 
diff --git a/content/browser/renderer_host/frame_tree.cc b/content/browser/renderer_host/frame_tree.cc
index 7a11527..2c95fe1 100644
--- a/content/browser/renderer_host/frame_tree.cc
+++ b/content/browser/renderer_host/frame_tree.cc
@@ -517,8 +517,9 @@
     RenderViewHostImpl* render_view_host =
         GetRenderViewHost(site_instance).get();
     if (render_view_host) {
-      root()->render_manager()->EnsureRenderViewInitialized(render_view_host,
-                                                            site_instance);
+      root()->render_manager()->EnsureRenderViewInitialized(
+          render_view_host,
+          static_cast<SiteInstanceImpl*>(site_instance)->group());
     } else {
       // Due to the check above, we are creating either an opener proxy (when
       // source is null) or a main frame proxy due to a subframe navigation
diff --git a/content/browser/renderer_host/navigation_controller_impl.cc b/content/browser/renderer_host/navigation_controller_impl.cc
index 18881d1..087ba78 100644
--- a/content/browser/renderer_host/navigation_controller_impl.cc
+++ b/content/browser/renderer_host/navigation_controller_impl.cc
@@ -3766,6 +3766,8 @@
       params.impression, params.is_pdf);
   navigation_request->set_from_download_cross_origin_redirect(
       params.from_download_cross_origin_redirect);
+  navigation_request->set_force_new_browsing_instance(
+      params.force_new_browsing_instance);
   return navigation_request;
 }
 
diff --git a/content/browser/renderer_host/navigation_controller_impl_browsertest.cc b/content/browser/renderer_host/navigation_controller_impl_browsertest.cc
index a688d90..50d06328 100644
--- a/content/browser/renderer_host/navigation_controller_impl_browsertest.cc
+++ b/content/browser/renderer_host/navigation_controller_impl_browsertest.cc
@@ -21052,6 +21052,32 @@
   EXPECT_EQ(url, new_controller.GetLastCommittedEntry()->GetURL());
 }
 
+// A test to verify that a navigation with |force_new_browsing_instance| set to
+// true results in a new BrowsingInstance, even if it's to a URL compatible with
+// the original SiteInstance.
+IN_PROC_BROWSER_TEST_P(NavigationControllerBrowserTest,
+                       ResetForTestForcesNewBrowsingInstance) {
+  GURL test_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
+  EXPECT_TRUE(NavigateToURL(shell(), test_url));
+
+  SiteInstance* initial_site_instance = contents()->GetSiteInstance();
+
+  FrameTreeNode* root = contents()->GetPrimaryFrameTree().root();
+  DisableProactiveBrowsingInstanceSwapFor(root->current_frame_host());
+
+  TestNavigationObserver navigation_observer(contents());
+  NavigationController::LoadURLParams params(test_url);
+  params.force_new_browsing_instance = true;
+  contents()->GetController().LoadURLWithParams(params);
+  navigation_observer.Wait();
+  EXPECT_TRUE(navigation_observer.last_navigation_succeeded());
+
+  SiteInstance* post_reset_site_instance = contents()->GetSiteInstance();
+  EXPECT_NE(initial_site_instance, post_reset_site_instance);
+  EXPECT_FALSE(
+      post_reset_site_instance->IsRelatedSiteInstance(initial_site_instance));
+}
+
 INSTANTIATE_TEST_SUITE_P(
     All,
     NavigationControllerAlertDialogBrowserTest,
diff --git a/content/browser/renderer_host/navigation_request.h b/content/browser/renderer_host/navigation_request.h
index c9ad80c..99324f4 100644
--- a/content/browser/renderer_host/navigation_request.h
+++ b/content/browser/renderer_host/navigation_request.h
@@ -920,6 +920,16 @@
     prerender_embedder_histogram_suffix_ = suffix;
   }
 
+  // Used in tests to indicate this navigation should force a BrowsingInstance
+  // swap.
+  void set_force_new_browsing_instance(bool force_new_browsing_instance) {
+    force_new_browsing_instance_ = force_new_browsing_instance;
+  }
+
+  // When this returns true, it indicates this navigation should force a
+  // BrowsingInstance swap. Used only in tests.
+  bool force_new_browsing_instance() { return force_new_browsing_instance_; }
+
  private:
   friend class NavigationRequestTest;
 
@@ -1978,6 +1988,10 @@
   // navigation.
   std::unique_ptr<ui::CompositorLock> compositor_lock_;
 
+  // This navigation request should swap browsing instances as part of a test
+  // reset.
+  bool force_new_browsing_instance_ = false;
+
   base::WeakPtrFactory<NavigationRequest> weak_factory_{this};
 };
 
diff --git a/content/browser/renderer_host/navigation_throttle_runner.cc b/content/browser/renderer_host/navigation_throttle_runner.cc
index d364a76..2e0f0fa6 100644
--- a/content/browser/renderer_host/navigation_throttle_runner.cc
+++ b/content/browser/renderer_host/navigation_throttle_runner.cc
@@ -18,7 +18,6 @@
 #include "content/browser/renderer_host/navigation_request.h"
 #include "content/browser/renderer_host/navigator_delegate.h"
 #include "content/browser/renderer_host/origin_policy_throttle.h"
-#include "content/browser/webid/federated_auth_navigation_throttle.h"
 #include "content/public/browser/navigation_handle.h"
 
 namespace content {
@@ -168,9 +167,6 @@
   AddThrottle(
       PrerenderSubframeNavigationThrottle::MaybeCreateThrottleFor(request));
 
-  // Intercept federated identity requests.
-  AddThrottle(FederatedAuthNavigationThrottle::MaybeCreateThrottleFor(request));
-
   // Prevent navigations to/from isolated apps.
   AddThrottle(IsolatedAppThrottle::MaybeCreateThrottleFor(request));
 
diff --git a/content/browser/renderer_host/render_frame_host_manager.cc b/content/browser/renderer_host/render_frame_host_manager.cc
index 4240be1b..015dcdf 100644
--- a/content/browser/renderer_host/render_frame_host_manager.cc
+++ b/content/browser/renderer_host/render_frame_host_manager.cc
@@ -1798,6 +1798,7 @@
     bool was_server_redirect,
     bool cross_origin_opener_policy_mismatch,
     bool should_replace_current_entry,
+    bool force_new_browsing_instance,
     std::string* reason) {
   // On renderer-initiated navigations, when the frame initiating the navigation
   // and the frame being navigated differ, |source_instance| is set to the
@@ -1854,12 +1855,15 @@
   SiteInstanceImpl* current_instance_impl =
       static_cast<SiteInstanceImpl*>(current_instance);
   ShouldSwapBrowsingInstance should_swap_result =
-      ShouldSwapBrowsingInstancesForNavigation(
-          current_effective_url, current_is_view_source_mode, source_instance,
-          current_instance_impl, dest_instance, dest_url_info,
-          dest_is_view_source_mode, transition, is_failure, is_reload,
-          is_same_document, cross_origin_opener_policy_mismatch,
-          was_server_redirect, should_replace_current_entry);
+      force_new_browsing_instance
+          ? ShouldSwapBrowsingInstance::kYes_SameSiteProactiveSwap
+          : ShouldSwapBrowsingInstancesForNavigation(
+                current_effective_url, current_is_view_source_mode,
+                source_instance, current_instance_impl, dest_instance,
+                dest_url_info, dest_is_view_source_mode, transition, is_failure,
+                is_reload, is_same_document,
+                cross_origin_opener_policy_mismatch, was_server_redirect,
+                should_replace_current_entry);
 
   TraceShouldSwapBrowsingInstanceResult(frame_tree_node_->frame_tree_node_id(),
                                         should_swap_result);
@@ -2771,7 +2775,7 @@
 
     SiteInstanceGroup* site_instance_group =
         static_cast<SiteInstanceImpl*>(instance)->group();
-    if (!InitRenderView(instance, render_view_host,
+    if (!InitRenderView(site_instance_group, render_view_host,
                         browsing_context_state->GetRenderFrameProxyHost(
                             site_instance_group))) {
       return nullptr;
@@ -2867,7 +2871,8 @@
 
   // Make sure that the RenderFrameProxy is present in the renderer.
   if (frame_tree_node_->IsMainFrame() && proxy->GetRenderViewHost()) {
-    InitRenderView(instance, proxy->GetRenderViewHost(), proxy);
+    InitRenderView(static_cast<SiteInstanceImpl*>(instance)->group(),
+                   proxy->GetRenderViewHost(), proxy);
   } else {
     proxy->InitRenderFrameProxy();
   }
@@ -2919,7 +2924,7 @@
 
 void RenderFrameHostManager::EnsureRenderViewInitialized(
     RenderViewHostImpl* render_view_host,
-    SiteInstance* instance) {
+    SiteInstanceGroup* group) {
   DCHECK(frame_tree_node_->IsMainFrame());
 
   if (render_view_host->IsRenderViewLive())
@@ -2929,11 +2934,11 @@
   // out and shouldn't be reinitialized here.
   RenderFrameProxyHost* proxy =
       render_frame_host_->browsing_context_state()->GetRenderFrameProxyHost(
-          static_cast<SiteInstanceImpl*>(instance)->group());
+          group);
   if (!proxy)
     return;
 
-  InitRenderView(instance, render_view_host, proxy);
+  InitRenderView(group, render_view_host, proxy);
 }
 
 RenderFrameProxyHost* RenderFrameHostManager::CreateOuterDelegateProxy(
@@ -2975,7 +2980,7 @@
 }
 
 bool RenderFrameHostManager::InitRenderView(
-    SiteInstance* site_instance,
+    SiteInstanceGroup* site_instance_group,
     RenderViewHostImpl* render_view_host,
     RenderFrameProxyHost* proxy) {
   // Ensure the renderer process is initialized before creating the
@@ -2987,8 +2992,7 @@
   if (render_view_host->IsRenderViewLive())
     return true;
 
-  auto opener_frame_token = GetOpenerFrameToken(
-      static_cast<SiteInstanceImpl*>(site_instance)->group());
+  auto opener_frame_token = GetOpenerFrameToken(site_instance_group);
 
   bool created = delegate_->CreateRenderViewForRenderManager(
       render_view_host, opener_frame_token, proxy);
@@ -3078,7 +3082,8 @@
       request->GetRestoreType() == RestoreType::kRestored,
       request->commit_params().is_view_source, request->WasServerRedirect(),
       request->coop_status().require_browsing_instance_swap(),
-      request->common_params().should_replace_current_entry, reason);
+      request->common_params().should_replace_current_entry,
+      request->force_new_browsing_instance(), reason);
 
   TRACE_EVENT_INSTANT(
       "navigation",
@@ -3217,7 +3222,7 @@
   // use InitRenderView.
   DCHECK(!render_frame_host->browsing_context_state()->GetRenderFrameProxyHost(
       render_frame_host->GetSiteInstance()->group()));
-  if (!InitRenderView(render_frame_host->GetSiteInstance(),
+  if (!InitRenderView(render_frame_host->GetSiteInstance()->group(),
                       render_frame_host->render_view_host(), nullptr))
     return false;
 
diff --git a/content/browser/renderer_host/render_frame_host_manager.h b/content/browser/renderer_host/render_frame_host_manager.h
index 819d687..14c9136 100644
--- a/content/browser/renderer_host/render_frame_host_manager.h
+++ b/content/browser/renderer_host/render_frame_host_manager.h
@@ -411,7 +411,7 @@
       const blink::mojom::FrameOwnerProperties& properties);
 
   void EnsureRenderViewInitialized(RenderViewHostImpl* render_view_host,
-                                   SiteInstance* instance);
+                                   SiteInstanceGroup* group);
 
   // Creates RenderFrameProxies and inactive RenderViewHosts for this frame's
   // FrameTree and for its opener chain in the given SiteInstance. This allows
@@ -507,7 +507,7 @@
   // null, it creates a RenderFrameProxy in the target renderer process which is
   // used to route IPC messages.  Returns early if the RenderViewHost has
   // already been initialized for another RenderFrameHost.
-  bool InitRenderView(SiteInstance* site_instance,
+  bool InitRenderView(SiteInstanceGroup* site_instance_group,
                       RenderViewHostImpl* render_view_host,
                       RenderFrameProxyHost* proxy);
 
@@ -679,6 +679,7 @@
       bool was_server_redirect,
       bool cross_origin_opener_policy_mismatch,
       bool should_replace_current_entry,
+      bool force_new_browsing_instance,
       std::string* reason);
 
   // Returns a descriptor of the appropriate SiteInstance object for the given
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index a4f64c8..7d20301d 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -2465,8 +2465,9 @@
   // the first navigation, but when attaching a new window we don't navigate
   // before attaching. If the browser side is already initialized, the calls
   // below will just early return.
-  inner_render_manager->InitRenderView(inner_main_frame->GetSiteInstance(),
-                                       inner_render_view_host, nullptr);
+  inner_render_manager->InitRenderView(
+      inner_main_frame->GetSiteInstance()->group(), inner_render_view_host,
+      nullptr);
   if (!inner_render_manager->GetRenderWidgetHostView()) {
     inner_web_contents_impl->CreateRenderWidgetHostViewForRenderManager(
         inner_render_view_host);
@@ -3071,8 +3072,9 @@
   if (params.desired_renderer_state ==
       CreateParams::kInitializeAndWarmupRendererProcess) {
     if (!GetRenderManager()->current_frame_host()->IsRenderFrameLive()) {
-      GetRenderManager()->InitRenderView(site_instance.get(),
-                                         GetRenderViewHost(), nullptr);
+      GetRenderManager()->InitRenderView(
+          static_cast<SiteInstanceImpl*>(site_instance.get())->group(),
+          GetRenderViewHost(), nullptr);
     }
   }
 
diff --git a/content/browser/web_contents/web_contents_impl_browsertest.cc b/content/browser/web_contents/web_contents_impl_browsertest.cc
index 10bfc76..3c415139 100644
--- a/content/browser/web_contents/web_contents_impl_browsertest.cc
+++ b/content/browser/web_contents/web_contents_impl_browsertest.cc
@@ -1815,7 +1815,7 @@
   FrameTreeNode* frame = root->child_at(0);
   ASSERT_NE(nullptr, frame);
 
-  url::Replacements<char> clear_port;
+  GURL::Replacements clear_port;
   clear_port.ClearPort();
 
   // A dialog from the main frame.
diff --git a/content/browser/web_contents/web_contents_view_aura.cc b/content/browser/web_contents/web_contents_view_aura.cc
index eff6616..79485e4 100644
--- a/content/browser/web_contents/web_contents_view_aura.cc
+++ b/content/browser/web_contents/web_contents_view_aura.cc
@@ -1157,7 +1157,7 @@
       web_contents_->GetBrowserContext()->IsOffTheRecord()
           ? nullptr
           : std::make_unique<ui::DataTransferEndpoint>(
-                web_contents_->GetMainFrame()->GetLastCommittedOrigin()));
+                web_contents_->GetMainFrame()->GetLastCommittedURL()));
   WebContentsDelegate* delegate = web_contents_->GetDelegate();
   if (delegate && delegate->IsPrivileged())
     data->MarkAsFromPrivileged();
@@ -1495,7 +1495,7 @@
   auto* focused_frame = web_contents_->GetFocusedFrame();
   if (focused_frame && !web_contents_->GetBrowserContext()->IsOffTheRecord()) {
     drag_info.data_endpoint = ui::DataTransferEndpoint(
-        web_contents_->GetMainFrame()->GetLastCommittedOrigin());
+        web_contents_->GetMainFrame()->GetLastCommittedURL());
   }
 
   std::unique_ptr<DropData> drop_data = std::make_unique<DropData>();
diff --git a/content/browser/web_contents/web_contents_view_aura_unittest.cc b/content/browser/web_contents/web_contents_view_aura_unittest.cc
index 1983f03..34913c12 100644
--- a/content/browser/web_contents/web_contents_view_aura_unittest.cc
+++ b/content/browser/web_contents/web_contents_view_aura_unittest.cc
@@ -749,8 +749,7 @@
   EXPECT_TRUE(exchange_data);
   EXPECT_TRUE(exchange_data->GetSource());
   EXPECT_TRUE(exchange_data->GetSource()->IsUrlType());
-  EXPECT_TRUE(exchange_data->GetSource()->GetOrigin()->IsSameOriginWith(
-      url::Origin::Create(GURL(kGmailUrl))));
+  EXPECT_EQ(*(exchange_data->GetSource()->GetURL()), GURL(kGmailUrl));
 }
 
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
diff --git a/content/browser/webauth/authenticator_impl_unittest.cc b/content/browser/webauth/authenticator_impl_unittest.cc
index 5f65cd0..692295e 100644
--- a/content/browser/webauth/authenticator_impl_unittest.cc
+++ b/content/browser/webauth/authenticator_impl_unittest.cc
@@ -8341,6 +8341,83 @@
   ASSERT_EQ(pairings_.size(), 0u);
 }
 
+// ServerLinkValues contains keys that mimic those created by a site doing
+// caBLEv2 server-link.
+struct ServerLinkValues {
+  // This value would be provided by the site to the desktop, in a caBLE
+  // extension in the get() call.
+  device::CableDiscoveryData desktop_side;
+
+  // These values would be provided to the phone via a custom mechanism.
+  std::array<uint8_t, device::cablev2::kQRSecretSize> secret;
+  std::array<uint8_t, device::kP256X962Length> peer_identity;
+};
+
+// CreateServerLink simulates a site doing caBLEv2 server-link and calculates
+// server-link values that could be sent to the desktop and phone sides of a
+// transaction.
+static ServerLinkValues CreateServerLink() {
+  std::vector<uint8_t> seed(device::cablev2::kQRSeedSize);
+  base::RandBytes(seed.data(), seed.size());
+
+  bssl::UniquePtr<EC_GROUP> p256(
+      EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1));
+  bssl::UniquePtr<EC_KEY> ec_key(
+      EC_KEY_derive_from_secret(p256.get(), seed.data(), seed.size()));
+
+  ServerLinkValues ret;
+  base::RandBytes(ret.secret.data(), ret.secret.size());
+  CHECK_EQ(ret.peer_identity.size(),
+           EC_POINT_point2oct(p256.get(), EC_KEY_get0_public_key(ec_key.get()),
+                              POINT_CONVERSION_UNCOMPRESSED,
+                              ret.peer_identity.data(),
+                              ret.peer_identity.size(), /*ctx=*/nullptr));
+
+  ret.desktop_side.version = device::CableDiscoveryData::Version::V2;
+  ret.desktop_side.v2 = seed;
+  ret.desktop_side.v2->insert(ret.desktop_side.v2->end(), ret.secret.begin(),
+                              ret.secret.end());
+
+  return ret;
+}
+
+TEST_F(AuthenticatorCableV2Test, ServerLink) {
+  const ServerLinkValues server_link_1 = CreateServerLink();
+  const ServerLinkValues server_link_2 = CreateServerLink();
+  const std::vector<device::CableDiscoveryData> extension_values = {
+      server_link_1.desktop_side, server_link_2.desktop_side};
+
+  auto discovery = std::make_unique<device::cablev2::Discovery>(
+      device::FidoRequestType::kGetAssertion, network_context_.get(),
+      qr_generator_key_, std::move(ble_advert_events_),
+      /*pairings=*/std::vector<std::unique_ptr<device::cablev2::Pairing>>(),
+      /*contact_device_stream=*/nullptr, extension_values,
+      GetPairingCallback());
+
+  AuthenticatorEnvironmentImpl::GetInstance()
+      ->ReplaceDefaultDiscoveryFactoryForTesting(
+          std::make_unique<DiscoveryFactory>(std::move(discovery)));
+
+  // Both extension values should work, but we can only do a single transaction
+  // per test because a lot of state is setup for a test. Therefore pick one of
+  // the two to check, at random.
+  const auto& server_link =
+      (base::RandUint64() & 1) ? server_link_1 : server_link_2;
+
+  std::unique_ptr<device::cablev2::authenticator::Transaction> transaction =
+      device::cablev2::authenticator::TransactFromQRCode(
+          device::cablev2::authenticator::NewMockPlatform(
+              std::move(ble_advert_callback_), &virtual_device_,
+              /*observer=*/nullptr),
+          network_context_.get(), root_secret_, "Test Authenticator",
+          server_link.secret, server_link.peer_identity,
+          /*contact_id=*/absl::nullopt,
+          /*use_new_crypter_construction=*/false);
+
+  EXPECT_EQ(AuthenticatorMakeCredential().status, AuthenticatorStatus::SUCCESS);
+  EXPECT_EQ(pairings_.size(), 0u);
+}
+
 // AuthenticatorCableV2AuthenticatorTest tests aspects of the authenticator
 // implementation, rather than of the underlying caBLEv2 transport.
 class AuthenticatorCableV2AuthenticatorTest
diff --git a/content/browser/webid/fedcm_metrics.cc b/content/browser/webid/fedcm_metrics.cc
index 50cea03..ddabaf71 100644
--- a/content/browser/webid/fedcm_metrics.cc
+++ b/content/browser/webid/fedcm_metrics.cc
@@ -83,4 +83,13 @@
   UMA_HISTOGRAM_BOOLEAN("Blink.FedCm.WebContentsVisible", is_visible);
 }
 
+void RecordApprovedClientsExistence(bool has_approved_clients) {
+  UMA_HISTOGRAM_BOOLEAN("Blink.FedCm.ApprovedClientsExistence",
+                        has_approved_clients);
+}
+
+void RecordApprovedClientsSize(int size) {
+  UMA_HISTOGRAM_COUNTS_10000("Blink.FedCm.ApprovedClientsSize", size);
+}
+
 }  // namespace content
diff --git a/content/browser/webid/fedcm_metrics.h b/content/browser/webid/fedcm_metrics.h
index fb4d463..e391dcab 100644
--- a/content/browser/webid/fedcm_metrics.h
+++ b/content/browser/webid/fedcm_metrics.h
@@ -94,6 +94,12 @@
 // Records whether a user has left the page where the API is called when the
 // browser is ready to show the accounts dialog.
 void RecordWebContentsVisibilityUponReadyToShowDialog(bool is_visible);
+
+// Records whether an IDP returns an approved clients list in the response.
+void RecordApprovedClientsExistence(bool has_approved_clients);
+
+// Records the size of the approved clients list if applicable.
+void RecordApprovedClientsSize(int size);
 }  // namespace content
 
 #endif  // CONTENT_BROWSER_WEBID_FEDCM_METRICS_H_
diff --git a/content/browser/webid/federated_auth_navigation_throttle.cc b/content/browser/webid/federated_auth_navigation_throttle.cc
deleted file mode 100644
index 25c49c1..0000000
--- a/content/browser/webid/federated_auth_navigation_throttle.cc
+++ /dev/null
@@ -1,225 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "federated_auth_navigation_throttle.h"
-
-#include "base/time/time.h"
-#include "content/browser/webid/flags.h"
-#include "content/browser/webid/idp_network_request_manager.h"
-#include "content/browser/webid/redirect_uri_data.h"
-#include "content/public/browser/browser_context.h"
-#include "content/public/browser/browser_thread.h"
-#include "content/public/browser/content_browser_client.h"
-#include "content/public/browser/federated_identity_request_permission_context_delegate.h"
-#include "content/public/browser/federated_identity_sharing_permission_context_delegate.h"
-#include "content/public/browser/navigation_controller.h"
-#include "content/public/browser/navigation_handle.h"
-#include "content/public/browser/web_contents.h"
-#include "content/public/common/content_client.h"
-#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
-#include "net/base/url_util.h"
-#include "net/http/http_request_headers.h"
-#include "ui/base/page_transition_types.h"
-
-namespace content {
-
-namespace {
-
-// Determines if the source and destination would need special permission to
-// get access to 1st party state in each other's contexts. Currently this only
-// determines if they are same-site based on the public suffix list, but later
-// should account for other considerations such as first-party sets or
-// enterprise policies.
-// Returns true if the source and destination would need permission. Both
-// arguments must be absolute URLs.
-bool AreCookieIsolatedPrincipals(url::Origin src_origin,
-                                 url::Origin dest_origin) {
-  if (src_origin.scheme() != dest_origin.scheme()) {
-    return true;
-  }
-
-  if (!net::registry_controlled_domains::SameDomainOrHost(
-          src_origin, dest_origin,
-          net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES)) {
-    return true;
-  }
-
-  return false;
-}
-
-}  // namespace
-
-// static
-std::unique_ptr<NavigationThrottle>
-FederatedAuthNavigationThrottle::MaybeCreateThrottleFor(
-    NavigationHandle* handle) {
-  if (!IsFedCmInterceptionEnabled() || handle->GetParentFrameOrOuterDocument())
-    return nullptr;
-
-  return std::make_unique<FederatedAuthNavigationThrottle>(handle);
-}
-
-FederatedAuthNavigationThrottle::FederatedAuthNavigationThrottle(
-    NavigationHandle* handle)
-    : NavigationThrottle(handle) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-}
-
-FederatedAuthNavigationThrottle::~FederatedAuthNavigationThrottle() {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-}
-
-NavigationThrottle::ThrottleCheckResult
-FederatedAuthNavigationThrottle::WillStartRequest() {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-
-  GURL navigation_url = navigation_handle()->GetURL();
-
-  // Explicit FedCM requests are exempt from throttling.
-  const auto headers = navigation_handle()->GetRequestHeaders();
-  if (headers.HasHeader(kSecFedCmCsrfHeader))
-    return NavigationThrottle::PROCEED;
-
-  const auto initiator_origin = navigation_handle()->GetInitiatorOrigin();
-  url::Origin navigation_origin = url::Origin::Create(navigation_url);
-
-  if (IsFederationRequest(navigation_url) && initiator_origin &&
-      AreCookieIsolatedPrincipals(*initiator_origin, navigation_origin)) {
-    net::GetValueForKeyInQuery(navigation_url, "redirect_uri", &redirect_uri_);
-
-    // Permission dialog is skipped if this RP/IdP pair already have the
-    // Identity Request permission.
-    auto* request_permission_delegate =
-        navigation_handle()
-            ->GetWebContents()
-            ->GetBrowserContext()
-            ->GetFederatedIdentityRequestPermissionContext();
-    if (request_permission_delegate &&
-        request_permission_delegate->HasRequestPermission(*initiator_origin,
-                                                          navigation_origin)) {
-      RedirectUriData::Set(navigation_handle()->GetWebContents(),
-                           redirect_uri_);
-      return NavigationThrottle::PROCEED;
-    }
-
-    request_dialog_controller_ =
-        GetContentClient()->browser()->CreateIdentityRequestDialogController();
-    request_dialog_controller_->ShowInitialPermissionDialog(
-        navigation_handle()->GetWebContents(), navigation_url,
-        IdentityRequestDialogController::PermissionDialogMode::kStateless,
-        base::BindOnce(&FederatedAuthNavigationThrottle::OnSigninApproved,
-                       weak_ptr_factory_.GetWeakPtr()));
-    return NavigationThrottle::DEFER;
-  } else if (IsFederationResponse(navigation_url)) {
-    // TODO(kenrb): Currently no action, this may proceed. Two things to
-    // change here:
-    //     1) Check the redirect_uri and verify we are going back to the
-    //        original source, from which the user consented to login.
-    //        Set the session management permission if the IdP wants it.
-    //        First, that permission has to be created.
-    //        https://crbug.com/1223570.
-    //     2) (In the eventual future where directed identifiers are
-    //        important) Prompt the user for permission to share personalized
-    //        identifiers and store the FEDERATED_IDENTITY_SHARING
-    //        setting. https://crbug.com/1141125.
-    return NavigationThrottle::PROCEED;
-  }
-
-  return NavigationThrottle::PROCEED;
-}
-
-bool FederatedAuthNavigationThrottle::IsFederationRequest(GURL url) {
-  // Matches OAuth Requests:
-  // TODO: make a separation between OpenID Connect and OAuth?
-  // TODO: match SAML requests.
-
-  if (!url.has_query()) {
-    return false;
-  }
-
-  std::string client_id;
-  if (!net::GetValueForKeyInQuery(url, "client_id", &client_id)) {
-    return false;
-  }
-
-  std::string scope;
-  if (!net::GetValueForKeyInQuery(url, "scope", &scope)) {
-    return false;
-  }
-
-  std::string redirect_uri;
-  if (!net::GetValueForKeyInQuery(url, "redirect_uri", &redirect_uri)) {
-    return false;
-  }
-
-  return true;
-}
-
-bool FederatedAuthNavigationThrottle::IsFederationResponse(GURL url) {
-  // Matches an expected OAuth Response
-  if (!RedirectUriData::Get(navigation_handle()->GetWebContents())) {
-    return false;
-  }
-  GURL redirect_url = GURL(
-      RedirectUriData::Get(navigation_handle()->GetWebContents())->Value());
-  if (url.DeprecatedGetOriginAsURL() ==
-          redirect_url.DeprecatedGetOriginAsURL() &&
-      url.path() == redirect_url.path()) {
-    return true;
-  }
-  return false;
-}
-
-NavigationThrottle::ThrottleCheckResult
-FederatedAuthNavigationThrottle::WillRedirectRequest() {
-  return WillStartRequest();
-}
-
-void FederatedAuthNavigationThrottle::OnSigninApproved(
-    IdentityRequestDialogController::UserApproval approval) {
-  if (approval == IdentityRequestDialogController::UserApproval::kApproved) {
-    auto* request_permission_delegate =
-        navigation_handle()
-            ->GetWebContents()
-            ->GetBrowserContext()
-            ->GetFederatedIdentityRequestPermissionContext();
-    const auto initiator_origin = navigation_handle()->GetInitiatorOrigin();
-    if (request_permission_delegate && initiator_origin) {
-      request_permission_delegate->GrantRequestPermission(
-          *initiator_origin,
-          url::Origin::Create(navigation_handle()->GetURL()));
-    }
-    RedirectUriData::Set(navigation_handle()->GetWebContents(), redirect_uri_);
-    Resume();
-    return;
-  }
-
-  CancelDeferredNavigation(NavigationThrottle::CANCEL);
-}
-
-void FederatedAuthNavigationThrottle::OnTokenProvisionApproved(
-    IdentityRequestDialogController::UserApproval approval) {
-  if (approval == IdentityRequestDialogController::UserApproval::kApproved) {
-    auto* sharing_permission_delegate =
-        navigation_handle()
-            ->GetWebContents()
-            ->GetBrowserContext()
-            ->GetFederatedIdentitySharingPermissionContext();
-    const auto initiator_origin = navigation_handle()->GetInitiatorOrigin();
-    if (sharing_permission_delegate && initiator_origin) {
-      sharing_permission_delegate->GrantSharingPermission(
-          *initiator_origin,
-          url::Origin::Create(navigation_handle()->GetURL()));
-    }
-    Resume();
-    return;
-  }
-  CancelDeferredNavigation(NavigationThrottle::CANCEL);
-}
-
-const char* FederatedAuthNavigationThrottle::GetNameForLogging() {
-  return "FederatedAuthNavigationThrottle";
-}
-
-}  // namespace content
diff --git a/content/browser/webid/federated_auth_navigation_throttle.h b/content/browser/webid/federated_auth_navigation_throttle.h
deleted file mode 100644
index 2065ecf..0000000
--- a/content/browser/webid/federated_auth_navigation_throttle.h
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CONTENT_BROWSER_WEBID_FEDERATED_AUTH_NAVIGATION_THROTTLE_H_
-#define CONTENT_BROWSER_WEBID_FEDERATED_AUTH_NAVIGATION_THROTTLE_H_
-
-#include <memory>
-
-#include "base/memory/weak_ptr.h"
-#include "base/timer/timer.h"
-#include "content/common/content_export.h"
-#include "content/public/browser/identity_request_dialog_controller.h"
-#include "content/public/browser/navigation_throttle.h"
-
-namespace content {
-class NavigationHandle;
-
-// Used to delay a navigation while the browser gathers the user's
-// awareness / permission of the tracking risks involved in third party
-// federated identity flows.
-class CONTENT_EXPORT FederatedAuthNavigationThrottle
-    : public NavigationThrottle {
- public:
-  explicit FederatedAuthNavigationThrottle(NavigationHandle* handle);
-  ~FederatedAuthNavigationThrottle() override;
-  FederatedAuthNavigationThrottle(const FederatedAuthNavigationThrottle&) =
-      delete;
-  FederatedAuthNavigationThrottle& operator=(
-      const FederatedAuthNavigationThrottle&) = delete;
-
-  static std::unique_ptr<NavigationThrottle> MaybeCreateThrottleFor(
-      NavigationHandle* handle);
-
-  // NavigationThrottle implementation:
-  NavigationThrottle::ThrottleCheckResult WillStartRequest() override;
-  NavigationThrottle::ThrottleCheckResult WillRedirectRequest() override;
-  const char* GetNameForLogging() override;
-
- private:
-  void OnSigninApproved(IdentityRequestDialogController::UserApproval approval);
-  void OnTokenProvisionApproved(
-      IdentityRequestDialogController::UserApproval approval);
-
-  bool IsFederationRequest(GURL url);
-  bool IsFederationResponse(GURL url);
-
-  std::unique_ptr<IdentityRequestDialogController> request_dialog_controller_;
-  std::string redirect_uri_;
-  base::WeakPtrFactory<FederatedAuthNavigationThrottle> weak_ptr_factory_{this};
-};
-
-}  // namespace content
-
-#endif  // CONTENT_BROWSER_WEBID_FEDERATED_AUTH_NAVIGATION_THROTTLE_H_
diff --git a/content/browser/webid/federated_auth_navigation_throttle_unittest.cc b/content/browser/webid/federated_auth_navigation_throttle_unittest.cc
deleted file mode 100644
index fc30b8b6..0000000
--- a/content/browser/webid/federated_auth_navigation_throttle_unittest.cc
+++ /dev/null
@@ -1,234 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "content/browser/webid/federated_auth_navigation_throttle.h"
-
-#include <ostream>
-#include <string>
-
-#include "base/memory/raw_ptr.h"
-#include "base/test/scoped_feature_list.h"
-#include "content/browser/webid/test/fake_identity_request_dialog_controller.h"
-#include "content/browser/webid/test/webid_test_content_browser_client.h"
-#include "content/public/browser/content_browser_client.h"
-#include "content/public/browser/identity_request_dialog_controller.h"
-#include "content/public/browser/navigation_throttle.h"
-#include "content/public/browser/render_frame_host.h"
-#include "content/public/common/content_client.h"
-#include "content/public/common/content_features.h"
-#include "content/public/test/mock_navigation_handle.h"
-#include "content/public/test/navigation_simulator.h"
-#include "content/public/test/test_renderer_host.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
-#include "third_party/blink/public/common/features.h"
-
-namespace content {
-
-namespace {
-
-typedef std::unique_ptr<base::test::ScopedFeatureList> MovableScopedFeatureList;
-
-constexpr char kOauthRequestParams[] =
-    "?client_id=12345&scope=67890&"
-    "redirect_uri=https%3A%2F%2Frp.example%2F";
-
-struct CrossSiteTestCase {
-  std::string test_name;
-  std::string idp_origin;
-  std::string rp_origin;
-  NavigationThrottle::ThrottleAction expected_action;
-};
-
-std::ostream& operator<<(std::ostream& os, const CrossSiteTestCase& testcase) {
-  std::string name;
-  base::ReplaceChars(testcase.test_name, " ", "", &name);
-  return os << name;
-}
-
-static const CrossSiteTestCase kCrossSiteTests[]{
-    {"RP is subdomain of registered IdP domain", "https://idp.example/",
-     "https://subdomain.idp.example", NavigationThrottle::PROCEED},
-    {"IdP is subdomain of registered RP domain",
-     "https://subdomain.idp.example/", "https://idp.example",
-     NavigationThrottle::PROCEED},
-    {"Sibling subdomains of registered domain", "https://a.idp.example/",
-     "https://b.idp.example", NavigationThrottle::PROCEED},
-    {"Subdomain of listed private eTLD", "https://x.github.io/",
-     "https://y.github.io/", NavigationThrottle::DEFER},
-    {"Subdomain of listed public eTLD", "https://example1.co.uk/",
-     "https://example2.co.uk/", NavigationThrottle::DEFER},
-    {"Same domain with different port number", "https://idp.example:1234/",
-     "https://idp.example", NavigationThrottle::PROCEED},
-    // This result should change when first-party sets are accommodated.
-    {"Same first party set", "https://accounts.google.com/",
-     "https://youtube.com", NavigationThrottle::DEFER},
-    {"Same domain with different scheme", "http://idp.example/",
-     "https://idp.example", NavigationThrottle::DEFER},
-};
-
-}  // namespace
-
-class FederatedAuthNavigationThrottleTest : public RenderViewHostTestHarness {
- public:
-  FederatedAuthNavigationThrottleTest() = default;
-  ~FederatedAuthNavigationThrottleTest() override = default;
-
-  void SetUp() override {
-    RenderViewHostTestHarness::SetUp();
-    test_browser_client_ = std::make_unique<WebIdTestContentBrowserClient>();
-    auto controller = std::make_unique<FakeIdentityRequestDialogController>(
-        absl::nullopt, absl::nullopt, "id_token");
-    test_browser_client_->SetIdentityRequestDialogController(
-        std::move(controller));
-    old_client_ = SetBrowserClientForTesting(test_browser_client_.get());
-  }
-
-  void TearDown() override {
-    CHECK_EQ(SetBrowserClientForTesting(old_client_),
-             test_browser_client_.get());
-    RenderViewHostTestHarness::TearDown();
-  }
-
- private:
-  std::unique_ptr<WebIdTestContentBrowserClient> test_browser_client_;
-  raw_ptr<ContentBrowserClient> old_client_ = nullptr;
-};
-
-class FederatedAuthNavigationThrottleWithFlagEnabledTest
-    : public FederatedAuthNavigationThrottleTest {
- public:
-  FederatedAuthNavigationThrottleWithFlagEnabledTest() {
-    feature_list_.InitAndEnableFeatureWithParameters(
-        features::kFedCm,
-        {{features::kFedCmInterceptionFieldTrialParamName, "true"}});
-  }
-
- private:
-  base::test::ScopedFeatureList feature_list_;
-};
-
-// Test that the throttle is not created when the feature flag is not set.
-TEST_F(FederatedAuthNavigationThrottleTest, InstantiateWithoutFlag) {
-  GURL url("https://idp.example");
-
-  content::RenderFrameHostTester::For(main_rfh())
-      ->InitializeRenderFrameIfNeeded();
-
-  MockNavigationHandle top_frame_handle(url, main_rfh());
-
-  auto throttle = FederatedAuthNavigationThrottle::MaybeCreateThrottleFor(
-      &top_frame_handle);
-  ASSERT_FALSE(throttle);
-}
-
-// Test that the throttle is created when the FedCM feature is turned on
-// and only when the frame is a main frame.
-TEST_F(FederatedAuthNavigationThrottleWithFlagEnabledTest, Instantiate) {
-  GURL url("https://idp.example");
-  GURL url_child("https://child.example");
-
-  content::RenderFrameHostTester::For(main_rfh())
-      ->InitializeRenderFrameIfNeeded();
-  RenderFrameHost* child_rfh =
-      content::RenderFrameHostTester::For(main_rfh())->AppendChild("child");
-
-  MockNavigationHandle top_frame_handle(url, main_rfh());
-  MockNavigationHandle child_frame_handle(url_child, child_rfh);
-
-  // Attempt to create throttle for a child frame with features::kFedCm set.
-  auto throttle = FederatedAuthNavigationThrottle::MaybeCreateThrottleFor(
-      &child_frame_handle);
-  ASSERT_FALSE(throttle);
-
-  // Attempt to create throttle for the main frame with features::kFedCm set.
-  throttle = FederatedAuthNavigationThrottle::MaybeCreateThrottleFor(
-      &top_frame_handle);
-  ASSERT_TRUE(throttle);
-}
-
-// Verify an OAuth request is throttled.
-TEST_F(FederatedAuthNavigationThrottleWithFlagEnabledTest,
-       ThrottleAuthRequest) {
-  GURL idp_url(
-      "https://idp.example/?client_id=12345&scope=67890&"
-      "redirect_uri=https%3A%2F%2Frp.example%2F");
-
-  MockNavigationHandle handle(idp_url, main_rfh());
-  handle.set_initiator_origin(url::Origin::Create(GURL("https://rp.example")));
-
-  auto throttle =
-      FederatedAuthNavigationThrottle::MaybeCreateThrottleFor(&handle);
-  ASSERT_TRUE(throttle);
-
-  EXPECT_EQ(NavigationThrottle::DEFER, throttle->WillStartRequest().action());
-}
-
-class CrossSiteFederatedAuthNavigationThrottleTest
-    : public FederatedAuthNavigationThrottleWithFlagEnabledTest,
-      public ::testing::WithParamInterface<CrossSiteTestCase> {};
-
-INSTANTIATE_TEST_SUITE_P(CrossSiteThrottlingTests,
-                         CrossSiteFederatedAuthNavigationThrottleTest,
-                         ::testing::ValuesIn(kCrossSiteTests),
-                         ::testing::PrintToStringParamName());
-
-// Verify same-site OAuth requests are not throttled.
-TEST_P(CrossSiteFederatedAuthNavigationThrottleTest, SameSiteAuthRequest) {
-  CrossSiteTestCase test_case = GetParam();
-  GURL idp_url(test_case.idp_origin + kOauthRequestParams);
-
-  MockNavigationHandle handle(idp_url, main_rfh());
-  handle.set_initiator_origin(url::Origin::Create(GURL(test_case.rp_origin)));
-
-  auto throttle =
-      FederatedAuthNavigationThrottle::MaybeCreateThrottleFor(&handle);
-  ASSERT_TRUE(throttle);
-
-  EXPECT_EQ(test_case.expected_action, throttle->WillStartRequest().action());
-}
-
-class ContentSubresourceFilterThrottleManagerFencedFramesTest
-    : public FederatedAuthNavigationThrottleWithFlagEnabledTest {
- public:
-  ContentSubresourceFilterThrottleManagerFencedFramesTest() {
-    scoped_feature_list_.InitAndEnableFeatureWithParameters(
-        blink::features::kFencedFrames, {{"implementation_type", "mparch"}});
-  }
-  ~ContentSubresourceFilterThrottleManagerFencedFramesTest() override = default;
-
-  content::RenderFrameHost* CreateFencedFrame(
-      content::RenderFrameHost* parent) {
-    content::RenderFrameHost* fenced_frame =
-        content::RenderFrameHostTester::For(parent)->AppendFencedFrame();
-    return fenced_frame;
-  }
-
- private:
-  base::test::ScopedFeatureList scoped_feature_list_;
-};
-
-TEST_F(ContentSubresourceFilterThrottleManagerFencedFramesTest,
-       BlockThrottleCreationForFencedFrames) {
-  const GURL kUrl("https://idp.example");
-  GURL kFencedFrameUrl("https://child.example");
-
-  content::RenderFrameHostTester::For(main_rfh())
-      ->InitializeRenderFrameIfNeeded();
-  RenderFrameHost* fenced_frame_rfh = CreateFencedFrame(main_rfh());
-
-  MockNavigationHandle top_frame_handle(kUrl, main_rfh());
-  MockNavigationHandle fenced_frame_handle(kFencedFrameUrl, fenced_frame_rfh);
-
-  // Should be able to create throttle for the main frame.
-  auto throttle = FederatedAuthNavigationThrottle::MaybeCreateThrottleFor(
-      &top_frame_handle);
-  ASSERT_TRUE(throttle);
-
-  // A throttle should not be allowed for a fenced frame.
-  throttle = FederatedAuthNavigationThrottle::MaybeCreateThrottleFor(
-      &fenced_frame_handle);
-  ASSERT_FALSE(throttle);
-}
-
-}  // namespace content
diff --git a/content/browser/webid/flags.cc b/content/browser/webid/flags.cc
index a9c5880b..d01563a 100644
--- a/content/browser/webid/flags.cc
+++ b/content/browser/webid/flags.cc
@@ -20,9 +20,4 @@
       features::kFedCm, features::kFedCmAutoSigninFieldTrialParamName, false);
 }
 
-bool IsFedCmInterceptionEnabled() {
-  return GetFieldTrialParamByFeatureAsBool(
-      features::kFedCm, features::kFedCmInterceptionFieldTrialParamName, false);
-}
-
 }  // namespace content
diff --git a/content/browser/webid/flags.h b/content/browser/webid/flags.h
index 8201e70..d50da34 100644
--- a/content/browser/webid/flags.h
+++ b/content/browser/webid/flags.h
@@ -15,9 +15,6 @@
 // Whether FedCM auto sign-in is enabled.
 bool IsFedCmAutoSigninEnabled();
 
-// Whether FedCM HTTP filtering is enabled.
-bool IsFedCmInterceptionEnabled();
-
 }  // namespace content
 
 #endif  // CONTENT_BROWSER_WEBID_FLAGS_H_
diff --git a/content/browser/webid/idp_network_request_manager.cc b/content/browser/webid/idp_network_request_manager.cc
index 060438ad..99f3d6e 100644
--- a/content/browser/webid/idp_network_request_manager.cc
+++ b/content/browser/webid/idp_network_request_manager.cc
@@ -7,6 +7,7 @@
 #include "base/base64.h"
 #include "base/json/json_writer.h"
 #include "content/browser/renderer_host/render_frame_host_impl.h"
+#include "content/browser/webid/fedcm_metrics.h"
 #include "content/public/browser/identity_request_dialog_controller.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/storage_partition.h"
@@ -181,6 +182,8 @@
   if (!(id && email && name))
     return absl::nullopt;
 
+  RecordApprovedClientsExistence(approved_clients != nullptr);
+
   absl::optional<LoginState> approved_value;
   if (approved_clients) {
     for (const base::Value& entry : approved_clients->GetListDeprecated()) {
@@ -195,6 +198,7 @@
       // kSignUp instead of leaving as nullopt.
       approved_value = LoginState::kSignUp;
     }
+    RecordApprovedClientsSize(approved_clients->GetList().size());
   }
 
   return content::IdentityRequestAccount(
diff --git a/content/browser/webid/idp_network_request_manager_unittest.cc b/content/browser/webid/idp_network_request_manager_unittest.cc
index 768dc09..033739d 100644
--- a/content/browser/webid/idp_network_request_manager_unittest.cc
+++ b/content/browser/webid/idp_network_request_manager_unittest.cc
@@ -11,6 +11,7 @@
 #include <utility>
 #include "base/strings/stringprintf.h"
 #include "base/test/bind.h"
+#include "base/test/metrics/histogram_tester.h"
 #include "base/test/task_environment.h"
 #include "base/values.h"
 #include "content/public/browser/identity_request_dialog_controller.h"
@@ -696,6 +697,69 @@
       SendRevokeRequestAndWaitForResponse("xxx", "yyy", net::HTTP_FORBIDDEN);
   ASSERT_EQ(RevokeResponse::kError, status);
 }
+
+// Tests that we correctly records metrics regarding approved_clients.
+TEST_F(IdpNetworkRequestManagerTest, RecordApprovedClientsMetrics) {
+  base::HistogramTester histogram_tester;
+  bool called = false;
+  auto interceptor =
+      base::BindLambdaForTesting([&](const network::ResourceRequest& request) {
+        called = true;
+        EXPECT_EQ(GURL(kTestAccountsEndpoint), request.url);
+        EXPECT_EQ(request.request_body, nullptr);
+        EXPECT_FALSE(request.referrer.is_valid());
+      });
+  test_url_loader_factory().SetInterceptor(interceptor);
+
+  const char test_accounts_json[] = R"({
+  "accounts" : [
+    {
+      "id" : "1",
+      "email": "ken@idp.test",
+      "name": "Ken R. Example",
+      "approved_clients": []
+    },
+    {
+      "id" : "2",
+      "email": "jim@idp.test",
+      "name": "Jim R. Example",
+      "approved_clients": ["xxx"]
+    },
+    {
+      "id" : "3",
+      "email": "rashida@idp.test",
+      "name": "Rashida R. Example",
+      "approved_clients": ["xxx", "yyy"]
+    },
+    {
+      "id" : "4",
+      "email": "wei@idp.test",
+      "name": "Wei R. Example"
+    }
+   ]
+  })";
+
+  FetchStatus accounts_response;
+  AccountList accounts;
+  std::tie(accounts_response, accounts) =
+      SendAccountsRequestAndWaitForResponse(test_accounts_json, "xxx");
+
+  EXPECT_TRUE(called);
+  EXPECT_EQ(FetchStatus::kSuccess, accounts_response);
+  ASSERT_EQ(4ul, accounts.size());
+
+  histogram_tester.ExpectTotalCount("Blink.FedCm.ApprovedClientsExistence", 4);
+  histogram_tester.ExpectBucketCount("Blink.FedCm.ApprovedClientsExistence", 1,
+                                     3);
+  histogram_tester.ExpectBucketCount("Blink.FedCm.ApprovedClientsExistence", 0,
+                                     1);
+
+  histogram_tester.ExpectTotalCount("Blink.FedCm.ApprovedClientsSize", 3);
+  histogram_tester.ExpectBucketCount("Blink.FedCm.ApprovedClientsSize", 0, 1);
+  histogram_tester.ExpectBucketCount("Blink.FedCm.ApprovedClientsSize", 1, 1);
+  histogram_tester.ExpectBucketCount("Blink.FedCm.ApprovedClientsSize", 2, 1);
+}
+
 }  // namespace
 
 }  // namespace content
diff --git a/content/public/browser/navigation_controller.h b/content/public/browser/navigation_controller.h
index c2a9711..085fb5f 100644
--- a/content/public/browser/navigation_controller.h
+++ b/content/public/browser/navigation_controller.h
@@ -294,6 +294,11 @@
 
     // Indicates that this navigation is for PDF content in a renderer.
     bool is_pdf = false;
+
+    // Indicates this navigation should use a new BrowsingInstance. For example,
+    // this is used in web platform tests to guarantee that each test starts in
+    // a fresh BrowsingInstance.
+    bool force_new_browsing_instance = false;
   };
 
   // Disables checking for a repost and prompting the user. This is used during
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc
index c3c9fbe..75b671c 100644
--- a/content/public/common/content_features.cc
+++ b/content/public/common/content_features.cc
@@ -322,10 +322,6 @@
 // sign-in is enabled.
 const char kFedCmAutoSigninFieldTrialParamName[] = "AutoSignin";
 
-// Field trial boolean parameter which indicates whether FedCM HTTP filtering is
-// enabled.
-const char kFedCmInterceptionFieldTrialParamName[] = "Interception";
-
 // Enables usage of First Party Sets to determine cookie availability.
 constexpr base::Feature kFirstPartySets{"FirstPartySets",
                                         base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/content/public/common/content_features.h b/content/public/common/content_features.h
index 3c96a31..c74ee91 100644
--- a/content/public/common/content_features.h
+++ b/content/public/common/content_features.h
@@ -82,7 +82,6 @@
     kExtraSafelistedRequestHeadersForOutOfBlinkCors;
 CONTENT_EXPORT extern const base::Feature kFedCm;
 CONTENT_EXPORT extern const char kFedCmAutoSigninFieldTrialParamName[];
-CONTENT_EXPORT extern const char kFedCmInterceptionFieldTrialParamName[];
 CONTENT_EXPORT extern const base::Feature kFirstPartySets;
 CONTENT_EXPORT extern const base::FeatureParam<bool> kFirstPartySetsIsDogfooder;
 CONTENT_EXPORT extern const base::Feature kFirstPartySetsV2ComponentFormat;
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index a3f6f1ca..3f9bed0 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -2296,7 +2296,6 @@
     "../browser/web_package/web_bundle_reader_unittest.cc",
     "../browser/web_package/web_bundle_url_loader_factory_unittest.cc",
     "../browser/web_package/web_bundle_utils_unittest.cc",
-    "../browser/webid/federated_auth_navigation_throttle_unittest.cc",
     "../browser/webid/federated_auth_request_impl_unittest.cc",
     "../browser/webid/idp_network_request_manager_unittest.cc",
     "../browser/webid/test/fake_identity_request_dialog_controller.cc",
diff --git a/content/test/content_browser_test_test.cc b/content/test/content_browser_test_test.cc
index 55b31164..b406e26 100644
--- a/content/test/content_browser_test_test.cc
+++ b/content/test/content_browser_test_test.cc
@@ -99,15 +99,8 @@
 #define USE_EXTERNAL_SYMBOLIZER 0
 #endif
 
-// Flaky on Linux: crbug.com/1223763
-#if BUILDFLAG(IS_LINUX)
-#define MAYBE_RendererCrashCallStack DISABLED_RendererCrashCallStack
-#else
-#define MAYBE_RendererCrashCallStack RendererCrashCallStack
-#endif
-
 // Tests that browser tests print the callstack when a child process crashes.
-IN_PROC_BROWSER_TEST_F(ContentBrowserTest, MAYBE_RendererCrashCallStack) {
+IN_PROC_BROWSER_TEST_F(ContentBrowserTest, RendererCrashCallStack) {
   base::ScopedAllowBlockingForTesting allow_blocking;
 
   base::CommandLine new_test = CreateCommandLine();
diff --git a/content/test/data/attribution_reporting/register_source_headers_all_params.html.mock-http-headers b/content/test/data/attribution_reporting/register_source_headers_all_params.html.mock-http-headers
index a739139..ef3dae2 100644
--- a/content/test/data/attribution_reporting/register_source_headers_all_params.html.mock-http-headers
+++ b/content/test/data/attribution_reporting/register_source_headers_all_params.html.mock-http-headers
@@ -1,2 +1,2 @@
 HTTP/1.1 200 OK
-Attribution-Reporting-Register-Source:{"source_event_id":"5","destination":"https://advertiser.example", "priority":"10", "expiry": "1000", "debug_key":"789"}
+Attribution-Reporting-Register-Source:{"source_event_id":"5","destination":"https://advertiser.example", "priority":"10", "expiry": "1000", "debug_key":"789","filter_data":{"a":[],"b":["1","2"]}}
diff --git a/content/test/gpu/gpu_tests/test_expectations/gpu_process_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/gpu_process_expectations.txt
index 807fd2f..f39644e7 100644
--- a/content/test/gpu/gpu_tests/test_expectations/gpu_process_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/gpu_process_expectations.txt
@@ -118,6 +118,9 @@
 crbug.com/1261260 [ android android-nexus-5x ] GpuProcess_webgpu_iframe_removed [ Failure ]
 crbug.com/1261260 [ android android-shield-android-tv ] GpuProcess_webgpu_iframe_removed [ Failure ]
 
+# Flaky Vulkan Loader Timeout
+crbug.com/1275738 [ linux angle-swiftshader ] GpuProcess_disable_gpu [ RetryOnFailure ]
+
 #######################################################################
 # Automated Entries After This Point - Do Not Manually Add Below Here #
 #######################################################################
diff --git a/content/web_test/browser/web_test_control_host.cc b/content/web_test/browser/web_test_control_host.cc
index 9b9d8b1..3b50d6c 100644
--- a/content/web_test/browser/web_test_control_host.cc
+++ b/content/web_test/browser/web_test_control_host.cc
@@ -1818,6 +1818,12 @@
   params.transition_type = ui::PageTransitionFromInt(ui::PAGE_TRANSITION_TYPED);
   params.should_clear_history_list = true;
   params.initiator_origin = url::Origin();  // Opaque initiator.
+  // We should always reset the browsing instance, but it slows down tests
+  // significantly. For efficiency, this is limited to tests known to be
+  // affected.
+  params.force_new_browsing_instance =
+      base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kResetBrowsingInstanceBetweenTests);
   web_contents->GetController().LoadURLWithParams(params);
 
   // The navigation might have to wait for before unload handler to execute. The
diff --git a/content/web_test/common/web_test_switches.cc b/content/web_test/common/web_test_switches.cc
index 18b5b6d3..5482992 100644
--- a/content/web_test/common/web_test_switches.cc
+++ b/content/web_test/common/web_test_switches.cc
@@ -42,6 +42,12 @@
 const char kDisableAutoWPTOriginIsolation[] =
     "disable-auto-wpt-origin-isolation";
 
+// Forces each web test to be run in a new BrowsingInstance. Required for origin
+// isolation web tests where the BrowsingInstance retains state from origin
+// isolation requests, but this flag may benefit other web tests.
+const char kResetBrowsingInstanceBetweenTests[] =
+    "reset-browsing-instance-between-tests";
+
 // This makes us disable some web-platform runtime features so that we test
 // content_shell as if it was a stable release. It is only followed when
 // kRunWebTest is set. For the features' level, see
diff --git a/content/web_test/common/web_test_switches.h b/content/web_test/common/web_test_switches.h
index d1cb960..34f1d93 100644
--- a/content/web_test/common/web_test_switches.h
+++ b/content/web_test/common/web_test_switches.h
@@ -23,6 +23,7 @@
 extern const char kStableReleaseMode[];
 extern const char kDisableHeadlessMode[];
 extern const char kDisableAutoWPTOriginIsolation[];
+extern const char kResetBrowsingInstanceBetweenTests[];
 
 #if BUILDFLAG(IS_WIN)
 extern const char kRegisterFontFiles[];
diff --git a/docs/security/severity-guidelines.md b/docs/security/severity-guidelines.md
index 06d0166..db6c56b 100644
--- a/docs/security/severity-guidelines.md
+++ b/docs/security/severity-guidelines.md
@@ -8,9 +8,19 @@
 [security release management page](https://www.chromium.org/Home/chromium-security/security-release-management)
 for guidance on how to release fixes based on severity.
 
-Any significant mitigating factors, such as unusual or additional user
-interaction, or running Chrome with a specific command line flag or non-default
-feature enabled, may reduce an issue’s severity by one or more levels.
+Any significant mitigating factors will generally reduce an issue's severity by one or
+more levels:
+* Not web accessible, reliant solely on direct UI interaction to trigger.
+* Unusual or unlikely user interaction will normally reduce severity by one
+  level. This means interaction which may sometimes occur, but would not be
+  typical of an average user engaging with Chrome or a particular feature in
+  Chrome, nor could a user be easily convinced to perform by a persuasive web page.
+* Requiring profile destruction or browser shutdown will normally reduce
+  severity by one level.
+
+Bugs that require implausible interaction, interactions a user would not
+realistically be convinced to perform, will generally be downgraded to a
+functional bug and not considered a security bug.
 
 Conversely, we do not consider it a mitigating factor if a vulnerability applies
 only to a particular group of users. For instance, a Critical vulnerability is
@@ -63,6 +73,12 @@
 that of a critical severity bug, but they require the precondition of a
 compromised renderer. (Bugs which involve using [MojoJS](../../mojo/public/js/README.md)
 to trigger an exploitable browser process crash usually fall into this category).
+Another example are bugs that result in memory corruption in the browser
+process, which would normally be critical severity, but require browser shutdown
+or profile destruction, which would lower these issues to high severity. A
+bug with the precondition of browser shutdown or profile destruction should be
+considered to have a maximum severity of high and could potentially be
+reduced by other mitigating factors.
 
 They are normally assigned priority **Pri-1** and assigned to the current stable
 milestone (or earliest milestone affected). For high severity bugs,
@@ -78,7 +94,7 @@
 bugs fall into this category, as they allow script execution in the context of
 an arbitrary origin ([534923](https://crbug.com/534923)).
 * A bug that allows arbitrary code execution within the confines of the sandbox,
-such as renderer or GPU process memory corruption
+such as renderer, network, or GPU process memory corruption
 ([570427](https://crbug.com/570427), [468936](https://crbug.com/468936)).
 * Complete control over the apparent origin in the omnibox
 ([76666](https://crbug.com/76666)).
@@ -119,6 +135,10 @@
 passed to a compromised renderer via IPC ([469151](https://crbug.com/469151)).
 * Memory corruption that requires a specific extension to be installed
 ([313743](https://crbug.com/313743)).
+* Memory corruption in the browser process, triggered by a browser shutdown that
+  is not reliably triggered and/or is difficult to trigger ([1230513](https://crbug.com/1230513)).
+* Memory corruption in the browser process, requiring a non-standard flag and
+  user interaction ([1255332](https://crbug.com/1255332)).
 * An HSTS bypass ([461481](https://crbug.com/461481)).
 * A bypass of the same origin policy for pages that meet several preconditions
 ([419383](https://crbug.com/419383)).
diff --git a/extensions/renderer/bindings/api_binding_hooks.cc b/extensions/renderer/bindings/api_binding_hooks.cc
index a29335a7..263605a 100644
--- a/extensions/renderer/bindings/api_binding_hooks.cc
+++ b/extensions/renderer/bindings/api_binding_hooks.cc
@@ -224,7 +224,17 @@
                   "active request";
 
   auto iter = data->active_requests.find(request_id);
-  DCHECK(iter != data->active_requests.end());
+  if (iter == data->active_requests.end()) {
+    // In theory there should always be an associated stored request found, but
+    // if one of our custom bindings erroneously calls the callbacks for
+    // completing a request more than once the associated request will have
+    // already been removed. If that is the case we bail early.
+    // TODO(tjudkins): Audit existing handle request custom hooks to see if this
+    // could happen in any of them. crbug.com/1298409 seemed to indicate this
+    // was happening, hence why we fail gracefully here to avoid a crash.
+    NOTREACHED() << "No callback found for the specified request ID.";
+    return;
+  }
   auto callback = std::move(iter->second);
   data->active_requests.erase(iter);
   std::move(callback).Run(did_succeed, &args);
diff --git a/extensions/renderer/bindings/api_binding_unittest.cc b/extensions/renderer/bindings/api_binding_unittest.cc
index a4c98a9..e3c429a0 100644
--- a/extensions/renderer/bindings/api_binding_unittest.cc
+++ b/extensions/renderer/bindings/api_binding_unittest.cc
@@ -399,6 +399,8 @@
       promise_availability_callback_;
 };
 
+using APIBindingDeathTest = APIBindingUnittest;
+
 v8::Local<v8::Value> APIBindingUnittest::RunTest(
     v8::Local<v8::Context> context,
     v8::Local<v8::Object> object,
@@ -1343,6 +1345,60 @@
   }
 }
 
+// Tests that a JS handle request hook that calls the resolver callback more
+// than once will cause a crash on a DCHECK build, but fail gracefully on a
+// release build. Regression test for https://crbug.com/1298409.
+TEST_F(APIBindingDeathTest, TestHandleRequestFailureCallback) {
+  bool context_allows_promises = true;
+  SetPromiseAvailabilityFlag(&context_allows_promises);
+
+  // Register a hook for supportsPromises that calls the success callback twice.
+  static const char* const kRegisterHook = R"(
+      (function(hooks) {
+        function handler(firstArg, callback, failureCallback) {
+          callback(firstArg);
+          // Calling the callback to resolve the request a second time is
+          // something our custom hooks shouldn't be doing, but this test
+          // intentionally does it to verify behavior if it does happen by
+          // accident.
+          callback(firstArg);
+        };
+        hooks.setHandleRequest('supportsPromises', handler);
+      }))";
+
+  InitializeJSHooks(kRegisterHook);
+  SetFunctions(kFunctionsWithPromiseSignatures);
+  InitializeBinding();
+
+  v8::HandleScope handle_scope(isolate());
+  v8::Local<v8::Context> context = MainContext();
+  v8::Local<v8::Object> binding_object = binding()->CreateInstance(context);
+
+  v8::Local<v8::Function> function = FunctionFromString(
+      context, "(function(obj) { return obj.supportsPromises(42); })");
+  v8::Local<v8::Value> args[] = {binding_object};
+
+  // Calling supportsPromises will trigger the HandleRequest hook which attempts
+  // to resolve the request twice by calling the success callback twice. This
+  // should cause a crash if DCHECKs are on, but otherwise should gracefully
+  // fail without a crash and still result in the request resolving as expected.
+#if DCHECK_IS_ON()
+  EXPECT_DEATH(
+      {
+        RunFunction(function, context, v8::Undefined(isolate()),
+                    base::size(args), args);
+      },
+      "Check failed: false. No callback found for the specified request ID.");
+#else
+  v8::Local<v8::Value> result = RunFunction(
+      function, context, v8::Undefined(isolate()), base::size(args), args);
+  v8::Local<v8::Promise> promise;
+  ASSERT_TRUE(GetValueAs(result, &promise));
+  EXPECT_EQ(v8::Promise::kFulfilled, promise->State());
+  EXPECT_EQ(R"(42)", V8ToString(promise->Result(), context));
+#endif
+}
+
 // Tests that JS custom hooks correctly handle the context being invalidated.
 // Regression test for https://crbug.com/944014.
 TEST_F(APIBindingUnittest, TestInvalidatingInCustomHook) {
diff --git a/fuchsia/engine/browser/accessibility_bridge.cc b/fuchsia/engine/browser/accessibility_bridge.cc
index 8c713ab..51d1cf29 100644
--- a/fuchsia/engine/browser/accessibility_bridge.cc
+++ b/fuchsia/engine/browser/accessibility_bridge.cc
@@ -313,7 +313,7 @@
     // Set up inspect node for semantic trees.
     inspect_node_tree_dump_ = inspect_node_.CreateLazyNode(
         kSemanticTreesInspectNodeName,
-        [this]() { return fit::make_ok_promise(FillInspectData()); });
+        [this]() { return fpromise::make_ok_promise(FillInspectData()); });
   } else {
     // The SemanticsManager will clear all state in this case, which is
     // mirrored here.
diff --git a/gin/v8_initializer.cc b/gin/v8_initializer.cc
index 0d00a94..8aae8ca 100644
--- a/gin/v8_initializer.cc
+++ b/gin/v8_initializer.cc
@@ -12,6 +12,7 @@
 
 #include "base/allocator/partition_allocator/page_allocator.h"
 #include "base/allocator/partition_allocator/partition_address_space.h"
+#include "base/bits.h"
 #include "base/check.h"
 #include "base/debug/alias.h"
 #include "base/debug/crash_logging.h"
diff --git a/gin/v8_platform.cc b/gin/v8_platform.cc
index a7dad3d..78b8a28 100644
--- a/gin/v8_platform.cc
+++ b/gin/v8_platform.cc
@@ -8,7 +8,6 @@
 
 #include "base/bind.h"
 #include "base/bit_cast.h"
-#include "base/bits.h"
 #include "base/check_op.h"
 #include "base/debug/stack_trace.h"
 #include "base/location.h"
diff --git a/gpu/command_buffer/client/gles2_implementation.cc b/gpu/command_buffer/client/gles2_implementation.cc
index 70812285..12b24451 100644
--- a/gpu/command_buffer/client/gles2_implementation.cc
+++ b/gpu/command_buffer/client/gles2_implementation.cc
@@ -23,7 +23,6 @@
 
 #include "base/atomic_sequence_num.h"
 #include "base/bind.h"
-#include "base/bits.h"
 #include "base/compiler_specific.h"
 #include "base/containers/span.h"
 #include "base/cxx17_backports.h"
diff --git a/gpu/command_buffer/service/raster_decoder.cc b/gpu/command_buffer/service/raster_decoder.cc
index 96e37774..f0e4439c 100644
--- a/gpu/command_buffer/service/raster_decoder.cc
+++ b/gpu/command_buffer/service/raster_decoder.cc
@@ -14,7 +14,6 @@
 
 #include "base/atomic_sequence_num.h"
 #include "base/bind.h"
-#include "base/bits.h"
 #include "base/containers/flat_map.h"
 #include "base/cxx17_backports.h"
 #include "base/debug/crash_logging.h"
diff --git a/gpu/config/process_json.py b/gpu/config/process_json.py
index 4ed3117..9c3e2bea 100755
--- a/gpu/config/process_json.py
+++ b/gpu/config/process_json.py
@@ -188,7 +188,7 @@
       data_helper_file.write(',\n')
     data_helper_file.write('};\n\n')
     # use the list
-    data_file.write('base::size(%s),  // %s size\n' % (var_name, entry_kind))
+    data_file.write('std::size(%s),  // %s size\n' % (var_name, entry_kind))
     data_file.write('%s,  // %s\n' % (var_name, entry_kind))
   else:
     data_file.write('0,  // %s size\n' % entry_kind)
@@ -294,7 +294,7 @@
       data_helper_file.write(',\n')
     data_helper_file.write('};\n\n')
     # reference the list
-    data_file.write('base::size(%s),  // %s size\n' % (var_name, name_tag))
+    data_file.write('std::size(%s),  // %s size\n' % (var_name, name_tag))
     data_file.write('%s,  // %s\n' % (var_name, name_tag))
   else:
     data_file.write('0,  // %s size\n' % name_tag)
@@ -350,7 +350,7 @@
                              (device_id[ii], device_revision[ii]))
     data_helper_file.write('};\n\n')
     # reference the list
-    data_file.write('base::size(%s),  // Devices size\n' % var_name)
+    data_file.write('std::size(%s),  // Devices size\n' % var_name)
     data_file.write('%s,  // Devices\n' % var_name)
   else:
     assert not device_revision
@@ -382,7 +382,7 @@
     data_helper_file.write(
       'const GpuControlList::MachineModelInfo %s = {\n' % var_name)
     if machine_model_name:
-      data_helper_file.write('base::size(%s),  // machine model name size\n' %
+      data_helper_file.write('std::size(%s),  // machine model name size\n' %
                              model_name_var_name)
       data_helper_file.write('%s,  // machine model names\n' %
                              model_name_var_name)
@@ -691,7 +691,7 @@
                              intel_gpu_series_map[series])
     data_helper_file.write('};\n\n')
 
-    data_file.write('base::size(%s),  // intel_gpu_series size\n' % var_name)
+    data_file.write('std::size(%s),  // intel_gpu_series size\n' % var_name)
     data_file.write('%s,  // intel_gpu_series\n' % var_name)
   else:
     data_file.write('0,  // intel_gpu_series size\n')
@@ -753,7 +753,7 @@
     var_name = 'kFeatureListFor%sEntry%d' % (unique_symbol_id, entry_id)
     features = entry['features']
     feature_set = get_feature_set(features, total_feature_set)
-    data_file.write('base::size(%s),  // features size\n' % var_name)
+    data_file.write('std::size(%s),  // features size\n' % var_name)
     data_file.write('%s,  // features\n' % var_name)
     write_features(feature_set, feature_name_prefix, var_name, data_helper_file)
   else:
@@ -796,7 +796,7 @@
                        data_exception_file, data_helper_file, None)
       data_exception_file.write('},\n')
     data_exception_file.write('};\n\n')
-    data_file.write('base::size(%s),  // exceptions count\n' % exception_var)
+    data_file.write('std::size(%s),  // exceptions count\n' % exception_var)
     data_file.write('%s,  // exceptions\n' % exception_var)
   else:
     data_file.write('0,  // exceptions count\n')
@@ -837,6 +837,7 @@
   data_file.write(_LICENSE)
   data_file.write(_DO_NOT_EDIT_WARNING)
   data_file.write('#include "%s/%s"\n\n' % (path, output_header_filename))
+  data_file.write('#include <iterator>\n\n')
   data_file.write('#include "%s/%s"\n' % (path, output_helper_filename))
   data_file.write('#include "%s/%s"\n\n' % (path, output_exception_filename))
   data_helper_file = open(output_helper_filepath, 'w')
diff --git a/infra/config/generated/builders/try/android-12-x64-dbg/properties.json b/infra/config/generated/builders/try/android-12-x64-dbg/properties.json
new file mode 100644
index 0000000..d90599c
--- /dev/null
+++ b/infra/config/generated/builders/try/android-12-x64-dbg/properties.json
@@ -0,0 +1,17 @@
+{
+  "$build/goma": {
+    "enable_ats": true,
+    "rpc_extra_params": "?prod",
+    "server_host": "goma.chromium.org",
+    "use_luci_auth": true
+  },
+  "$recipe_engine/resultdb/test_presentation": {
+    "column_keys": [],
+    "grouping_keys": [
+      "status",
+      "v.test_suite"
+    ]
+  },
+  "builder_group": "tryserver.chromium.android",
+  "recipe": "chromium_trybot"
+}
\ No newline at end of file
diff --git a/infra/config/generated/builders/try/android-webview-12-x64-dbg/properties.json b/infra/config/generated/builders/try/android-webview-12-x64-dbg/properties.json
new file mode 100644
index 0000000..d90599c
--- /dev/null
+++ b/infra/config/generated/builders/try/android-webview-12-x64-dbg/properties.json
@@ -0,0 +1,17 @@
+{
+  "$build/goma": {
+    "enable_ats": true,
+    "rpc_extra_params": "?prod",
+    "server_host": "goma.chromium.org",
+    "use_luci_auth": true
+  },
+  "$recipe_engine/resultdb/test_presentation": {
+    "column_keys": [],
+    "grouping_keys": [
+      "status",
+      "v.test_suite"
+    ]
+  },
+  "builder_group": "tryserver.chromium.android",
+  "recipe": "chromium_trybot"
+}
\ No newline at end of file
diff --git a/infra/config/generated/luci/commit-queue.cfg b/infra/config/generated/luci/commit-queue.cfg
index 6c52788..9210b3c 100644
--- a/infra/config/generated/luci/commit-queue.cfg
+++ b/infra/config/generated/luci/commit-queue.cfg
@@ -281,6 +281,10 @@
         includable_only: true
       }
       builders {
+        name: "chromium/try/android-12-x64-dbg"
+        includable_only: true
+      }
+      builders {
         name: "chromium/try/android-12-x64-fyi-rel"
         includable_only: true
       }
@@ -493,6 +497,10 @@
         includable_only: true
       }
       builders {
+        name: "chromium/try/android-webview-12-x64-dbg"
+        includable_only: true
+      }
+      builders {
         name: "chromium/try/android-webview-marshmallow-arm64-dbg"
         includable_only: true
       }
diff --git a/infra/config/generated/luci/cr-buildbucket.cfg b/infra/config/generated/luci/cr-buildbucket.cfg
index 33af9d5..ea47208 100644
--- a/infra/config/generated/luci/cr-buildbucket.cfg
+++ b/infra/config/generated/luci/cr-buildbucket.cfg
@@ -46413,6 +46413,96 @@
       description_html: "This is the compilator half of an orchestrator + compilator pair of builders. The orchestrator is <a href=\"https://ci.chromium.org/p/chromium/builders/try/android-11-x86-rel\">android-11-x86-rel</a>."
     }
     builders {
+      name: "android-12-x64-dbg"
+      swarming_host: "chromium-swarm.appspot.com"
+      dimensions: "builderless:1"
+      dimensions: "cores:8"
+      dimensions: "cpu:x86-64"
+      dimensions: "os:Ubuntu-18.04"
+      dimensions: "pool:luci.chromium.try"
+      dimensions: "ssd:0"
+      exe {
+        cipd_package: "infra/chromium/bootstrapper/${platform}"
+        cipd_version: "latest"
+        cmd: "bootstrapper"
+      }
+      properties:
+        '{'
+        '  "$bootstrap/exe": {'
+        '    "exe": {'
+        '      "cipd_package": "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build",'
+        '      "cipd_version": "refs/heads/main",'
+        '      "cmd": ['
+        '        "luciexe"'
+        '      ]'
+        '    }'
+        '  },'
+        '  "$bootstrap/properties": {'
+        '    "properties_file": "infra/config/generated/builders/try/android-12-x64-dbg/properties.json",'
+        '    "top_level_project": {'
+        '      "ref": "refs/heads/main",'
+        '      "repo": {'
+        '        "host": "chromium.googlesource.com",'
+        '        "project": "chromium/src"'
+        '      }'
+        '    }'
+        '  },'
+        '  "builder_group": "tryserver.chromium.android",'
+        '  "led_builder_is_bootstrapped": true,'
+        '  "recipe": "chromium_trybot"'
+        '}'
+      execution_timeout_secs: 14400
+      expiration_secs: 7200
+      grace_period {
+        seconds: 120
+      }
+      caches {
+        name: "win_toolchain"
+        path: "win_toolchain"
+      }
+      build_numbers: YES
+      service_account: "chromium-try-builder@chops-service-accounts.iam.gserviceaccount.com"
+      task_template_canary_percentage {
+        value: 5
+      }
+      experiments {
+        key: "luci.recipes.use_python3"
+        value: 100
+      }
+      resultdb {
+        enable: true
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "try_test_results"
+          test_results {}
+        }
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "gpu_try_test_results"
+          test_results {
+            predicate {
+              test_id_regexp: "ninja://chrome/test:telemetry_gpu_integration_test[^/]*/.+"
+            }
+          }
+        }
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "blink_web_tests_try_test_results"
+          test_results {
+            predicate {
+              test_id_regexp: "ninja://[^/]*blink_web_tests/.+"
+            }
+          }
+        }
+        history_options {
+          use_invocation_timestamp: true
+        }
+      }
+    }
+    builders {
       name: "android-12-x64-fyi-rel"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
@@ -50529,6 +50619,96 @@
       }
     }
     builders {
+      name: "android-webview-12-x64-dbg"
+      swarming_host: "chromium-swarm.appspot.com"
+      dimensions: "builderless:1"
+      dimensions: "cores:8"
+      dimensions: "cpu:x86-64"
+      dimensions: "os:Ubuntu-18.04"
+      dimensions: "pool:luci.chromium.try"
+      dimensions: "ssd:0"
+      exe {
+        cipd_package: "infra/chromium/bootstrapper/${platform}"
+        cipd_version: "latest"
+        cmd: "bootstrapper"
+      }
+      properties:
+        '{'
+        '  "$bootstrap/exe": {'
+        '    "exe": {'
+        '      "cipd_package": "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build",'
+        '      "cipd_version": "refs/heads/main",'
+        '      "cmd": ['
+        '        "luciexe"'
+        '      ]'
+        '    }'
+        '  },'
+        '  "$bootstrap/properties": {'
+        '    "properties_file": "infra/config/generated/builders/try/android-webview-12-x64-dbg/properties.json",'
+        '    "top_level_project": {'
+        '      "ref": "refs/heads/main",'
+        '      "repo": {'
+        '        "host": "chromium.googlesource.com",'
+        '        "project": "chromium/src"'
+        '      }'
+        '    }'
+        '  },'
+        '  "builder_group": "tryserver.chromium.android",'
+        '  "led_builder_is_bootstrapped": true,'
+        '  "recipe": "chromium_trybot"'
+        '}'
+      execution_timeout_secs: 14400
+      expiration_secs: 7200
+      grace_period {
+        seconds: 120
+      }
+      caches {
+        name: "win_toolchain"
+        path: "win_toolchain"
+      }
+      build_numbers: YES
+      service_account: "chromium-try-builder@chops-service-accounts.iam.gserviceaccount.com"
+      task_template_canary_percentage {
+        value: 5
+      }
+      experiments {
+        key: "luci.recipes.use_python3"
+        value: 100
+      }
+      resultdb {
+        enable: true
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "try_test_results"
+          test_results {}
+        }
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "gpu_try_test_results"
+          test_results {
+            predicate {
+              test_id_regexp: "ninja://chrome/test:telemetry_gpu_integration_test[^/]*/.+"
+            }
+          }
+        }
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "blink_web_tests_try_test_results"
+          test_results {
+            predicate {
+              test_id_regexp: "ninja://[^/]*blink_web_tests/.+"
+            }
+          }
+        }
+        history_options {
+          use_invocation_timestamp: true
+        }
+      }
+    }
+    builders {
       name: "android-webview-marshmallow-arm64-dbg"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
diff --git a/infra/config/generated/luci/luci-milo.cfg b/infra/config/generated/luci/luci-milo.cfg
index f7dccec..457e5d9 100644
--- a/infra/config/generated/luci/luci-milo.cfg
+++ b/infra/config/generated/luci/luci-milo.cfg
@@ -14214,6 +14214,9 @@
     name: "buildbucket/luci.chromium.try/android-11-x86-rel-compilator"
   }
   builders {
+    name: "buildbucket/luci.chromium.try/android-12-x64-dbg"
+  }
+  builders {
     name: "buildbucket/luci.chromium.try/android-12-x64-fyi-rel"
   }
   builders {
@@ -14352,6 +14355,9 @@
     name: "buildbucket/luci.chromium.try/android-weblayer-pie-x86-wpt-smoketest"
   }
   builders {
+    name: "buildbucket/luci.chromium.try/android-webview-12-x64-dbg"
+  }
+  builders {
     name: "buildbucket/luci.chromium.try/android-webview-marshmallow-arm64-dbg"
   }
   builders {
@@ -15328,6 +15334,9 @@
     name: "buildbucket/luci.chromium.try/android-11-x86-rel-compilator"
   }
   builders {
+    name: "buildbucket/luci.chromium.try/android-12-x64-dbg"
+  }
+  builders {
     name: "buildbucket/luci.chromium.try/android-12-x64-fyi-rel"
   }
   builders {
@@ -15454,6 +15463,9 @@
     name: "buildbucket/luci.chromium.try/android-weblayer-pie-x86-wpt-smoketest"
   }
   builders {
+    name: "buildbucket/luci.chromium.try/android-webview-12-x64-dbg"
+  }
+  builders {
     name: "buildbucket/luci.chromium.try/android-webview-marshmallow-arm64-dbg"
   }
   builders {
diff --git a/infra/config/lib/builder_config.star b/infra/config/lib/builder_config.star
index 7b67bc1..3235fe4 100644
--- a/infra/config/lib/builder_config.star
+++ b/infra/config/lib/builder_config.star
@@ -6,7 +6,6 @@
 
 load("./args.star", "args")
 load("./nodes.star", "nodes")
-load("//project.star", "settings")
 
 # TODO(gbeaty) Add support for PROVIDE_TEST_SPEC mirrors
 
@@ -401,17 +400,6 @@
 def _struct_to_dict(obj):
     return json.decode(json.encode(obj))
 
-_ALLOW_LIST = (
-    ("ci", "chromeos-amd64-generic-rel"),
-    ("ci", "chromeos-arm-generic-rel"),
-    ("ci", "linux-bootstrap"),
-    ("ci", "linux-bootstrap-tests"),
-    ("ci", "Win x64 Builder (reclient compare)"),
-    ("try", "chromeos-amd64-generic-rel"),
-    ("try", "chromeos-arm-generic-rel"),
-    ("try", "linux-bootstrap"),
-)
-
 def register_builder_config(bucket, name, builder_group, builder_spec, mirrors, try_settings):
     """Registers the builder config so the properties can be computed.
 
@@ -434,11 +422,6 @@
         # family of recipes
         return
 
-    # TODO(gbeaty) Allow any builders to use builder config once no other
-    # systems rely on the recipe-side config
-    if (bucket, name) not in _ALLOW_LIST:
-        fail("src-side builder config is not available for general use yet")
-
     if not builder_group:
         fail("builder_group must be set to use chromium_tests_builder_config")
     if builder_spec and mirrors:
@@ -528,7 +511,10 @@
 
 def _builder_id(node):
     return dict(
-        project = settings.project,
+        # TODO(crbug.com/868153) Once the configs for all chromium builders are
+        # migrated src-side, switch this to settings.project and remove the use
+        # of project_trigger_override within the starlark
+        project = "chromium",
         bucket = node.props.bucket,
         builder = node.props.name,
     )
diff --git a/infra/config/subprojects/chromium/try/tryserver.chromium.android.star b/infra/config/subprojects/chromium/try/tryserver.chromium.android.star
index ffae7b14..8f7e154 100644
--- a/infra/config/subprojects/chromium/try/tryserver.chromium.android.star
+++ b/infra/config/subprojects/chromium/try/tryserver.chromium.android.star
@@ -52,6 +52,10 @@
 )
 
 try_.builder(
+    name = "android-12-x64-dbg",
+)
+
+try_.builder(
     name = "android-12-x64-fyi-rel",
 )
 
@@ -330,6 +334,10 @@
 )
 
 try_.builder(
+    name = "android-webview-12-x64-dbg",
+)
+
+try_.builder(
     name = "android-webview-pie-x86-wpt-fyi-rel",
 )
 
diff --git a/ios/chrome/browser/complex_tasks/BUILD.gn b/ios/chrome/browser/complex_tasks/BUILD.gn
index 46193cc..26f8bcf4 100644
--- a/ios/chrome/browser/complex_tasks/BUILD.gn
+++ b/ios/chrome/browser/complex_tasks/BUILD.gn
@@ -19,6 +19,7 @@
   sources = [ "ios_task_tab_helper_unittest.mm" ]
   deps = [
     ":complex_tasks",
+    "//ios/chrome/browser/browser_state:test_support",
     "//ios/chrome/browser/web:test_support",
     "//ios/web/public:public",
     "//ios/web/public/test:test",
diff --git a/ios/chrome/browser/complex_tasks/ios_task_tab_helper_unittest.mm b/ios/chrome/browser/complex_tasks/ios_task_tab_helper_unittest.mm
index 9617b75..9023e10 100644
--- a/ios/chrome/browser/complex_tasks/ios_task_tab_helper_unittest.mm
+++ b/ios/chrome/browser/complex_tasks/ios_task_tab_helper_unittest.mm
@@ -5,17 +5,18 @@
 #import "ios/chrome/browser/complex_tasks/ios_task_tab_helper.h"
 
 #include "base/time/time.h"
-#import "ios/chrome/browser/web/chrome_web_test.h"
 #import "ios/web/public/test/fakes/fake_navigation_context.h"
 #import "ios/web/public/test/fakes/fake_navigation_manager.h"
 #import "ios/web/public/test/fakes/fake_web_state.h"
+#import "ios/web/public/test/web_task_environment.h"
+#import "testing/platform_test.h"
 #include "url/gurl.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
 #endif
 
-class IOSTaskTabHelperTest : public ChromeWebTest {
+class IOSTaskTabHelperTest : public PlatformTest {
  protected:
   web::NavigationItem* AddItemToFakeNavigationManager(
       web::FakeNavigationManager* test_navigation_manager,
@@ -41,6 +42,7 @@
     return item;
   }
 
+  web::WebTaskEnvironment task_environment_;
   web::FakeWebState web_state_;
 };
 
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_view_controller.mm b/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_view_controller.mm
index c1be429..9660038 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_view_controller.mm
+++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_view_controller.mm
@@ -304,6 +304,8 @@
   }
 
   _mode = mode;
+  // TODO(crbug.com/1300369): Enable dragging items from search results.
+  self.collectionView.dragInteractionEnabled = (_mode != TabGridModeSearch);
 
   if (IsTabsSearchRegularResultsSuggestedActionsEnabled()) {
     if (mode == TabGridModeSearch && self.suggestedActionsDelegate) {
@@ -689,6 +691,10 @@
 - (NSArray<UIDragItem*>*)collectionView:(UICollectionView*)collectionView
            itemsForBeginningDragSession:(id<UIDragSession>)session
                             atIndexPath:(NSIndexPath*)indexPath {
+  if (_mode == TabGridModeSearch) {
+    // TODO(crbug.com/1300369): Enable dragging items from search results.
+    return @[];
+  }
   if ([self isIndexPathForPlusSignCell:indexPath]) {
     // Return an empty array because the plus sign cell should not be dragged.
     return @[];
@@ -748,7 +754,8 @@
 
 - (BOOL)collectionView:(UICollectionView*)collectionView
     canHandleDropSession:(id<UIDropSession>)session {
-  return YES;
+  // Prevent dropping tabs into grid while displaying search results.
+  return (_mode != TabGridModeSearch);
 }
 
 - (UICollectionViewDropProposal*)
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_view_controller.mm b/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_view_controller.mm
index 0a3780a..1bbf190 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_view_controller.mm
+++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_view_controller.mm
@@ -1859,13 +1859,29 @@
   [self updateScrimVisibilityForText:searchText];
   switch (self.currentPage) {
     case TabGridPageIncognitoTabs:
-      [self.incognitoTabsDelegate searchItemsWithText:searchText];
+      if (searchText.length) {
+        [self.incognitoTabsDelegate searchItemsWithText:searchText];
+      } else {
+        // The expectation from searchItemsWithText is to search tabs from all
+        // the available windows to the app. However in the case of empy string
+        // the grid should revert back to its original state so it doesn't
+        // display all the tabs from all the available windows.
+        [self.incognitoTabsDelegate resetToAllItems];
+      }
       self.incognitoTabsViewController.searchText = searchText;
       break;
     case TabGridPageRegularTabs:
     case TabGridPageRemoteTabs:
+      if (searchText.length) {
+        [self.regularTabsDelegate searchItemsWithText:searchText];
+      } else {
+        // The expectation from searchItemsWithText is to search tabs from all
+        // the available windows to the app. However in the case of empy string
+        // the grid should revert back to its original state so it doesn't
+        // display all the tabs from all the available windows.
+        [self.regularTabsDelegate resetToAllItems];
+      }
       self.regularTabsViewController.searchText = searchText;
-      [self.regularTabsDelegate searchItemsWithText:searchText];
       self.remoteTabsViewController.searchTerms = searchText;
       break;
   }
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 ca4c112..6daf8ea 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 @@
-43f4bdd8b03a2e38cfa87684a0cc59859b767f9e
\ No newline at end of file
+5f6d9d6c69e4d81f0d6b9917a7da16bdbf53b534
\ 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 a1ebccc..19440e9 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 @@
-10e3500efda90c4c2f3c7d07a6fa865802e35616
\ No newline at end of file
+22dd58fb4ee41fece1bb399c83284cf29ecb1213
\ 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 0a5af42..4d88b70d 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 @@
-8e6a301a051d58cab81a8e4dbc6501120c711b33
\ No newline at end of file
+e35144ca5a4b65c63fe2ea9d0f69446a3955b0f8
\ 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 f1e4d052..759f323d 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 @@
-6365cacb706b60b2cd1cf115be6b830a524fb6fa
\ No newline at end of file
+b82756c93b03c964190ece139c5eb8339e5775dc
\ 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 aa1a043e..5b6043c2 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 @@
-018c1184c5f15b1462ad01dc5ffce25e67c33a57
\ No newline at end of file
+bd41fa8eb600c32e60ce7d897f97b805df3d9fdb
\ 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 d1603a4c..b8ff0cd8 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 @@
-d6095c50efc7f62d9b573776cf580d657238c87c
\ No newline at end of file
+05f54f34fd89b73d78bc2a6c05fc60e01a141895
\ 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 71764aa..8f4122c 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 @@
-c66512d119ea0422f3d93a4d0a38e48d4aee3f61
\ No newline at end of file
+7a97318c76783dfb27ad35e8230c7a7b657f7bdf
\ 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 85b3f5d..6f04140f 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 @@
-a886a2c7b1960aaea25adbd8e30915588972859a
\ No newline at end of file
+5fecc8a9cdfb895cabcbe87eda13205869048754
\ 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 83acd0be..a89908e 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 @@
-4e3dfdc6a11ae562eb15d72dde0018beb840050d
\ No newline at end of file
+ab15c4758b1fa20d5ed75f08d3bb3409fbc1a68b
\ 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 df1f7e2..80cad9a 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 @@
-c12d68ed85a0076df35aecd9665991dd35945301
\ No newline at end of file
+9900918877b9630de1395c35f72e77540679aabb
\ No newline at end of file
diff --git a/media/filters/fuchsia/fuchsia_video_decoder.cc b/media/filters/fuchsia/fuchsia_video_decoder.cc
index 7661dcc..eab1ebd 100644
--- a/media/filters/fuchsia/fuchsia_video_decoder.cc
+++ b/media/filters/fuchsia/fuchsia_video_decoder.cc
@@ -9,7 +9,6 @@
 #include <vulkan/vulkan.h>
 
 #include "base/bind.h"
-#include "base/bits.h"
 #include "base/callback_helpers.h"
 #include "base/command_line.h"
 #include "base/fuchsia/fuchsia_logging.h"
diff --git a/media/fuchsia/common/vmo_buffer_writer_queue.cc b/media/fuchsia/common/vmo_buffer_writer_queue.cc
index 2367a8d..b983e00 100644
--- a/media/fuchsia/common/vmo_buffer_writer_queue.cc
+++ b/media/fuchsia/common/vmo_buffer_writer_queue.cc
@@ -7,7 +7,6 @@
 #include <zircon/rights.h>
 #include <algorithm>
 
-#include "base/bits.h"
 #include "base/fuchsia/fuchsia_logging.h"
 #include "base/process/process_metrics.h"
 #include "media/base/decoder_buffer.h"
diff --git a/media/gpu/vaapi/vaapi_wrapper.cc b/media/gpu/vaapi/vaapi_wrapper.cc
index f2a8f2cb..0f287fab 100644
--- a/media/gpu/vaapi/vaapi_wrapper.cc
+++ b/media/gpu/vaapi/vaapi_wrapper.cc
@@ -21,7 +21,6 @@
 #include <utility>
 
 #include "base/bind.h"
-#include "base/bits.h"
 #include "base/callback_helpers.h"
 #include "base/containers/contains.h"
 #include "base/containers/cxx20_erase.h"
diff --git a/media/gpu/video_decode_accelerator_perf_tests.cc b/media/gpu/video_decode_accelerator_perf_tests.cc
index e312f91..1e7f389 100644
--- a/media/gpu/video_decode_accelerator_perf_tests.cc
+++ b/media/gpu/video_decode_accelerator_perf_tests.cc
@@ -402,27 +402,16 @@
 // created decoder. We should instead keep track of multiple evaluators, and
 // then decide how to aggregate/report those metrics.
 // Play multiple videos simultaneously from start to finish.
-TEST_F(VideoDecoderTest,
-       MeasureUncappedPerformance_MultipleConcurrentDecoders) {
+TEST_F(VideoDecoderTest, MeasureUncappedPerformance_TenConcurrentDecoders) {
   // Set RLIMIT_NOFILE soft limit to its hard limit value.
   if (sandbox::ResourceLimits::AdjustCurrent(
           RLIMIT_NOFILE, std::numeric_limits<long long int>::max())) {
     DPLOG(ERROR) << "Unable to increase soft limit of RLIMIT_NOFILE";
   }
 
-// The minimal number of concurrent decoders we expect to be supported on
-// platforms.
-#if defined(USE_VAAPI)
-  constexpr size_t kMinSupportedConcurrentDecoders =
-      VaapiVideoDecoder::kMaxNumOfInstances;
-#elif defined(USE_V4L2_CODEC)
-  constexpr size_t kMinSupportedConcurrentDecoders = 10;
-#else
-  constexpr size_t kMinSupportedConcurrentDecoders = 10;
-#endif
+  constexpr size_t kNumConcurrentDecoders = 10;
 
-  std::vector<std::unique_ptr<VideoPlayer>> players(
-      kMinSupportedConcurrentDecoders);
+  std::vector<std::unique_ptr<VideoPlayer>> players(kNumConcurrentDecoders);
   for (auto&& player : players) {
     player = CreateVideoPlayer(g_env->Video());
     // Increase the timeout for older machines that cannot decode as
diff --git a/media/video/h265_nalu_parser.cc b/media/video/h265_nalu_parser.cc
index 559c833d..5734b33d 100644
--- a/media/video/h265_nalu_parser.cc
+++ b/media/video/h265_nalu_parser.cc
@@ -10,7 +10,6 @@
 #include <cmath>
 #include <cstring>
 
-#include "base/bits.h"
 #include "base/cxx17_backports.h"
 #include "base/logging.h"
 #include "media/base/decrypt_config.h"
diff --git a/net/http/http_security_headers_unittest.cc b/net/http/http_security_headers_unittest.cc
index 63223ba9e..e31a116 100644
--- a/net/http/http_security_headers_unittest.cc
+++ b/net/http/http_security_headers_unittest.cc
@@ -4,6 +4,8 @@
 
 #include <stdint.h>
 
+#include <iterator>
+
 #include "base/base64.h"
 #include "base/stl_util.h"
 #include "base/strings/string_piece.h"
diff --git a/net/http/transport_security_state_static.template b/net/http/transport_security_state_static.template
index 21542f9b7..ba40b5b 100644
--- a/net/http/transport_security_state_static.template
+++ b/net/http/transport_security_state_static.template
@@ -9,7 +9,8 @@
 
 #include <stdint.h>
 
-#include "base/cxx17_backports.h"
+#include <iterator>
+
 #include "net/http/transport_security_state_source.h"
 
 // These are SubjectPublicKeyInfo hashes for public key pinning. The
@@ -47,7 +48,7 @@
   kHSTSRootPosition,
   kExpectCTReportURIs,
   kPinsets,
-  base::size(kPinsets)
+  std::size(kPinsets)
 };
 
 #endif  // NET_HTTP_TRANSPORT_SECURITY_STATE_STATIC_H_
diff --git a/net/http/transport_security_state_static_unittest.template b/net/http/transport_security_state_static_unittest.template
index dfb4a33..8d08caa 100644
--- a/net/http/transport_security_state_static_unittest.template
+++ b/net/http/transport_security_state_static_unittest.template
@@ -6,7 +6,7 @@
 // See transport_security_state_static.template for more information on the data
 // in this file.
 
-// Note that consumers must include <stdint.h>, "base/cxx17_backports.h", and
+// Note that consumers must include <stdint.h>, "<iterator>", and
 // "net/http/transport_security_state_source.h", which this file cannot do
 // itself, since it's always included in a nested namespace.
 
@@ -15,7 +15,7 @@
 static const char* const kExpectCTReportURIs[] = [[EXPECT_CT_REPORT_URIS]];
 
 static const char* const kNoRejectedPublicKeys[] = {
-    NULL,
+    nullptr,
 };
 
 [[ACCEPTABLE_CERTS]]
@@ -37,5 +37,5 @@
   kHSTSRootPosition,
   kExpectCTReportURIs,
   kPinsets,
-  base::size(kPinsets)
+  std::size(kPinsets)
 };
diff --git a/net/http/transport_security_state_test_util.cc b/net/http/transport_security_state_test_util.cc
index d0d05bb..248a0047 100644
--- a/net/http/transport_security_state_test_util.cc
+++ b/net/http/transport_security_state_test_util.cc
@@ -4,6 +4,8 @@
 
 #include "net/http/transport_security_state_test_util.h"
 
+#include <iterator>
+
 #include "base/stl_util.h"
 #include "base/strings/string_number_conversions.h"
 #include "net/http/transport_security_state.h"
diff --git a/net/http/transport_security_state_unittest.cc b/net/http/transport_security_state_unittest.cc
index 228405ee..93bf809a 100644
--- a/net/http/transport_security_state_unittest.cc
+++ b/net/http/transport_security_state_unittest.cc
@@ -7,13 +7,13 @@
 #include <stdint.h>
 
 #include <algorithm>
+#include <iterator>
 #include <memory>
 #include <string>
 #include <vector>
 
 #include "base/base64.h"
 #include "base/callback_helpers.h"
-#include "base/cxx17_backports.h"
 #include "base/files/file_path.h"
 #include "base/json/json_reader.h"
 #include "base/memory/raw_ptr.h"
diff --git a/net/url_request/url_request_unittest.cc b/net/url_request/url_request_unittest.cc
index 8b730f24..3284777c 100644
--- a/net/url_request/url_request_unittest.cc
+++ b/net/url_request/url_request_unittest.cc
@@ -30,7 +30,6 @@
 #include "base/base64url.h"
 #include "base/bind.h"
 #include "base/compiler_specific.h"
-#include "base/cxx17_backports.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
@@ -3921,7 +3920,7 @@
       req->set_upload(CreateSimpleUploadData(kData));
       HttpRequestHeaders headers;
       headers.SetHeader(HttpRequestHeaders::kContentLength,
-                        base::NumberToString(base::size(kData) - 1));
+                        base::NumberToString(std::size(kData) - 1));
       headers.SetHeader(HttpRequestHeaders::kContentType, "text/plain");
       req->SetExtraRequestHeaders(headers);
     }
@@ -4137,7 +4136,7 @@
       BlockingNetworkDelegate::ON_BEFORE_URL_REQUEST,
       BlockingNetworkDelegate::ON_BEFORE_SEND_HEADERS,
       BlockingNetworkDelegate::ON_HEADERS_RECEIVED};
-  static const size_t blocking_stages_length = base::size(blocking_stages);
+  static const size_t blocking_stages_length = std::size(blocking_stages);
 
   ASSERT_TRUE(http_test_server()->Start());
 
@@ -4420,7 +4419,7 @@
     r->set_upload(CreateSimpleUploadData(kData));
     HttpRequestHeaders headers;
     headers.SetHeader(HttpRequestHeaders::kContentLength,
-                      base::NumberToString(base::size(kData) - 1));
+                      base::NumberToString(std::size(kData) - 1));
     r->SetExtraRequestHeaders(headers);
 
     // Quit after hitting the redirect, so can check the headers.
@@ -8771,7 +8770,7 @@
   req->set_upload(CreateSimpleUploadData(kData));
   HttpRequestHeaders headers;
   headers.SetHeader(HttpRequestHeaders::kContentLength,
-                    base::NumberToString(base::size(kData) - 1));
+                    base::NumberToString(std::size(kData) - 1));
   req->SetExtraRequestHeaders(headers);
 
   std::unique_ptr<URLRequestRedirectJob> job =
@@ -8798,7 +8797,7 @@
   req->set_upload(CreateSimpleUploadData(kData));
   HttpRequestHeaders headers;
   headers.SetHeader(HttpRequestHeaders::kContentLength,
-                    base::NumberToString(base::size(kData) - 1));
+                    base::NumberToString(std::size(kData) - 1));
   req->SetExtraRequestHeaders(headers);
 
   std::unique_ptr<URLRequestRedirectJob> job =
@@ -8996,7 +8995,7 @@
                {"/echoheader?Accept-Charset", "None"},
                {"/echoheader?User-Agent", ""}};
 
-  for (size_t i = 0; i < base::size(tests); i++) {
+  for (size_t i = 0; i < std::size(tests); i++) {
     TestDelegate d;
     std::unique_ptr<URLRequest> req(context->CreateRequest(
         http_test_server()->GetURL(tests[i].request), DEFAULT_PRIORITY, &d,
diff --git a/remoting/host/linux/linux_me2me_host.py b/remoting/host/linux/linux_me2me_host.py
index cfb5d3c..da1446b 100755
--- a/remoting/host/linux/linux_me2me_host.py
+++ b/remoting/host/linux/linux_me2me_host.py
@@ -125,9 +125,16 @@
 # Number of seconds to save session output to the log.
 SESSION_OUTPUT_TIME_LIMIT_SECONDS = 300
 
+# Number of seconds to save the display server output to the log.
+SERVER_OUTPUT_TIME_LIMIT_SECONDS = 300
+
 # Host offline reason if the X server retry count is exceeded.
 HOST_OFFLINE_REASON_X_SERVER_RETRIES_EXCEEDED = "X_SERVER_RETRIES_EXCEEDED"
 
+# Host offline reason if the wayland server retry count is exceeded.
+HOST_OFFLINE_REASON_WAYLAND_SERVER_RETRIES_EXCEEDED = (
+  "WAYLAND_SERVER_RETRIES_EXCEEDED")
+
 # Host offline reason if the X session retry count is exceeded.
 HOST_OFFLINE_REASON_SESSION_RETRIES_EXCEEDED = "SESSION_RETRIES_EXCEEDED"
 
@@ -153,6 +160,10 @@
 # This exit code is returned when a needed binary exists but cannot be executed.
 COMMAND_NOT_EXECUTABLE_EXIT_CODE = 126
 
+# User runtime directory. This is where the wayland socket is created by the
+# wayland compositor/server for clients to connect to.
+RUNTIME_DIR_TEMPLATE = "/run/user/%s"
+
 # Globals needed by the atexit cleanup() handler.
 g_desktop = None
 g_host_hash = hashlib.md5(socket.gethostname().encode()).hexdigest()
@@ -506,6 +517,7 @@
     """Launches process required for session and records the backoff time
     for inhibitors so that process restarts are not attempted again until
     that time has passed."""
+    logging.info("Setting up and launching session")
     self._init_child_env()
     self.setup_audio()
     self._setup_gnubby()
@@ -518,8 +530,17 @@
     self.session_inhibitor.record_started(MINIMUM_PROCESS_LIFETIME,
                                      backoff_time)
 
+  def _wait_for_setup_before_host_launch(self):
+    """
+    If a virtual desktop needs to do some setup before launching the host
+    process, it can override this method and ensure that the required setup is
+    done before returning from this process.
+    """
+    pass
 
   def launch_host(self, host_config, extra_start_host_args, backoff_time):
+    self._wait_for_setup_before_host_launch()
+    logging.info("Launching host process")
     # Start remoting host
     args = [HOST_BINARY_PATH, "--host-config=-"]
     if self.audio_pipe:
@@ -561,7 +582,7 @@
       self.host_proc.stdin.close()
     self.host_inhibitor.record_started(MINIMUM_PROCESS_LIFETIME, backoff_time)
 
-  def shutdown_all_procs(self):
+  def cleanup(self):
     """Send SIGTERM to all procs and wait for them to exit. Will fallback to
     SIGKILL if a process doesn't exit within 10 seconds.
     """
@@ -704,60 +725,7 @@
       failure_count += inhibitor.failures
     return failure_count
 
-  @abc.abstractmethod
   def setup_audio(self):
-    pass
-
-  @abc.abstractmethod
-  def launch_desktop_session(self):
-    """Start desktop session."""
-    pass
-
-  @abc.abstractmethod
-  def check_server_responding(self):
-    """Checks if the display server is responding to connections."""
-    return False
-
-
-class XDesktop(Desktop):
-  """Manage a single virtual X desktop"""
-
-  def __init__(self, sizes):
-    super(XDesktop, self).__init__(sizes)
-    self.xorg_conf = None
-    self.audio_pipe = None
-    self.server_supports_randr = False
-    self.randr_add_sizes = False
-    self.ssh_auth_sockname = None
-    global g_desktop
-    assert(g_desktop is None)
-    g_desktop = self
-
-  @staticmethod
-  def get_unused_display_number():
-    """Return a candidate display number for which there is currently no
-    X Server lock file"""
-    display = FIRST_X_DISPLAY_NUMBER
-    while os.path.exists(X_LOCK_FILE_TEMPLATE % display):
-      display += 1
-    return display
-
-  def _init_child_env(self):
-    super(XDesktop, self)._init_child_env()
-    # Force GDK to use the X11 backend, as otherwise parts of the host that use
-    # GTK can end up connecting to an active Wayland display instead of the
-    # CRD X11 session.
-    self.child_env["GDK_BACKEND"] = "x11"
-
-
-  def launch_session(self, *args, **kwargs):
-    logging.info("Launching X server and X session.")
-    super(XDesktop, self).launch_session(*args, **kwargs)
-
-  def setup_audio(self):
-    self._setup_pulseaudio()
-
-  def _setup_pulseaudio(self):
     self.audio_pipe = None
 
     # pulseaudio uses UNIX sockets for communication. Length of UNIX socket
@@ -807,6 +775,271 @@
 
     return True
 
+
+  @abc.abstractmethod
+  def launch_desktop_session(self):
+    """Start desktop session."""
+    pass
+
+  @abc.abstractmethod
+  def check_server_responding(self):
+    """Checks if the display server is responding to connections."""
+    return False
+
+
+class WaylandDesktop(Desktop):
+  """Manage a single virtual wayland based desktop"""
+
+  WL_SOCKET_CHECK_DELAY_SECONDS = 1
+  WL_SOCKET_CHECK_TIMEOUT_SECONDS = 30
+  # We scan for the unused socket starting from number 0. If we are not able to
+  # find anything between 0 and 100 then we error out since there could be a
+  # socket leak and we don't want to keep retrying forever.
+  MAX_WAYLAND_SOCKET_NUM = 100
+
+  def __init__(self, sizes):
+    super(WaylandDesktop, self).__init__(sizes)
+    self.debug = False
+    self._wayland_socket = None
+    self._runtime_dir = None
+    self.inhibitors = {
+        self.server_inhibitor:
+          HOST_OFFLINE_REASON_WAYLAND_SERVER_RETRIES_EXCEEDED,
+        self.host_inhibitor: HOST_OFFLINE_REASON_HOST_RETRIES_EXCEEDED
+    }
+    global g_desktop
+    assert(g_desktop is None)
+    g_desktop = self
+
+  @property
+  def runtime_dir(self):
+    if not self._runtime_dir:
+      self._runtime_dir = RUNTIME_DIR_TEMPLATE % os.getuid()
+    return self._runtime_dir
+
+  def _init_child_env(self):
+    super(WaylandDesktop, self)._init_child_env()
+    self.child_env["GDK_BACKEND"] = "wayland"
+    self.child_env["XDG_SESSION_TYPE"] = "wayland"
+    self.child_env["XDG_RUNTIME_DIR"] = self.runtime_dir
+    self._wayland_socket = self._get_unused_wayland_socket()
+    if self._wayland_socket is None:
+      logging.error("Unable to find unused wayland socket, running compositor "
+                    "is going to fail")
+      sys.exit(1)
+    else:
+      self.child_env["WAYLAND_DISPLAY"] = self._wayland_socket
+      self.child_env["DISPLAY"] = self._wayland_socket
+    self.child_env["CHROME_REMOTE_DESKTOP_SESSION"] = "1"
+    chrome_profile = os.path.join(CONFIG_DIR, "chrome-profile")
+    chrome_config_home = os.path.join(CONFIG_DIR, "chrome-config")
+    if (os.path.exists(chrome_profile)
+        and not os.path.exists(chrome_config_home)):
+      self.child_env["CHROME_USER_DATA_DIR"] = chrome_profile
+    else:
+      self.child_env["CHROME_CONFIG_HOME"] = chrome_config_home
+
+    if self.debug:
+      self.child_env["G_MESSAGES_DEBUG"] = "all"
+      self.child_env["GDK_DEBUG"]  = "all"
+      self.child_env["G_DEBUG"] = "fatal-criticals"
+      self.child_env["WAYLAND_DEBUG"] = 1
+
+  def _get_unused_wayland_socket(self):
+    """
+    Return a candidate wayland socket that is not already taken by another
+    compositor.
+    """
+    socket_num = 1
+    full_sock_path = os.path.join(self.runtime_dir, "wayland-%s" % socket_num)
+    while ((os.path.exists(full_sock_path)) and
+            socket_num <= self.MAX_WAYLAND_SOCKET_NUM)):
+      socket_num += 1
+      full_sock_path = os.path.join(self.runtime_dir, "wayland-%s" % socket_num)
+    if socket_num > self.MAX_WAYLAND_SOCKET_NUM:
+      logging.error("Unable to find an unused wayland socket (searched between "
+                    "'wayland-1' to 'wayland-%s' under runtime directory",
+                    self.MAX_WAYLAND_SOCKET_NUM, self.runtime_dir)
+      return None
+    return "wayland-%s" % socket_num
+
+  @staticmethod
+  def _is_gnome_shell_present():
+    try:
+      subprocess.check_output(["gnome-shell", "--help"],
+                              stderr=subprocess.STDOUT)
+    except subprocess.CalledProcessError as err:
+      logging.warning("Unable to find 'gnome-shell' on the host, "
+                      "returncode: %s, output: %s" % (err.returncode,
+                                                      err.output))
+      return False
+    return True
+
+  def _gnome_shell_cmd(self, screen_width=1280, screen_height=720):
+    return ["gnome-shell", "--wayland", "--headless", "--virtual-monitor",
+            "%sx%s" % (screen_width, screen_height), "--wayland-display",
+            self._wayland_socket, "--no-x11"]
+
+  def _launch_server(self, *args, **kwargs):
+    if not self._is_gnome_shell_present():
+      logging.error("Only GNOME based wayland hosts are supported currently. "
+                    "If the host is a GNOME host, please ensure that "
+                    "'gnome-shell' is installed on it")
+      # Error won't be fixed without user intervention so we quit here without
+      # attempting to relaunch.
+      sys.exit(1)
+    logging.info("Launching wayland server.")
+    self.server_proc = subprocess.Popen(self._gnome_shell_cmd(),
+                                         stdout=subprocess.PIPE,
+                                         stderr=subprocess.STDOUT,
+                                         env=self.child_env)
+
+    if not self.server_proc.pid:
+      raise Exception("Could not start wayland session")
+
+    output_filter_thread = SessionOutputFilterThread(self.server_proc.stdout,
+        "Wayland server output: ", SERVER_OUTPUT_TIME_LIMIT_SECONDS)
+    output_filter_thread.start()
+
+  def _wait_for_wayland_compositor_running(self):
+    """
+    Waits for wayland socket to be created by the wayland compositor. Returns
+    true if socket is created within the allowed timeout, else false.
+    """
+    full_socket_path = os.path.join(self.runtime_dir, self._wayland_socket)
+    start_time = time.time()
+    while not (os.path.exists(full_socket_path) and
+               time.time() - start_time < self.WL_SOCKET_CHECK_TIMEOUT_SECONDS):
+      logging.info("Wayland socket not yet present. Will wait for %s seconds "
+                   "for compositor to create it" %
+                   self.WL_SOCKET_CHECK_DELAY_SECONDS)
+      time.sleep(self.WL_SOCKET_CHECK_DELAY_SECONDS)
+    if not os.path.exists(full_socket_path):
+      logging.error("Waited for wayland compositor to create wayland "
+                    "socket: %s, but it didn't happen in %s seconds" %
+                    (full_socket_path, self.WL_SOCKET_CHECK_TIMEOUT_SECONDS))
+      return False
+    logging.info("Wayland socket detected in %s seconds: " %
+                 str(time.time() - start_time))
+    return True
+
+  def launch_desktop_session(self):
+    """
+    Restarts the portal services so that they can connect to the wayland socket.
+    This helps host process to talk to call into the the xdg-desktop-portal
+    APIs.
+    """
+    if not self._wait_for_wayland_compositor_running():
+      logging.error("Aborting wayland session since compositor isn't running")
+      return
+    logging.info("Wayland compositor is running, restarting the portal "
+                 "services now")
+    try:
+      subprocess.check_output(["systemctl", "--user", "import-environment"],
+                              stderr=subprocess.STDOUT,
+                              env=self.child_env)
+    except subprocess.CalledProcessError as err:
+      logging.error("Unable to import env vars into systemd, "
+                    "returncode: %s, output: %s" % (err.returncode,
+                                                    err.output))
+      # Host process will not be functional without these services.
+      sys.exit(1)
+
+    try:
+      subprocess.check_output(["systemctl", "--user", "restart",
+                               "xdg-desktop-portal",
+                               "xdg-desktop-portal-gnome",
+                               "xdg-desktop-portal-gtk"],
+                               stderr=subprocess.STDOUT, env=self.child_env)
+    except subprocess.CalledProcessError as err:
+      logging.error("Unable to restart portal services on the host, "
+                    "returncode: %s, output: %s" % (err.returncode, err.output))
+      # Host process will not be functional without these services.
+      sys.exit(1)
+    logging.info("Done restarting the portal services")
+
+  def _wait_for_setup_before_host_launch(self):
+    return self._wait_for_wayland_compositor_running()
+
+  def cleanup(self):
+    super(WaylandDesktop, self).cleanup()
+    if self._wayland_socket:
+      full_socket_path = os.path.join(self.runtime_dir, self._wayland_socket)
+      for to_remove in (full_socket_path, "%s.lock" % full_socket_path):
+        try:
+          os.remove(to_remove)
+        except FileNotFoundError:
+          pass
+      self._wayland_socket = None
+
+  def check_server_responding(self):
+    """
+    Connects to the server that is listening on the wayland socket.
+    If the connection succeeds, it means that the server is still up and
+    running.
+    """
+    sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+    try:
+      sock.connect(os.path.join(self.runtime_dir, self._wayland_socket))
+      # Asks the server for the global registry object
+      # (See: https://wayland-book.com/registry.html)
+      sock.sendall(struct.pack(">III", 0x00000001, 0x000C0001, 0x00000002))
+
+      num_bytes_received = 0
+      NUM_BYTES_EXPECTED = 32
+      sock.settimeout(1)  # We don't want to wait forever for a reply
+      while num_bytes_received < NUM_BYTES_EXPECTED:
+          data = sock.recv(NUM_BYTES_EXPECTED)
+          if len(data) == 0:  # Expect empty reply if server dies
+             break
+          num_bytes_received += len(data)
+          logging.debug("Wayland server replied with: %s" % data)
+      if not num_bytes_received:
+        # If we don't receive a reply at all then the server is likely not
+        # listening on the socket.
+        return False
+    except socket.error as err:
+        logging.error("Wayland server is not responding: %s" % err)
+        return False
+    finally:
+      sock.close()
+    return True
+
+
+class XDesktop(Desktop):
+  """Manage a single virtual X desktop"""
+
+  def __init__(self, sizes):
+    super(XDesktop, self).__init__(sizes)
+    self.xorg_conf = None
+    self.audio_pipe = None
+    self.server_supports_randr = False
+    self.randr_add_sizes = False
+    self.ssh_auth_sockname = None
+    global g_desktop
+    assert(g_desktop is None)
+    g_desktop = self
+
+  @staticmethod
+  def get_unused_display_number():
+    """Return a candidate display number for which there is currently no
+    X Server lock file"""
+    display = FIRST_X_DISPLAY_NUMBER
+    while os.path.exists(X_LOCK_FILE_TEMPLATE % display):
+      display += 1
+    return display
+
+  def _init_child_env(self):
+    super(XDesktop, self)._init_child_env()
+    # Force GDK to use the X11 backend, as otherwise parts of the host that use
+    # GTK can end up connecting to an active Wayland display instead of the
+    # CRD X11 session.
+    self.child_env["GDK_BACKEND"] = "x11"
+
+  def launch_session(self, *args, **kwargs):
+    logging.info("Launching X server and X session.")
+    super(XDesktop, self).launch_session(*args, **kwargs)
+
   # Returns child environment not containing TMPDIR.
   # Certain values of TMPDIR can break the X server (crbug.com/672684), so we
   # want to make sure it isn't set in the envirionment we use to start the
@@ -1446,13 +1679,14 @@
 
   global g_desktop
   if g_desktop is not None:
-    g_desktop.shutdown_all_procs()
-    if g_desktop.xorg_conf is not None:
+    g_desktop.cleanup()
+    if getattr(g_desktop, 'xorg_conf', None) is not None:
       os.remove(g_desktop.xorg_conf)
 
   g_desktop = None
   ParentProcessLogger.release_parent_if_connected(False)
 
+
 class SignalHandler:
   """Reload the config file on SIGHUP. Since we pass the configuration to the
   host processes via stdin, they can't reload it, so terminate them. They will
@@ -1699,6 +1933,9 @@
                       type=int, nargs=2, default=False, action="store",
                       help=argparse.SUPPRESS)
   parser.add_argument(dest="args", nargs="*", help=argparse.SUPPRESS)
+  parser.add_argument("--is-wayland", dest="is_wayland",
+                      default=False, action="store_true",
+                      help="If true, starts wayland session on the host.")
   return parser
 
 
@@ -1920,7 +2157,10 @@
   if host.host_id:
     logging.info("Using host_id: " + host.host_id)
 
-  desktop = XDesktop(sizes)
+  if options.is_wayland:
+    desktop = WaylandDesktop(sizes)
+  else:
+    desktop = XDesktop(sizes)
 
   # Whether we are tearing down because the display server and/or session
   # exited. This keeps us from counting processes exiting because we've
@@ -1932,7 +2172,7 @@
     # user logged out), terminate all processes. The session will be restarted
     # once everything has exited.
     if tear_down:
-      desktop.shutdown_all_procs()
+      desktop.cleanup()
 
       failure_count = desktop.aggregate_failure_count()
       tear_down = False
@@ -1974,7 +2214,6 @@
           desktop.session_proc is None):
         desktop.launch_session(options.args, backoff_time)
       if desktop.host_proc is None:
-        logging.info("Launching host process")
         extra_start_host_args = []
         if HOST_EXTRA_PARAMS_ENV_VAR in os.environ:
             extra_start_host_args = \
diff --git a/sql/sandboxed_vfs_file.cc b/sql/sandboxed_vfs_file.cc
index 45a9551..b651eb6 100644
--- a/sql/sandboxed_vfs_file.cc
+++ b/sql/sandboxed_vfs_file.cc
@@ -165,6 +165,7 @@
   DCHECK_GE(offset, 0);
 
 #if DCHECK_IS_ON()
+  // See http://www.sqlite.org/fileformat2.html#database_header
   constexpr int kSqliteDatabaseHeaderOffset = 0;
   constexpr int kSqliteDatabaseHeaderSize = 100;
   // SQLite's locking protocol only acquires locks on the database file. The
diff --git a/styleguide/c++/c++-features.md b/styleguide/c++/c++-features.md
index 8174319..4276c91 100644
--- a/styleguide/c++/c++-features.md
+++ b/styleguide/c++/c++-features.md
@@ -606,7 +606,7 @@
 None
 ***
 
-### Type trait variable templates <sup>[tbd]</sup>
+### Type trait variable templates <sup>[allowed]</sup>
 
 ```c++
 bool b = std::is_same_v<int, std::int32_t>;
@@ -661,6 +661,37 @@
 [Discussion thread](https://groups.google.com/a/chromium.org/g/cxx/c/Uv2tUfIwUfQ/m/ffMxCk9uAAAJ)
 ***
 
+### Non-member std::size/std::empty/std::data <sup>[allowed]</sup>
+
+```c++
+char buffer[260];
+memcpy(std::data(buffer), source_str.data(), std::size(buffer));
+
+if (!std::empty(container)) { ... }
+```
+
+**Description:** Non-member versions of what are often member functions on STL
+containers. Primarily useful when:
+- using `std::size()` as a replacement for the old `arraysize()` macro.
+- writing code that needs to generically operate across things like
+  `std::vector` and `std::list` (which provide `size()`, `empty()`, and `data()
+  member functions), `std::array` and `std::initialize_list` (which only provide
+  a subset of the aforementioned member functions), and regular arrays (which
+  have no member functions at all).
+
+**Documentation:**
+[std::size](https://en.cppreference.com/w/cpp/iterator/size),
+[std::empty](https://en.cppreference.com/w/cpp/iterator/empty),
+[std::data](https://en.cppreference.com/w/cpp/iterator/data)
+
+**Notes:**
+*** promo
+[Discussion thread](https://groups.google.com/a/chromium.org/g/cxx/c/58qlA3zk5ZI/m/7kKok65xAAAJ)
+
+Prefer range-based for loops over `std::size()`: range-based for loops work even
+for regular arrays.
+***
+
 ## C++17 Banned Library Features {#library-blocklist-17}
 
 The following C++17 library features are not allowed in the Chromium codebase.
@@ -1390,27 +1421,6 @@
 None
 ***
 
-### Non-member std::size/std::empty/std::data <sup>[tbd]</sup>
-
-```c++
-for (std::size_t i = 0; i < std::size(c); ++i) { ...
-if (!std::empty(c)) { ...
-std::strcpy(arr, std::data(str));
-```
-
-**Description:** Non-member versions of what are normally member functions, for
-symmetrical use with things like arrays and initializer_lists.
-
-**Documentation:**
-[std::size](https://en.cppreference.com/w/cpp/iterator/size),
-[std::empty](https://en.cppreference.com/w/cpp/iterator/empty),
-[std::data](https://en.cppreference.com/w/cpp/iterator/data)
-
-**Notes:**
-*** promo
-See `base::size`, `base::empty`, and `base::data`.
-***
-
 ### Mathematical special functions <sup>[tbd]</sup>
 
 ```c++
diff --git a/testing/buildbot/filters/fuchsia.content_browsertests.filter b/testing/buildbot/filters/fuchsia.content_browsertests.filter
index 8557c61..f3cbd150 100644
--- a/testing/buildbot/filters/fuchsia.content_browsertests.filter
+++ b/testing/buildbot/filters/fuchsia.content_browsertests.filter
@@ -1,46 +1,45 @@
 # Being ported, https://crbug.com/1071095.
 
--All/DumpAccessibilityTreeTest.*
--All/EmergencyStopTracingTest.StopOnUIThread/*
--All/RenderFrameHostManagerTest.DontSelectInvalidFiles/*
--All/RenderFrameHostManagerTest.IgnoreRendererDebugURLsWhenCrashed/*
--All/SitePerProcessBrowserTest.RenderFrameProxyNotRecreatedDuringProcessShutdown/*
--All/StartupTracingTest.TestEnableTracing/*
--All/WorkerFromAnonymousIframeNikBrowserTest.SharedWorkerRequestIsDoneWithPartitionedNetworkState/*
--All/WorkerTest.WebSocketSharedWorker/*
--AutoPictureInPictureContentBrowserTest.AutoPictureInPictureTriggeredWhenFullscreen
--BackForwardCacheBrowserTest.DoesNotCacheIfSpeechRecognitionIsStarted
+-All/EmergencyStopTracingTest.StopOnUIThread/0
+-All/EmergencyStopTracingTest.StopOnUIThread/1
+-All/RenderFrameHostManagerTest.DontSelectInvalidFiles/0
+-All/RenderFrameHostManagerTest.DontSelectInvalidFiles/1
+-All/RenderFrameHostManagerTest.IgnoreRendererDebugURLsWhenCrashed/0
+-All/RenderFrameHostManagerTest.IgnoreRendererDebugURLsWhenCrashed/1
+-All/SitePerProcessBrowserTest.RenderFrameProxyNotRecreatedDuringProcessShutdown/0
+-All/SitePerProcessBrowserTest.RenderFrameProxyNotRecreatedDuringProcessShutdown/1
+-All/WorkerFromAnonymousIframeNikBrowserTest.SharedWorkerRequestIsDoneWithPartitionedNetworkState/0
+-All/WorkerFromAnonymousIframeNikBrowserTest.SharedWorkerRequestIsDoneWithPartitionedNetworkState/1
+-All/WorkerFromAnonymousIframeNikBrowserTest.SharedWorkerRequestIsDoneWithPartitionedNetworkState/2
 -ContentBrowserTest.BrowserCrashCallStack
 -ContentBrowserTest.RendererCrashCallStack
--CrossPlatformAccessibilityBrowserTest.ControlsIdsForDateTimePopup
 -DirectSocketsTcpBrowserTest.OpenTcp_MDNS
--DnsHttpsProtocolUpgradeBrowserTest.HttpsProtocolUpgrade
+-DohHttpsProtocolUpgradeBrowserTest.HttpsProtocolUpgrade
+-DohHttpsProtocolUpgradeBrowserTest.NoProtocolUpgrade
 -MemoryTracingTest.BrowserInitiatedDump
 -MidiBrowserTest.RequestMIDIAccess
 -MidiBrowserTest.SubscribeAll
 -MojoSandboxTest.NotIsProcessSandboxed
--NavigationBrowserTestWithPerformanceManager.BeginNewNavigationAfterCommitNavigationInMainFrame
 -NetworkServiceDataMigrationBrowserTest.LegacyDataDir
 -NetworkServiceDataMigrationBrowserTest.MigrateThenNoMigrate
 -NetworkServiceDataMigrationBrowserTest.MigratedPreviouslyAndMigrateAgain
 -NetworkServiceDataMigrationBrowserTest.NewDataDirWithMigrationTest
 -NetworkServiceDataMigrationBrowserTest.NewDataDirWithNoMigrationTest
--OutOfProcess/NetworkServiceDataMigrationBrowserTestWithFailures.MigrateDataTest/*
--PrerenderBrowserTest.AbandonIfRendererProcessCrashes
--ProprietaryCodec/WebRtcMediaRecorderTest.PeerConnection/*
--SRC_ClearKey/EncryptedMediaTest.*
+-OutOfProcess/NetworkServiceDataMigrationBrowserTestWithFailures.MigrateDataTest/0
+-OutOfProcess/NetworkServiceDataMigrationBrowserTestWithFailures.MigrateDataTest/1
+-OutOfProcess/NetworkServiceDataMigrationBrowserTestWithFailures.MigrateDataTest/2
+-OutOfProcess/NetworkServiceDataMigrationBrowserTestWithFailures.MigrateDataTest/3
+-OutOfProcess/NetworkServiceDataMigrationBrowserTestWithFailures.MigrateDataTest/4
 -SitePerProcessDelegatedInkBrowserTest.MetadataAndPointGoThroughOOPIF
--SitePerProcessMouseWheelHitTestBrowserTest.MouseWheelEventPositionChange
--SnapshotBrowserTest.AsyncMultiWindowTest
--SnapshotBrowserTest.SyncMultiWindowTest
 -StorageServiceSandboxBrowserTest.CompactDatabase
--TtsSsmlBrowserTest.TestStripSSML
--UnrestrictedSharedArrayBufferOriginTrialBrowserTest.*
--UserMedia/WebRtcConstraintsBrowserTest.GetUserMediaConstraints/4
+-UnrestrictedSharedArrayBufferOriginTrialBrowserTest.CrashForBug1201589
+-UnrestrictedSharedArrayBufferOriginTrialBrowserTest.HasSharedArrayBuffer
+-UnrestrictedSharedArrayBufferOriginTrialBrowserTest.HasSharedArrayBufferReuseContext
+-UnrestrictedSharedArrayBufferOriginTrialBrowserTest.SupportForMeta
+-UnrestrictedSharedArrayBufferOriginTrialBrowserTest.TransferSharedArrayBuffer
 -WebContentsViewAuraTest.OverscrollNavigation
 -WebContentsViewAuraTest.OverscrollNavigationWithTouchHandler
 -WebContentsViewAuraTest.RepeatedQuickOverscrollGestures
--WebRtcCaptureFromElementBrowserTest.VerifyCanvasCaptureWebGLFrames
 -WebRtcVideoCaptureServiceBrowserTest.FramesSentThroughTextureVirtualDeviceGetDisplayedOnPage
 
 # crbug.com/1212491: WebSQL is being deprecated, these are low priority
diff --git a/testing/rust_gtest_interop/BUILD.gn b/testing/rust_gtest_interop/BUILD.gn
index 13f7b02..13641b03 100644
--- a/testing/rust_gtest_interop/BUILD.gn
+++ b/testing/rust_gtest_interop/BUILD.gn
@@ -36,24 +36,19 @@
     }
   }
 
-  # TODO(crbug.com/1297592): Android doesn't build tests with mixed-target
-  # support, so we can't include Rust files. This is because tests are built
-  # in a shared_library, and we have no mixed_shared_library template yet.
-  if (!is_android) {
-    rust_macro("gtest_attribute") {
-      testonly = true
+  rust_macro("gtest_attribute") {
+    testonly = true
 
-      crate_root = "gtest_attribute.rs"
-      sources = [ "gtest_attribute.rs" ]
-      deps = [
-        "//third_party/rust/proc_macro2/v1:lib",
-        "//third_party/rust/quote/v1:lib",
-        "//third_party/rust/syn/v1:lib",
-      ]
+    crate_root = "gtest_attribute.rs"
+    sources = [ "gtest_attribute.rs" ]
+    deps = [
+      "//third_party/rust/proc_macro2/v1:lib",
+      "//third_party/rust/quote/v1:lib",
+      "//third_party/rust/syn/v1:lib",
+    ]
 
-      # This target's contents are exposed as part of :rust_gtest_interop.
-      visibility = [ ":*" ]
-    }
+    # This target's contents are exposed as part of :rust_gtest_interop.
+    visibility = [ ":*" ]
   }
 
   test("rust_gtest_interop_unittests") {
@@ -64,13 +59,8 @@
       "//testing/gtest",
     ]
 
-    # TODO(crbug.com/1297592): Android doesn't build tests with mixed-target
-    # support, so we can't include Rust files. This is because tests are built
-    # in a shared_library, and we have no mixed_shared_library template yet.
-    if (!is_android) {
-      rs_deps = [ ":rust_gtest_interop" ]
-      rs_crate_root = "rust_gtest_interop_unittest.rs"
-      rs_sources = [ "rust_gtest_interop_unittest.rs" ]
-    }
+    rs_deps = [ ":rust_gtest_interop" ]
+    rs_crate_root = "rust_gtest_interop_unittest.rs"
+    rs_sources = [ "rust_gtest_interop_unittest.rs" ]
   }
 }
diff --git a/testing/rust_gtest_interop/gtest_attribute.rs b/testing/rust_gtest_interop/gtest_attribute.rs
index fdf263c..8571691 100644
--- a/testing/rust_gtest_interop/gtest_attribute.rs
+++ b/testing/rust_gtest_interop/gtest_attribute.rs
@@ -179,7 +179,7 @@
             tokens.extend(quote! {
                 {
                     #[doc=#comment]
-                    &[#(#c_chars),*]
+                    &[#(#c_chars as std::os::raw::c_char),*]
                 }
             });
         }
diff --git a/testing/rust_gtest_interop/rust_gtest_interop_unittest_main.cc b/testing/rust_gtest_interop/rust_gtest_interop_unittest_main.cc
index 8a5d86cf..1d47fe7 100644
--- a/testing/rust_gtest_interop/rust_gtest_interop_unittest_main.cc
+++ b/testing/rust_gtest_interop/rust_gtest_interop_unittest_main.cc
@@ -7,20 +7,10 @@
 #include "base/strings/stringprintf.h"
 #include "base/test/launcher/unit_test_launcher.h"
 #include "base/test/test_suite.h"
-#include "build/build_config.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 #include <iostream>
 
-// TODO(crbug.com/1297592): Android doesn't build tests with mixed-target
-// support, so we can't include Rust files. This is because tests are built in a
-// shared_library, and we have no mixed_shared_library template yet.
-#if BUILDFLAG(IS_ANDROID)
-// Placeholder to run the test suite on the bots, until Android can build gtests
-// with Rust in them.
-TEST(Test, AndroidPlaceholder) {}
-#endif
-
 // Update this when adding a new test to rust_test_interop_unittest.rs.
 int kNumTests = 8;
 
@@ -53,7 +43,6 @@
       my_argc, my_argv,
       base::BindOnce(&base::TestSuite::Run, base::Unretained(&test_suite)));
 
-#if !BUILDFLAG(IS_ANDROID)
   if (is_subprocess()) {
     // Double-check that we actually ran all the tests. If this fails we'll see
     // all the tests marked as "fail on exit" since the whole process is
@@ -66,7 +55,6 @@
       return 1;
     }
   }
-#endif
 
   return result;
 }
diff --git a/testing/test.gni b/testing/test.gni
index aba81b2..48cde7bf 100644
--- a/testing/test.gni
+++ b/testing/test.gni
@@ -12,6 +12,7 @@
 import("//build/config/gclient_args.gni")
 import("//build/config/rts.gni")
 import("//build/rust/mixed_executable.gni")
+import("//build/rust/mixed_shared_library.gni")
 import("//build_overrides/build.gni")
 
 declare_args() {
@@ -184,6 +185,7 @@
       }
     } else {
       _library_target = "${target_name}__library"
+      _library_crate_name = "${target_name}_library"
       _apk_target = "${target_name}__apk"
       _apk_specific_vars = [
         "allow_cleartext_traffic",
@@ -221,13 +223,15 @@
         }
       }
 
-      # TODO(crbug.com/1296718): Need a mixed_shared_library for rust tests on
-      # Android (or can it be a mixed_component?).
-      shared_library(_library_target) {
-        # Configs will always be defined since we set_defaults in BUILDCONFIG.gn.
+      mixed_shared_library(_library_target) {
+        # Configs will always be defined since we set_defaults in
+        # BUILDCONFIG.gn.
         configs = []  # Prevent list overwriting warning.
         configs = invoker.configs
 
+        rs_crate_name = _library_crate_name
+        rs_configs = [ "//build/rust:test" ]
+
         forward_variables_from(
             invoker,
             "*",
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 3ba0482..bdad468 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -3775,6 +3775,25 @@
             ]
         }
     ],
+    "JourneysUseEngagementScoreCache": [
+        {
+            "platforms": [
+                "chromeos",
+                "chromeos_lacros",
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "JourneysUseEngagementScoreCache"
+                    ]
+                }
+            ]
+        }
+    ],
     "KeyboardAccessoryAddressIPH": [
         {
             "platforms": [
diff --git a/third_party/android_deps/BUILD.gn b/third_party/android_deps/BUILD.gn
index 879e147f..c9430e9 100644
--- a/third_party/android_deps/BUILD.gn
+++ b/third_party/android_deps/BUILD.gn
@@ -70,10 +70,7 @@
       ":org_robolectric_shadows_multidex_java",
       ":org_robolectric_shadows_playservices_java",
       ":org_robolectric_utils_java",
-
-      # TODO: Remove 2 modification after robolectric 4.6 has landed.
-      # This is a temporary hack to force cache updating.
-      ":org_robolectric_utils_reflector2_java",
+      ":org_robolectric_utils_reflector_java",
 
       "//third_party/robolectric:robolectric_runtime_jars",
       "//third_party/robolectric:android-all-10-robolectric-5803371_java",
@@ -1459,10 +1456,7 @@
       ":org_robolectric_sandbox_java",
       ":org_robolectric_shadows_framework_java",
       ":org_robolectric_utils_java",
-
-      # TODO: Remove 2 modification after robolectric 4.6 has landed.
-      # This is a temporary hack to force cache updating.
-      ":org_robolectric_utils_reflector2_java",
+      ":org_robolectric_utils_reflector_java",
       "//third_party/androidx:androidx_test_monitor_java",
       "//third_party/bouncycastle:bouncycastle_java",
     ]
@@ -2209,10 +2203,7 @@
       ":org_robolectric_pluginapi_java",
       ":org_robolectric_sandbox_java",
       ":org_robolectric_shadowapi_java",
-
-      # TODO: Remove 2 modification after robolectric 4.6 has landed.
-      # This is a temporary hack to force cache updating.
-      ":org_robolectric_utils_reflector2_java",
+      ":org_robolectric_utils_reflector_java",
     ]
     bypass_platform_checks = true
   }
@@ -2299,10 +2290,7 @@
       ":org_robolectric_annotations_java",
       ":org_robolectric_shadowapi_java",
       ":org_robolectric_utils_java",
-
-      # TODO: Remove 2 modification after robolectric 4.6 has landed.
-      # This is a temporary hack to force cache updating.
-      ":org_robolectric_utils_reflector2_java",
+      ":org_robolectric_utils_reflector_java",
     ]
     bypass_platform_checks = true
   }
@@ -2348,10 +2336,7 @@
       ":org_robolectric_resources_java",
       ":org_robolectric_shadowapi_java",
       ":org_robolectric_utils_java",
-
-      # TODO: Remove 2 modification after robolectric 4.6 has landed.
-      # This is a temporary hack to force cache updating.
-      ":org_robolectric_utils_reflector2_java",
+      ":org_robolectric_utils_reflector_java",
       "//third_party/androidx:androidx_annotation_annotation_java",
       "//third_party/androidx:androidx_test_monitor_java",
       "//third_party/icu4j:icu4j_java",
@@ -2361,7 +2346,7 @@
   }
 
   # This is generated, do not edit. Update BuildConfigGenerator.groovy instead.
-  java_prebuilt("org_robolectric_utils_reflector2_java") {
+  java_prebuilt("org_robolectric_utils_reflector_java") {
     jar_path = "libs/org_robolectric_utils_reflector/utils-reflector-4.6.1.jar"
     output_name = "org_robolectric_utils_reflector"
     enable_bytecode_checks = false
diff --git a/third_party/android_deps/buildSrc/src/main/groovy/ChromiumDepGraph.groovy b/third_party/android_deps/buildSrc/src/main/groovy/ChromiumDepGraph.groovy
index 03327c1..f8f3acf 100644
--- a/third_party/android_deps/buildSrc/src/main/groovy/ChromiumDepGraph.groovy
+++ b/third_party/android_deps/buildSrc/src/main/groovy/ChromiumDepGraph.groovy
@@ -36,13 +36,6 @@
             licenseName: 'BSD 3-Clause',
             generateTarget: false,
             cipdSuffix: 'cr1'),
-        backport_util_concurrent_backport_util_concurrent: new PropertyOverride(
-            licensePath: 'licenses/CC01.0.txt',
-            licenseName: 'CC0 1.0'),
-        classworlds_classworlds: new PropertyOverride(
-            description: 'A class loader framework.',
-            licensePath: 'licenses/Codehaus_License-2009.txt',
-            licenseName: 'MIT'),
         com_github_kevinstern_software_and_algorithms: new PropertyOverride(
             licenseUrl: 'https://raw.githubusercontent.com/KevinStern/software-and-algorithms/master/LICENSE',
             licenseName: 'MIT License'),
@@ -125,96 +118,12 @@
             url: 'https://github.com/google/guava',
             licenseUrl: 'https://www.apache.org/licenses/LICENSE-2.0.txt',
             licenseName: 'Apache 2.0'),
-        nekohtml_nekohtml: new PropertyOverride(
-            description: 'NekoHTML is a simple HTML scanner and tag balancer.'),
-        nekohtml_xercesMinimal: new PropertyOverride(
-            description: 'Only contains necessary framework & Xerces2 classes',
-            url: 'http://nekohtml.sourceforge.net/index.html',
-            licenseUrl: 'https://www.apache.org/licenses/LICENSE-2.0.txt',
-            licenseName: 'Apache 2.0',
-            overrideLatest: true),
-        org_apache_ant_ant: new PropertyOverride(
-            url: 'https://ant.apache.org/',
-            licenseUrl: 'https://www.apache.org/licenses/LICENSE-2.0.txt',
-            licenseName: 'Apache 2.0'),
-        org_apache_ant_ant_launcher: new PropertyOverride(
-            url: 'https://ant.apache.org/',
-            licenseUrl: 'https://www.apache.org/licenses/LICENSE-2.0.txt',
-            licenseName: 'Apache 2.0'),
-        org_apache_maven_maven_ant_tasks: new PropertyOverride(
-            url: 'https://ant.apache.org/',
-            licenseUrl: 'https://www.apache.org/licenses/LICENSE-2.0.txt',
-            licenseName: 'Apache 2.0'),
-        org_apache_maven_maven_artifact: new PropertyOverride(
-            url: 'https://maven.apache.org/',
-            licenseUrl: 'https://www.apache.org/licenses/LICENSE-2.0.txt',
-            licenseName: 'Apache 2.0'),
-        org_apache_maven_maven_artifact_manager: new PropertyOverride(
-            url: 'https://maven.apache.org/',
-            licenseUrl: 'https://www.apache.org/licenses/LICENSE-2.0.txt',
-            licenseName: 'Apache 2.0'),
-        org_apache_maven_maven_error_diagnostics: new PropertyOverride(
-            url: 'https://maven.apache.org/',
-            licenseUrl: 'https://www.apache.org/licenses/LICENSE-2.0.txt',
-            licenseName: 'Apache 2.0'),
-        org_apache_maven_maven_model: new PropertyOverride(
-            url: 'https://maven.apache.org/',
-            licenseUrl: 'https://www.apache.org/licenses/LICENSE-2.0.txt',
-            licenseName: 'Apache 2.0'),
-        org_apache_maven_maven_plugin_registry: new PropertyOverride(
-            url: 'https://maven.apache.org/',
-            licenseUrl: 'https://www.apache.org/licenses/LICENSE-2.0.txt',
-            licenseName: 'Apache 2.0'),
-        org_apache_maven_maven_profile: new PropertyOverride(
-            url: 'https://maven.apache.org/',
-            licenseUrl: 'https://www.apache.org/licenses/LICENSE-2.0.txt',
-            licenseName: 'Apache 2.0'),
-        org_apache_maven_maven_project: new PropertyOverride(
-            url: 'https://maven.apache.org/',
-            licenseUrl: 'https://www.apache.org/licenses/LICENSE-2.0.txt',
-            licenseName: 'Apache 2.0'),
-        org_apache_maven_maven_repository_metadata: new PropertyOverride(
-            url: 'https://maven.apache.org/',
-            licenseUrl: 'https://www.apache.org/licenses/LICENSE-2.0.txt',
-            licenseName: 'Apache 2.0'),
-        org_apache_maven_maven_settings: new PropertyOverride(
-            url: 'https://maven.apache.org/',
-            licenseUrl: 'https://www.apache.org/licenses/LICENSE-2.0.txt',
-            licenseName: 'Apache 2.0'),
-        org_apache_maven_wagon_wagon_file: new PropertyOverride(
-            url: 'https://maven.apache.org/',
-            licenseUrl: 'https://www.apache.org/licenses/LICENSE-2.0.txt',
-            licenseName: 'Apache 2.0'),
-        org_apache_maven_wagon_wagon_http_lightweight: new PropertyOverride(
-            url: 'https://maven.apache.org/',
-            licenseUrl: 'https://www.apache.org/licenses/LICENSE-2.0.txt',
-            licenseName: 'Apache 2.0'),
-        org_apache_maven_wagon_wagon_http_shared: new PropertyOverride(
-            url: 'https://maven.apache.org/',
-            licenseUrl: 'https://www.apache.org/licenses/LICENSE-2.0.txt',
-            licenseName: 'Apache 2.0'),
-        org_apache_maven_wagon_wagon_provider_api: new PropertyOverride(
-            url: 'https://maven.apache.org/',
-            licenseUrl: 'https://www.apache.org/licenses/LICENSE-2.0.txt',
-            licenseName: 'Apache 2.0'),
         org_codehaus_mojo_animal_sniffer_annotations: new PropertyOverride(
             url: 'http://www.mojohaus.org/animal-sniffer/animal-sniffer-annotations/',
             /* groovylint-disable-next-line LineLength */
             licenseUrl: 'https://raw.githubusercontent.com/mojohaus/animal-sniffer/master/animal-sniffer-annotations/pom.xml',
             licensePath: 'licenses/Codehaus_License-2009.txt',
             licenseName: 'MIT'),
-        org_codehaus_plexus_plexus_container_default: new PropertyOverride(
-            url: 'https://codehaus-plexus.github.io/',
-            licenseUrl: 'https://www.apache.org/licenses/LICENSE-2.0.txt',
-            licenseName: 'Apache 2.0'),
-        org_codehaus_plexus_plexus_interpolation: new PropertyOverride(
-            url: 'https://codehaus-plexus.github.io/',
-            licenseUrl: 'https://www.apache.org/licenses/LICENSE-2.0.txt',
-            licenseName: 'Apache 2.0'),
-        org_codehaus_plexus_plexus_utils: new PropertyOverride(
-            url: 'https://codehaus-plexus.github.io/',
-            licenseUrl: 'https://www.apache.org/licenses/LICENSE-2.0.txt',
-            licenseName: 'Apache 2.0'),
         org_eclipse_jgit_org_eclipse_jgit: new PropertyOverride(
             url: 'https://www.eclipse.org/jgit/',
             licenseUrl: 'https://www.eclipse.org/org/documents/edl-v10.html',
diff --git a/third_party/android_deps/libs/org_apache_ant_ant/3pp/fetch.py b/third_party/android_deps/libs/org_apache_ant_ant/3pp/fetch.py
new file mode 100644
index 0000000..d116427e
--- /dev/null
+++ b/third_party/android_deps/libs/org_apache_ant_ant/3pp/fetch.py
@@ -0,0 +1,73 @@
+from __future__ import print_function
+
+import argparse
+import json
+import os
+import re
+
+from six.moves import urllib
+
+_REPO_URL = 'https://repo.maven.apache.org/maven2'
+_GROUP_NAME = 'org/apache/ant'
+_MODULE_NAME = 'ant'
+_FILE_EXT = 'jar'
+_OVERRIDE_LATEST = None
+_PATCH_VERSION = 'cr0'
+
+
+def do_latest():
+    if _OVERRIDE_LATEST is not None:
+        print(_OVERRIDE_LATEST)
+        return
+    maven_metadata_url = '{}/{}/{}/maven-metadata.xml'.format(
+        _REPO_URL, _GROUP_NAME, _MODULE_NAME)
+    metadata = urllib.request.urlopen(maven_metadata_url).read().decode(
+        'utf-8')
+    # Do not parse xml with the python included parser since it is susceptible
+    # to maliciously crafted xmls. Only use regular expression parsing to be
+    # safe. RE should be enough to handle what we need to extract.
+    match = re.search('<latest>([^<]+)</latest>', metadata)
+    if match:
+        latest = match.group(1)
+    else:
+        # if no latest info was found just hope the versions are sorted and the
+        # last one is the latest (as is commonly the case).
+        latest = re.findall('<version>([^<]+)</version>', metadata)[-1]
+    print(latest + f'.{_PATCH_VERSION}')
+
+
+def get_download_url(version):
+    # Remove the patch version when getting the download url
+    version_no_patch, patch = version.rsplit('.', 1)
+    if patch.startswith('cr'):
+        version = version_no_patch
+    file_url = '{0}/{1}/{2}/{3}/{2}-{3}.{4}'.format(_REPO_URL, _GROUP_NAME,
+                                                    _MODULE_NAME, version,
+                                                    _FILE_EXT)
+    file_name = file_url.rsplit('/', 1)[-1]
+
+    partial_manifest = {
+        'url': [file_url],
+        'name': [file_name],
+        'ext': '.' + _FILE_EXT,
+    }
+    print(json.dumps(partial_manifest))
+
+
+def main():
+    ap = argparse.ArgumentParser()
+    sub = ap.add_subparsers()
+
+    latest = sub.add_parser("latest")
+    latest.set_defaults(func=lambda _opts: do_latest())
+
+    download = sub.add_parser("get_url")
+    download.set_defaults(
+        func=lambda _opts: get_download_url(os.environ['_3PP_VERSION']))
+
+    opts = ap.parse_args()
+    opts.func(opts)
+
+
+if __name__ == '__main__':
+    main()
diff --git a/third_party/blink/public/common/BUILD.gn b/third_party/blink/public/common/BUILD.gn
index 6e3f85c..fd5f2e2 100644
--- a/third_party/blink/public/common/BUILD.gn
+++ b/third_party/blink/public/common/BUILD.gn
@@ -109,6 +109,7 @@
     "action_after_pagehide.h",
     "associated_interfaces/associated_interface_provider.h",
     "associated_interfaces/associated_interface_registry.h",
+    "attribution_reporting/constants.h",
     "blob/blob_utils.h",
     "bluetooth/web_bluetooth_device_id.h",
     "bluetooth/web_bluetooth_device_id_mojom_traits.h",
diff --git a/third_party/blink/public/common/attribution_reporting/OWNERS b/third_party/blink/public/common/attribution_reporting/OWNERS
new file mode 100644
index 0000000..6a3b4dd
--- /dev/null
+++ b/third_party/blink/public/common/attribution_reporting/OWNERS
@@ -0,0 +1 @@
+file://content/browser/attribution_reporting/OWNERS
diff --git a/third_party/blink/public/common/attribution_reporting/constants.h b/third_party/blink/public/common/attribution_reporting/constants.h
new file mode 100644
index 0000000..0c6a969
--- /dev/null
+++ b/third_party/blink/public/common/attribution_reporting/constants.h
@@ -0,0 +1,16 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_PUBLIC_COMMON_ATTRIBUTION_REPORTING_CONSTANTS_H_
+#define THIRD_PARTY_BLINK_PUBLIC_COMMON_ATTRIBUTION_REPORTING_CONSTANTS_H_
+
+namespace blink {
+
+constexpr size_t kMaxBytesPerAttributionFilterString = 25;
+constexpr size_t kMaxValuesPerAttributionFilter = 50;
+constexpr size_t kMaxAttributionFiltersPerSource = 50;
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_PUBLIC_COMMON_ATTRIBUTION_REPORTING_CONSTANTS_H_
diff --git a/third_party/blink/public/mojom/conversions/attribution_data_host.mojom b/third_party/blink/public/mojom/conversions/attribution_data_host.mojom
index 8acbf36d..5c3e743 100644
--- a/third_party/blink/public/mojom/conversions/attribution_data_host.mojom
+++ b/third_party/blink/public/mojom/conversions/attribution_data_host.mojom
@@ -11,6 +11,14 @@
   uint64 value;
 };
 
+// Filter data for selectively matching attribution sources and triggers.
+// See https://github.com/WICG/conversion-measurement-api/blob/main/EVENT.md#optional-attribution-filters
+// for details.
+struct AttributionFilterData {
+  // Map of filter name to a possibly empty set of values.
+  map<string, array<string>> filter_values;
+};
+
 struct AttributionSourceData {
   // Target site where this source will be triggered.
   //
@@ -34,6 +42,8 @@
   // A key that is propagated through the Attribution Reporting API for
   // debugging purposes.
   AttributionDebugKey? debug_key;
+
+  AttributionFilterData filter_data;
 };
 
 // Browser-process interface responsible for processing attribution
diff --git a/third_party/blink/renderer/bindings/scripts/bind_gen/interface.py b/third_party/blink/renderer/bindings/scripts/bind_gen/interface.py
index 5836ce3..e1f0dc5 100644
--- a/third_party/blink/renderer/bindings/scripts/bind_gen/interface.py
+++ b/third_party/blink/renderer/bindings/scripts/bind_gen/interface.py
@@ -5124,7 +5124,7 @@
                "}}, ")
     if no_alloc_direct_call_enabled:
         pattern = ("{{" + pattern + "{v8_cfunction_table}, "
-                   "base::size({v8_cfunction_table})}}, ")
+                   "std::size({v8_cfunction_table})}}, ")
     for entry in operation_entries:
         if no_alloc_direct_call_enabled:
             nadc_overload_table_name = name_style.constant(
diff --git a/third_party/blink/renderer/build/scripts/core/css/templates/style_property_shorthand.cc.tmpl b/third_party/blink/renderer/build/scripts/core/css/templates/style_property_shorthand.cc.tmpl
index 8928360..5275a96 100644
--- a/third_party/blink/renderer/build/scripts/core/css/templates/style_property_shorthand.cc.tmpl
+++ b/third_party/blink/renderer/build/scripts/core/css/templates/style_property_shorthand.cc.tmpl
@@ -24,7 +24,8 @@
 
 #include "third_party/blink/renderer/core/style_property_shorthand.h"
 
-#include "base/cxx17_backports.h"  // for base::size()
+#include <iterator>
+
 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
 
 {% macro define_shorthand(property, expansion) -%}
@@ -35,7 +36,7 @@
   };
 
   static const StylePropertyShorthand shorthand(
-      CSSPropertyID::{{property.enum_key}}, longhands, base::size(longhands));
+      CSSPropertyID::{{property.enum_key}}, longhands, std::size(longhands));
 {%- endmacro %}
 
 namespace blink {
diff --git a/third_party/blink/renderer/build/scripts/templates/element_factory.cc.tmpl b/third_party/blink/renderer/build/scripts/templates/element_factory.cc.tmpl
index fbfa1ec6..66aefc4 100644
--- a/third_party/blink/renderer/build/scripts/templates/element_factory.cc.tmpl
+++ b/third_party/blink/renderer/build/scripts/templates/element_factory.cc.tmpl
@@ -5,7 +5,8 @@
 
 #include "third_party/blink/renderer/core/{{namespace|lower}}_element_factory.h"
 
-#include "base/cxx17_backports.h"  // for base::size()
+#include <iterator>
+
 #include "third_party/blink/renderer/core/{{namespace|lower}}_names.h"
 {% for header in tags|groupby('interface_header') %}
 #include "{{header[0]}}"
@@ -56,7 +57,7 @@
     { {{cpp_namespace}}::{{tag|symbol}}Tag, {{namespace}}{{tag.name.to_upper_camel_case()}}Constructor },
   {% endfor %}
   };
-  for (size_t i = 0; i < base::size(data); i++)
+  for (size_t i = 0; i < std::size(data); i++)
     g_{{namespace|lower}}_constructors->Set(data[i].tag.LocalName(), data[i].func);
 }
 
diff --git a/third_party/blink/renderer/build/scripts/templates/make_names.cc.tmpl b/third_party/blink/renderer/build/scripts/templates/make_names.cc.tmpl
index 7add4147a..2802817e 100644
--- a/third_party/blink/renderer/build/scripts/templates/make_names.cc.tmpl
+++ b/third_party/blink/renderer/build/scripts/templates/make_names.cc.tmpl
@@ -5,7 +5,8 @@
 
 #include "{{this_include_path}}"
 
-#include "base/cxx17_backports.h"  // for base::size()
+#include <iterator>
+
 #include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
 
 namespace blink {
@@ -34,7 +35,7 @@
   {% endfor %}
   };
 
-  for (size_t i = 0; i < base::size(kNames); ++i) {
+  for (size_t i = 0; i < std::size(kNames); ++i) {
     StringImpl* impl = StringImpl::CreateStatic(kNames[i].name, kNames[i].length, kNames[i].hash);
     void* address = reinterpret_cast<AtomicString*>(&{{suffix|lower}}names_storage) + i;
     new (address) AtomicString(impl);
diff --git a/third_party/blink/renderer/build/scripts/templates/make_qualified_names.cc.tmpl b/third_party/blink/renderer/build/scripts/templates/make_qualified_names.cc.tmpl
index 65930e2..ed042c3 100644
--- a/third_party/blink/renderer/build/scripts/templates/make_qualified_names.cc.tmpl
+++ b/third_party/blink/renderer/build/scripts/templates/make_qualified_names.cc.tmpl
@@ -7,7 +7,8 @@
 
 #include <memory>
 
-#include "base/cxx17_backports.h"  // for base::size()
+#include <iterator>
+
 #include "third_party/blink/renderer/platform/wtf/static_constructors.h"
 #include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
 
@@ -77,7 +78,7 @@
   size_t tag_i = 0;
   {% endif %}
   size_t attr_i = 0;
-  for (size_t i = 0; i < base::size(kNames); ++i) {
+  for (size_t i = 0; i < std::size(kNames); ++i) {
     StringImpl* impl = StringImpl::CreateStatic(kNames[i].name, kNames[i].length, kNames[i].hash);
     {% if tags %}
     if (kNames[i].is_tag) {
diff --git a/third_party/blink/renderer/core/css/css_properties.json5 b/third_party/blink/renderer/core/css/css_properties.json5
index 7fea0cb..93dc1a1 100644
--- a/third_party/blink/renderer/core/css/css_properties.json5
+++ b/third_party/blink/renderer/core/css/css_properties.json5
@@ -5128,6 +5128,7 @@
     {
       name: "-webkit-user-modify",
       property_methods: ["CSSValueFromComputedStyleInternal"],
+      computed_style_custom_functions: ["getter"],
       inherited: true,
       field_group: "*",
       field_template: "keyword",
diff --git a/third_party/blink/renderer/core/css/resolver/style_adjuster.cc b/third_party/blink/renderer/core/css/resolver/style_adjuster.cc
index c056842..fc139008 100644
--- a/third_party/blink/renderer/core/css/resolver/style_adjuster.cc
+++ b/third_party/blink/renderer/core/css/resolver/style_adjuster.cc
@@ -81,22 +81,6 @@
   return overflow == EOverflow::kClip || overflow == EOverflow::kVisible;
 }
 
-bool IsEditableElement(Element* element, const ComputedStyle& style) {
-  if (style.UserModify() != EUserModify::kReadOnly)
-    return true;
-
-  if (!element)
-    return false;
-
-  if (auto* textarea = DynamicTo<HTMLTextAreaElement>(*element))
-    return !textarea->IsDisabledOrReadOnly();
-
-  if (auto* input = DynamicTo<HTMLInputElement>(*element))
-    return !input->IsDisabledOrReadOnly() && input->IsTextField();
-
-  return false;
-}
-
 TouchAction AdjustTouchActionForElement(TouchAction touch_action,
                                         const ComputedStyle& style,
                                         const ComputedStyle& parent_style,
@@ -645,10 +629,28 @@
   }
 }
 
-static void AdjustEffectiveTouchAction(ComputedStyle& style,
-                                       const ComputedStyle& parent_style,
-                                       Element* element,
-                                       bool is_svg_root) {
+bool StyleAdjuster::IsEditableElement(Element* element,
+                                      const ComputedStyle& style) {
+  if (style.UserModify() != EUserModify::kReadOnly)
+    return true;
+
+  if (!element)
+    return false;
+
+  if (auto* textarea = DynamicTo<HTMLTextAreaElement>(*element))
+    return !textarea->IsDisabledOrReadOnly();
+
+  if (auto* input = DynamicTo<HTMLInputElement>(*element))
+    return !input->IsDisabledOrReadOnly() && input->IsTextField();
+
+  return false;
+}
+
+void StyleAdjuster::AdjustEffectiveTouchAction(
+    ComputedStyle& style,
+    const ComputedStyle& parent_style,
+    Element* element,
+    bool is_svg_root) {
   TouchAction inherited_action = parent_style.GetEffectiveTouchAction();
 
   bool is_replaced_canvas = element && IsA<HTMLCanvasElement>(element) &&
diff --git a/third_party/blink/renderer/core/css/resolver/style_adjuster.h b/third_party/blink/renderer/core/css/resolver/style_adjuster.h
index b668fff5..199be33 100644
--- a/third_party/blink/renderer/core/css/resolver/style_adjuster.h
+++ b/third_party/blink/renderer/core/css/resolver/style_adjuster.h
@@ -45,6 +45,11 @@
   static void AdjustStyleForTextCombine(ComputedStyle&);
 
  private:
+  static bool IsEditableElement(Element*, const ComputedStyle&);
+  static void AdjustEffectiveTouchAction(ComputedStyle& style,
+                                         const ComputedStyle& parent_style,
+                                         Element* element,
+                                         bool is_svg_root);
   static void AdjustOverflow(ComputedStyle& style, Element* element);
   static void AdjustForForcedColorsMode(ComputedStyle& style);
 };
diff --git a/third_party/blink/renderer/core/dom/element.cc b/third_party/blink/renderer/core/dom/element.cc
index d1d9111..19886307 100644
--- a/third_party/blink/renderer/core/dom/element.cc
+++ b/third_party/blink/renderer/core/dom/element.cc
@@ -254,7 +254,7 @@
   const auto* style = element.GetComputedStyle();
   if (!style)
     return is_editable;
-  auto user_modify = style->UserModify();
+  auto user_modify = style->UsedUserModify();
   const AtomicString& ce_value =
       element.FastGetAttribute(html_names::kContenteditableAttr);
   if (ce_value.IsNull() || EqualIgnoringASCIICase(ce_value, "false")) {
@@ -422,7 +422,7 @@
   // TODO(layout-dev): Once LayoutNG handles inline content editable, we
   // should get rid of following code fragment.
   if (!RuntimeEnabledFeatures::EditingNGEnabled()) {
-    if (style.UserModify() != EUserModify::kReadOnly ||
+    if (style.UsedUserModify() != EUserModify::kReadOnly ||
         document.InDesignMode()) {
       UseCounter::Count(document, WebFeature::kLegacyLayoutByEditing);
       return true;
diff --git a/third_party/blink/renderer/core/editing/commands/apply_block_element_command.cc b/third_party/blink/renderer/core/editing/commands/apply_block_element_command.cc
index e9bc202..1917d3e 100644
--- a/third_party/blink/renderer/core/editing/commands/apply_block_element_command.cc
+++ b/third_party/blink/renderer/core/editing/commands/apply_block_element_command.cc
@@ -330,7 +330,7 @@
     }
 
     // If end is in the middle of a text node, split.
-    if (end_style->UserModify() != EUserModify::kReadOnly &&
+    if (end_style->UsedUserModify() != EUserModify::kReadOnly &&
         !end_style->CollapseWhiteSpace() && end.OffsetInContainerNode() &&
         end.OffsetInContainerNode() <
             static_cast<int>(To<Text>(end.ComputeContainerNode())->length())) {
diff --git a/third_party/blink/renderer/core/editing/commands/move_commands.cc b/third_party/blink/renderer/core/editing/commands/move_commands.cc
index 4f25a8a..1e10044 100644
--- a/third_party/blink/renderer/core/editing/commands/move_commands.cc
+++ b/third_party/blink/renderer/core/editing/commands/move_commands.cc
@@ -139,7 +139,7 @@
     return;
 
   const ComputedStyle* style = node->GetComputedStyle();
-  if (!style || style->UserModify() != EUserModify::kReadOnly)
+  if (!style || style->UsedUserModify() != EUserModify::kReadOnly)
     return;
 
   Element* new_focused_element = nullptr;
diff --git a/third_party/blink/renderer/core/editing/commands/replace_selection_command.cc b/third_party/blink/renderer/core/editing/commands/replace_selection_command.cc
index 3ac8b40..9a0f26a4 100644
--- a/third_party/blink/renderer/core/editing/commands/replace_selection_command.cc
+++ b/third_party/blink/renderer/core/editing/commands/replace_selection_command.cc
@@ -1055,17 +1055,20 @@
 
 void ReplaceSelectionCommand::SetUpStyle(const VisibleSelection& selection) {
   // We can skip matching the style if the selection is plain text.
-  // TODO(editing-dev): Use IsEditablePosition instead of using UserModify
+  // TODO(editing-dev): Use IsEditablePosition instead of using UsedUserModify
   // directly.
   if ((selection.Start().AnchorNode()->GetLayoutObject() &&
        selection.Start()
                .AnchorNode()
                ->GetLayoutObject()
                ->Style()
-               ->UserModify() == EUserModify::kReadWritePlaintextOnly) &&
+               ->UsedUserModify() == EUserModify::kReadWritePlaintextOnly) &&
       (selection.End().AnchorNode()->GetLayoutObject() &&
-       selection.End().AnchorNode()->GetLayoutObject()->Style()->UserModify() ==
-           EUserModify::kReadWritePlaintextOnly))
+       selection.End()
+               .AnchorNode()
+               ->GetLayoutObject()
+               ->Style()
+               ->UsedUserModify() == EUserModify::kReadWritePlaintextOnly))
     match_style_ = false;
 
   if (match_style_) {
diff --git a/third_party/blink/renderer/core/editing/editing_strategy.cc b/third_party/blink/renderer/core/editing/editing_strategy.cc
index 8afd1695..280038c4 100644
--- a/third_party/blink/renderer/core/editing/editing_strategy.cc
+++ b/third_party/blink/renderer/core/editing/editing_strategy.cc
@@ -19,7 +19,7 @@
     return blink::EUserSelect::kNone;
 
   const blink::ComputedStyle* style = node.GetLayoutObject()->Style();
-  if (style->UserModify() != blink::EUserModify::kReadOnly)
+  if (style->UsedUserModify() != blink::EUserModify::kReadOnly)
     return blink::EUserSelect::kText;
 
   return style->UsedUserSelect();
diff --git a/third_party/blink/renderer/core/editing/editing_utilities.cc b/third_party/blink/renderer/core/editing/editing_utilities.cc
index b08a54c6..c6c1c16d 100644
--- a/third_party/blink/renderer/core/editing/editing_utilities.cc
+++ b/third_party/blink/renderer/core/editing/editing_utilities.cc
@@ -357,7 +357,7 @@
     const ComputedStyle* style = ancestor.GetComputedStyle();
     if (!style)
       continue;
-    switch (style->UserModify()) {
+    switch (style->UsedUserModify()) {
       case EUserModify::kReadOnly:
         return false;
       case EUserModify::kReadWrite:
diff --git a/third_party/blink/renderer/core/exported/web_view_test.cc b/third_party/blink/renderer/core/exported/web_view_test.cc
index bb3a1a6..0ec17b9 100644
--- a/third_party/blink/renderer/core/exported/web_view_test.cc
+++ b/third_party/blink/renderer/core/exported/web_view_test.cc
@@ -3087,7 +3087,7 @@
 }
 
 TEST_F(WebViewTest, ContextMenuAndDragOnImageLongPress) {
-  ScopedTouchDragAndContextMenuForTest touch_drag_and_context_menu(true);
+  ScopedTouchDragOnShortPressForTest touch_drag_on_short_press(true);
   RegisterMockedHttpURLLoad("long_press_links_and_images.html");
 
   url_test_helpers::RegisterMockedURLLoad(
@@ -3114,7 +3114,7 @@
 }
 
 TEST_F(WebViewTest, ContextMenuAndDragOnLinkLongPress) {
-  ScopedTouchDragAndContextMenuForTest touch_drag_and_context_menu(true);
+  ScopedTouchDragOnShortPressForTest touch_drag_on_short_press(true);
 
   RegisterMockedHttpURLLoad("long_press_links_and_images.html");
 
diff --git a/third_party/blink/renderer/core/frame/attribution_src_loader.cc b/third_party/blink/renderer/core/frame/attribution_src_loader.cc
index 2df285c..73ff4df 100644
--- a/third_party/blink/renderer/core/frame/attribution_src_loader.cc
+++ b/third_party/blink/renderer/core/frame/attribution_src_loader.cc
@@ -10,6 +10,7 @@
 #include "services/network/public/mojom/referrer_policy.mojom-blink.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
+#include "third_party/blink/public/common/attribution_reporting/constants.h"
 #include "third_party/blink/public/mojom/conversions/attribution_data_host.mojom-blink.h"
 #include "third_party/blink/public/mojom/conversions/conversions.mojom-blink.h"
 #include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h"
@@ -36,9 +37,63 @@
 #include "third_party/blink/renderer/platform/weborigin/security_origin.h"
 #include "third_party/blink/renderer/platform/weborigin/security_policy.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
 
 namespace blink {
 
+namespace {
+
+bool ParseAttributionFilterData(
+    JSONValue* value,
+    mojom::blink::AttributionFilterData& filter_data) {
+  if (!value)
+    return true;
+
+  JSONObject* object = JSONObject::Cast(value);
+  if (!object)
+    return false;
+
+  const wtf_size_t num_filters = object->size();
+  if (num_filters > kMaxAttributionFiltersPerSource)
+    return false;
+
+  for (wtf_size_t i = 0; i < num_filters; ++i) {
+    JSONObject::Entry entry = object->at(i);
+
+    if (entry.first.CharactersSizeInBytes() >
+        kMaxBytesPerAttributionFilterString) {
+      return false;
+    }
+
+    JSONArray* array = JSONArray::Cast(entry.second);
+    if (!array)
+      return false;
+
+    const wtf_size_t num_values = array->size();
+    if (num_values > kMaxValuesPerAttributionFilter)
+      return false;
+
+    WTF::Vector<String> values;
+
+    for (wtf_size_t j = 0; j < num_values; ++j) {
+      String value;
+      if (!array->at(j)->AsString(&value))
+        return false;
+
+      if (value.CharactersSizeInBytes() > kMaxBytesPerAttributionFilterString)
+        return false;
+
+      values.push_back(std::move(value));
+    }
+
+    filter_data.filter_values.insert(entry.first, std::move(values));
+  }
+
+  return true;
+}
+
+}  // namespace
+
 AttributionSrcLoader::AttributionSrcLoader(LocalFrame* frame)
     : local_frame_(frame) {}
 
@@ -155,6 +210,7 @@
 
   mojom::blink::AttributionSourceDataPtr source_data =
       mojom::blink::AttributionSourceData::New();
+  source_data->filter_data = mojom::blink::AttributionFilterData::New();
 
   // Verify the current url is trustworthy and capable of registering sources.
   scoped_refptr<const SecurityOrigin> reporting_origin =
@@ -165,6 +221,7 @@
       SecurityOrigin::Create(response.CurrentRequestUrl());
 
   // Populate attribution data from provided JSON.
+  // TODO(apaseltiner): Consider applying a max stack depth to this.
   std::unique_ptr<JSONValue> json = ParseJSON(response.HttpHeaderField(
       http_names::kAttributionReportingRegisterSource));
 
@@ -224,6 +281,11 @@
     }
   }
 
+  if (!ParseAttributionFilterData(object->Get("filter_data"),
+                                  *source_data->filter_data)) {
+    return;
+  }
+
   it->value->SourceDataAvailable(std::move(source_data));
 }
 
diff --git a/third_party/blink/renderer/core/html/forms/text_control_element_test.cc b/third_party/blink/renderer/core/html/forms/text_control_element_test.cc
index 8083f435..524d33a 100644
--- a/third_party/blink/renderer/core/html/forms/text_control_element_test.cc
+++ b/third_party/blink/renderer/core/html/forms/text_control_element_test.cc
@@ -93,12 +93,12 @@
   Input().setAttribute(html_names::kReadonlyAttr, "");
   UpdateAllLifecyclePhases();
   EXPECT_EQ(EUserModify::kReadOnly,
-            Input().InnerEditorElement()->GetComputedStyle()->UserModify());
+            Input().InnerEditorElement()->GetComputedStyle()->UsedUserModify());
 
   Input().removeAttribute(html_names::kReadonlyAttr);
   UpdateAllLifecyclePhases();
   EXPECT_EQ(EUserModify::kReadWritePlaintextOnly,
-            Input().InnerEditorElement()->GetComputedStyle()->UserModify());
+            Input().InnerEditorElement()->GetComputedStyle()->UsedUserModify());
 }
 
 TEST_F(TextControlElementTest, DisabledAttributeChangeEditability) {
@@ -106,12 +106,12 @@
   Input().setAttribute(html_names::kDisabledAttr, "");
   UpdateAllLifecyclePhases();
   EXPECT_EQ(EUserModify::kReadOnly,
-            Input().InnerEditorElement()->GetComputedStyle()->UserModify());
+            Input().InnerEditorElement()->GetComputedStyle()->UsedUserModify());
 
   Input().removeAttribute(html_names::kDisabledAttr);
   UpdateAllLifecyclePhases();
   EXPECT_EQ(EUserModify::kReadWritePlaintextOnly,
-            Input().InnerEditorElement()->GetComputedStyle()->UserModify());
+            Input().InnerEditorElement()->GetComputedStyle()->UsedUserModify());
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/html/parser/text_document_parser.cc b/third_party/blink/renderer/core/html/parser/text_document_parser.cc
index 9b87078..1cfe08f 100644
--- a/third_party/blink/renderer/core/html/parser/text_document_parser.cc
+++ b/third_party/blink/renderer/core/html/parser/text_document_parser.cc
@@ -49,10 +49,21 @@
 void TextDocumentParser::InsertFakePreElement() {
   // In principle, we should create a specialized tree builder for
   // TextDocuments, but instead we re-use the existing HTMLTreeBuilder. We
-  // create a fake token and give it to the tree builder rather than sending
-  // fake bytes through the front-end of the parser to avoid distrubing the
-  // line/column number calculations.
+  // create two fake tokens and pass them to the tree builder rather than
+  // sending fake bytes through the front-end of the parser to avoid disturbing
+  // the line/column number calculations.
   Vector<Attribute> attributes;
+
+  // Allow the browser to display the text file in dark mode if it is set as
+  // the preferred color scheme.
+  attributes.push_back(Attribute(html_names::kNameAttr, "color-scheme"));
+  attributes.push_back(Attribute(html_names::kContentAttr, "light dark"));
+  AtomicHTMLToken fake_meta(HTMLToken::kStartTag,
+                            html_names::kMetaTag.LocalName(), attributes);
+  TreeBuilder()->ConstructTree(&fake_meta);
+  attributes.clear();
+
+  // Wrap the actual contents of the text file in <pre>.
   attributes.push_back(Attribute(
       html_names::kStyleAttr, "word-wrap: break-word; white-space: pre-wrap;"));
   AtomicHTMLToken fake_pre(HTMLToken::kStartTag,
diff --git a/third_party/blink/renderer/core/input/gesture_manager.cc b/third_party/blink/renderer/core/input/gesture_manager.cc
index 4eb3d2e..495935b 100644
--- a/third_party/blink/renderer/core/input/gesture_manager.cc
+++ b/third_party/blink/renderer/core/input/gesture_manager.cc
@@ -380,7 +380,13 @@
 WebInputEventResult GestureManager::HandleGestureShortPress(
     const GestureEventWithHitTestResults& targeted_event) {
   drag_in_progress_ = false;
-  if (RuntimeEnabledFeatures::TouchDragAndContextMenuEnabled()) {
+  // TODO(crbug.com/1299010): When TouchDragAndContextMenu is enabled, we want
+  // to start drag here at short-press and open context-menu later at
+  // long-press.  However, on Android an ACTION_CANCEL event is fired on
+  // drag-start, and occcasionally that happens before long-press gesture
+  // timeout which causes GestureRecognizer to suppress long-press detection.
+  if (RuntimeEnabledFeatures::TouchDragAndContextMenuEnabled() &&
+      RuntimeEnabledFeatures::TouchDragOnShortPressEnabled()) {
     drag_in_progress_ =
         mouse_event_manager_->HandleDragDropIfPossible(targeted_event);
   }
@@ -405,9 +411,14 @@
 
   gesture_context_menu_deferred_ = false;
 
-  if (!RuntimeEnabledFeatures::TouchDragAndContextMenuEnabled() &&
-      frame_->GetSettings() &&
-      frame_->GetSettings()->GetTouchDragDropEnabled() && frame_->View()) {
+  if (RuntimeEnabledFeatures::TouchDragAndContextMenuEnabled()) {
+    if (!RuntimeEnabledFeatures::TouchDragOnShortPressEnabled()) {
+      drag_in_progress_ =
+          mouse_event_manager_->HandleDragDropIfPossible(targeted_event);
+    }
+  } else if (frame_->GetSettings() &&
+             frame_->GetSettings()->GetTouchDragDropEnabled() &&
+             frame_->View()) {
     bool hit_test_contains_links =
         hit_test_result.URLElement() ||
         !hit_test_result.AbsoluteImageURL().IsNull() ||
diff --git a/third_party/blink/renderer/core/layout/layout_table_test.cc b/third_party/blink/renderer/core/layout/layout_table_test.cc
index d361e89..3371f65 100644
--- a/third_party/blink/renderer/core/layout/layout_table_test.cc
+++ b/third_party/blink/renderer/core/layout/layout_table_test.cc
@@ -66,11 +66,16 @@
 
   auto* table = GetTableByElementId("table");
 
-  // The table's border box rect covers all collapsed borders of the first
-  // row, and bottom collapsed borders of the last row.
   auto expected_border_box_rect = table->PhysicalContentBoxRect();
-  expected_border_box_rect.ExpandEdges(LayoutUnit(2), LayoutUnit(5),
-                                       LayoutUnit(0), LayoutUnit(1));
+  if (RuntimeEnabledFeatures::LayoutNGEnabled()) {
+    expected_border_box_rect.ExpandEdges(LayoutUnit(2), LayoutUnit(10),
+                                         LayoutUnit(0), LayoutUnit(10));
+  } else {
+    // The table's border box rect covers all collapsed borders of the first
+    // row, and bottom collapsed borders of the last row.
+    expected_border_box_rect.ExpandEdges(LayoutUnit(2), LayoutUnit(5),
+                                         LayoutUnit(0), LayoutUnit(1));
+  }
   EXPECT_EQ(expected_border_box_rect, table->PhysicalBorderBoxRect());
 
   // The table's self visual overflow rect covers all collapsed borders, but
@@ -136,10 +141,10 @@
     EXPECT_EQ(LayoutUnit(7.5), table3->BorderBefore());
     // Cell H's border-bottom won.
     EXPECT_EQ(20, table3->BorderAfter());
-    // Cell E's border-left won.
-    EXPECT_EQ(LayoutUnit(10.5), table3->BorderStart());
-    // Cell F's border-bottom won.
-    EXPECT_EQ(LayoutUnit(12.5), table3->BorderEnd());
+    // Cell G's border-left won.
+    EXPECT_EQ(LayoutUnit(15), table3->BorderStart());
+    // Cell H's border-right won.
+    EXPECT_EQ(LayoutUnit(20), table3->BorderEnd());
   } else {
     // Cell E's border-top won.
     EXPECT_EQ(7, table3->BorderBefore());
diff --git a/third_party/blink/renderer/core/layout/ng/flex/ng_flex_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/flex/ng_flex_layout_algorithm.cc
index 4abe813a..8086c62e 100644
--- a/third_party/blink/renderer/core/layout/ng/flex/ng_flex_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/flex/ng_flex_layout_algorithm.cc
@@ -1717,7 +1717,7 @@
   // be before this row, or before an earlier sibling, if there's a more
   // appealing breakpoint there.
   if (!AttemptRowSoftBreak(child, layout_result, appeal_before,
-                           fragmentainer_block_offset))
+                           fragmentainer_block_offset, row.line_cross_size))
     return NGBreakStatus::kNeedsEarlierBreak;
 
   return NGBreakStatus::kBrokeBefore;
@@ -1774,12 +1774,13 @@
     NGLayoutInputNode child,
     const NGLayoutResult& layout_result,
     NGBreakAppeal appeal_before,
-    LayoutUnit fragmentainer_block_offset) {
+    LayoutUnit fragmentainer_block_offset,
+    LayoutUnit row_block_size) {
   if (container_builder_.HasEarlyBreak() &&
       container_builder_.EarlyBreak().BreakAppeal() > appeal_before) {
-    // TODO(almaher): This will be different for rows.
-    PropagateSpaceShortage(ConstraintSpace(), layout_result,
-                           fragmentainer_block_offset, &container_builder_);
+    PropagateSpaceShortage(ConstraintSpace(), /* layout_result */ nullptr,
+                           fragmentainer_block_offset, &container_builder_,
+                           row_block_size);
     return false;
   }
 
diff --git a/third_party/blink/renderer/core/layout/ng/flex/ng_flex_layout_algorithm.h b/third_party/blink/renderer/core/layout/ng/flex/ng_flex_layout_algorithm.h
index 5e55c1a..301c272 100644
--- a/third_party/blink/renderer/core/layout/ng/flex/ng_flex_layout_algorithm.h
+++ b/third_party/blink/renderer/core/layout/ng/flex/ng_flex_layout_algorithm.h
@@ -139,13 +139,14 @@
   // If false is returned, it means that the desired breakpoint is earlier in
   // the container, and that we need to abort and re-layout to that breakpoint.
   // |child| and |layout_result| should be those associated with the first child
-  // in the row. |appeal_before| and |fragmentainer_block_offset| are specific
-  // to the row itself. See |::blink::AttemptSoftBreak()| for more
-  // documentation.
+  // in the row. |appeal_before|, |fragmentainer_block_offset| and
+  // |row_block_size| are specific to the row itself. See
+  // |::blink::AttemptSoftBreak()| for more documentation.
   bool AttemptRowSoftBreak(NGLayoutInputNode child,
                            const NGLayoutResult& layout_result,
                            NGBreakAppeal appeal_before,
-                           LayoutUnit fragmentainer_block_offset);
+                           LayoutUnit fragmentainer_block_offset,
+                           LayoutUnit row_block_size);
 
 #if DCHECK_IS_ON()
   void CheckFlexLines(const Vector<NGFlexLine>& flex_line_outputs) const;
diff --git a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm.cc
index b429ffc8..b94daa3 100644
--- a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm.cc
@@ -3308,7 +3308,7 @@
 
           // We are choosing to add an early breakpoint at a row. Propagate our
           // space shortage to the column balancer.
-          PropagateSpaceShortage(ConstraintSpace(), *result,
+          PropagateSpaceShortage(ConstraintSpace(), result,
                                  fragment_relative_block_offset,
                                  &container_builder_);
 
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items_builder_test.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items_builder_test.cc
index 6f59019..142a84e2 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items_builder_test.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items_builder_test.cc
@@ -40,7 +40,8 @@
       cursor.Current()->LineBoxFragment();
 
   NGInlineNode inline_node(container);
-  NGLogicalLineItems line_items_pool;
+  NGLogicalLineItems* line_items_pool =
+      MakeGarbageCollected<NGLogicalLineItems>();
   {
     // First test emulates what |NGBlockLayoutAlgorithm| does, which loops
     // following calls for each line:
@@ -49,7 +50,7 @@
     // 3. |AddLine|.
     NGFragmentItemsBuilder items_builder(
         inline_node, {WritingMode::kHorizontalTb, TextDirection::kLtr});
-    items_builder.AddLogicalLineItemsPool(&line_items_pool);
+    items_builder.AddLogicalLineItemsPool(line_items_pool);
     NGLogicalLineItems* line_items1 = items_builder.AcquireLogicalLineItems();
     items_builder.AssociateLogicalLineItems(line_items1, *line_fragment1);
     items_builder.AddLine(*line_fragment1, LogicalOffset());
@@ -58,7 +59,7 @@
     items_builder.AddLine(*line_fragment2, LogicalOffset());
 
     // In this case, we should reuse one |NGLogicalLineItems| instance.
-    EXPECT_EQ(line_items1, &line_items_pool);
+    EXPECT_EQ(line_items1, line_items_pool);
     EXPECT_EQ(line_items1, line_items2);
 
     const auto& items = items_builder.Items(PhysicalSize());
@@ -72,7 +73,7 @@
     // box.
     NGFragmentItemsBuilder items_builder(
         inline_node, {WritingMode::kHorizontalTb, TextDirection::kLtr});
-    items_builder.AddLogicalLineItemsPool(&line_items_pool);
+    items_builder.AddLogicalLineItemsPool(line_items_pool);
     NGLogicalLineItems* line_items1 = items_builder.AcquireLogicalLineItems();
     items_builder.AssociateLogicalLineItems(line_items1, *line_fragment1);
     NGLogicalLineItems* line_items2 = items_builder.AcquireLogicalLineItems();
@@ -80,7 +81,7 @@
 
     // Because |AcquireLogicalLineItems| without |AddLine|, new instances should
     // be allocated for line 2.
-    EXPECT_EQ(line_items1, &line_items_pool);
+    EXPECT_EQ(line_items1, line_items_pool);
     EXPECT_NE(line_items1, line_items2);
 
     items_builder.AddLine(*line_fragment1, LogicalOffset());
@@ -95,7 +96,7 @@
     // to the container box in the reverse order.
     NGFragmentItemsBuilder items_builder(
         inline_node, {WritingMode::kHorizontalTb, TextDirection::kLtr});
-    items_builder.AddLogicalLineItemsPool(&line_items_pool);
+    items_builder.AddLogicalLineItemsPool(line_items_pool);
     NGLogicalLineItems* line_items1 = items_builder.AcquireLogicalLineItems();
     items_builder.AssociateLogicalLineItems(line_items1, *line_fragment1);
     NGLogicalLineItems* line_items2 = items_builder.AcquireLogicalLineItems();
@@ -103,7 +104,7 @@
 
     // Because |AcquireLogicalLineItems| without |AddLine|, new instances should
     // be allocated for line 2.
-    EXPECT_EQ(line_items1, &line_items_pool);
+    EXPECT_EQ(line_items1, line_items_pool);
     EXPECT_NE(line_items1, line_items2);
 
     // Add lines in the reverse order.
@@ -118,7 +119,7 @@
     // Custom layout may not add all line boxes.
     NGFragmentItemsBuilder items_builder(
         inline_node, {WritingMode::kHorizontalTb, TextDirection::kLtr});
-    items_builder.AddLogicalLineItemsPool(&line_items_pool);
+    items_builder.AddLogicalLineItemsPool(line_items_pool);
     NGLogicalLineItems* line_items1 = items_builder.AcquireLogicalLineItems();
     items_builder.AssociateLogicalLineItems(line_items1, *line_fragment1);
     NGLogicalLineItems* line_items2 = items_builder.AcquireLogicalLineItems();
@@ -126,7 +127,7 @@
 
     // Because |AcquireLogicalLineItems| without |AddLine|, new instances should
     // be allocated for line 2.
-    EXPECT_EQ(line_items1, &line_items_pool);
+    EXPECT_EQ(line_items1, line_items_pool);
     EXPECT_NE(line_items1, line_items2);
 
     // Add line2, but not line1.
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_logical_line_item.h b/third_party/blink/renderer/core/layout/ng/inline/ng_logical_line_item.h
index 8ede48c2..1f7d9547 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_logical_line_item.h
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_logical_line_item.h
@@ -252,9 +252,10 @@
                                      const NGLogicalLineItem& item);
 
 // A vector of Child.
-// Unlike the fragment builder, chlidren are mutable.
+// Unlike the fragment builder, children are mutable.
 // Callers can add to the fragment builder in a batch once finalized.
-class NGLogicalLineItems : public GarbageCollected<NGLogicalLineItems> {
+class CORE_EXPORT NGLogicalLineItems
+    : public GarbageCollected<NGLogicalLineItems> {
  public:
   NGLogicalLineItems() = default;
   ~NGLogicalLineItems() { DCHECK(IsEmpty()); }
diff --git a/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.cc
index 85e1d66..876daa40 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.cc
@@ -2451,7 +2451,7 @@
       // require an additional piece of machinery. This case should be rare
       // enough (to worry about performance), so let's focus on code
       // simplicity instead.
-      PropagateSpaceShortage(ConstraintSpace(), layout_result,
+      PropagateSpaceShortage(ConstraintSpace(), &layout_result,
                              fragmentainer_block_offset, &container_builder_);
     }
     // Attempt to honor orphans and widows requests.
diff --git a/third_party/blink/renderer/core/layout/ng/ng_fragmentation_utils.cc b/third_party/blink/renderer/core/layout/ng/ng_fragmentation_utils.cc
index 32153c27d..fac18fd 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_fragmentation_utils.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_fragmentation_utils.cc
@@ -643,7 +643,7 @@
   // (only blocks), because line boxes need handle it in their own way (due to
   // how we implement widows).
   if (child.IsBlock() && space.HasKnownFragmentainerBlockSize()) {
-    PropagateSpaceShortage(space, layout_result, fragmentainer_block_offset,
+    PropagateSpaceShortage(space, &layout_result, fragmentainer_block_offset,
                            builder);
   }
 
@@ -660,33 +660,38 @@
 }
 
 void PropagateSpaceShortage(const NGConstraintSpace& space,
-                            const NGLayoutResult& layout_result,
+                            const NGLayoutResult* layout_result,
                             LayoutUnit fragmentainer_block_offset,
-                            NGBoxFragmentBuilder* builder) {
+                            NGBoxFragmentBuilder* builder,
+                            absl::optional<LayoutUnit> block_size_override) {
   // Space shortage is only reported for soft breaks, and they can only exist if
   // we know the fragmentainer block-size.
   DCHECK(space.HasKnownFragmentainerBlockSize());
+  DCHECK(layout_result || block_size_override);
 
   // Only multicol cares about space shortage.
   if (space.BlockFragmentationType() != kFragmentColumn)
     return;
 
   LayoutUnit space_shortage;
-  if (layout_result.MinimalSpaceShortage() == LayoutUnit::Max()) {
+  if (block_size_override) {
+    space_shortage = fragmentainer_block_offset + block_size_override.value() -
+                     space.FragmentainerBlockSize();
+  } else if (layout_result->MinimalSpaceShortage() == LayoutUnit::Max()) {
     // Calculate space shortage: Figure out how much more space would have been
     // sufficient to make the child fragment fit right here in the current
     // fragmentainer. If layout aborted, though, we can't propagate anything.
-    if (layout_result.Status() != NGLayoutResult::kSuccess)
+    if (layout_result->Status() != NGLayoutResult::kSuccess)
       return;
     NGFragment fragment(space.GetWritingDirection(),
-                        layout_result.PhysicalFragment());
+                        layout_result->PhysicalFragment());
     space_shortage = fragmentainer_block_offset + fragment.BlockSize() -
                      space.FragmentainerBlockSize();
   } else {
     // However, if space shortage was reported inside the child, use that. If we
     // broke inside the child, we didn't complete layout, so calculating space
     // shortage for the child as a whole would be impossible and pointless.
-    space_shortage = layout_result.MinimalSpaceShortage();
+    space_shortage = layout_result->MinimalSpaceShortage();
   }
 
   // TODO(mstensho): Turn this into a DCHECK, when the engine is ready for
@@ -864,7 +869,7 @@
       builder->EarlyBreak().BreakAppeal() > appeal_before) {
     // Found a better place to break. Before aborting, calculate and report
     // space shortage from where we'd actually break.
-    PropagateSpaceShortage(space, layout_result, fragmentainer_block_offset,
+    PropagateSpaceShortage(space, &layout_result, fragmentainer_block_offset,
                            builder);
     return false;
   }
diff --git a/third_party/blink/renderer/core/layout/ng/ng_fragmentation_utils.h b/third_party/blink/renderer/core/layout/ng/ng_fragmentation_utils.h
index 10e27d23..0e280ff 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_fragmentation_utils.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_fragmentation_utils.h
@@ -270,11 +270,15 @@
 // Propagate space shortage to the builder and beyond, if appropriate. This is
 // something we do during column balancing, when we already have a tentative
 // column block-size, as a means to calculate by how much we need to stretch the
-// columns to make everything fit.
-void PropagateSpaceShortage(const NGConstraintSpace&,
-                            const NGLayoutResult&,
-                            LayoutUnit fragmentainer_block_offset,
-                            NGBoxFragmentBuilder*);
+// columns to make everything fit. |block_size_override| should only be supplied
+// when you wish to propagate a different block-size than that of the provided
+// layout result.
+void PropagateSpaceShortage(
+    const NGConstraintSpace&,
+    const NGLayoutResult*,
+    LayoutUnit fragmentainer_block_offset,
+    NGBoxFragmentBuilder*,
+    absl::optional<LayoutUnit> block_size_override = absl::nullopt);
 
 // Move past the breakpoint before the child, if possible, and return true. Also
 // update the appeal of breaking before or inside the child (if we're not going
diff --git a/third_party/blink/renderer/core/layout/ng/table/ng_table_borders.cc b/third_party/blink/renderer/core/layout/ng/table/ng_table_borders.cc
index 3eb4f88..53fdd301 100644
--- a/third_party/blink/renderer/core/layout/ng/table/ng_table_borders.cc
+++ b/third_party/blink/renderer/core/layout/ng/table/ng_table_borders.cc
@@ -410,14 +410,6 @@
       GetCellBorders(0, 0, table_row_count, table_column_count);
   collapsed_visual_inline_start_ = borders.inline_start;
   collapsed_visual_inline_end_ = borders.inline_end;
-  wtf_size_t inline_start_edge = 0;
-  wtf_size_t inline_end_edge = 2 * table_column_count;
-  borders.inline_start = CanPaint(inline_start_edge)
-                             ? BorderWidth(inline_start_edge) / 2
-                             : LayoutUnit();
-  borders.inline_end = CanPaint(inline_end_edge)
-                           ? BorderWidth(inline_end_edge) / 2
-                           : LayoutUnit();
   cached_table_border_ = borders;
 }
 
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_inline_text.cc b/third_party/blink/renderer/core/layout/svg/layout_svg_inline_text.cc
index a778df7..114b440 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_inline_text.cc
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_inline_text.cc
@@ -70,7 +70,7 @@
   LayoutSVGText::NotifySubtreeStructureChanged(
       this, layout_invalidation_reason::kTextChanged);
 
-  if (StyleRef().UserModify() != EUserModify::kReadOnly)
+  if (StyleRef().UsedUserModify() != EUserModify::kReadOnly)
     UseCounter::Count(GetDocument(), WebFeature::kSVGTextEdited);
 }
 
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_text.cc b/third_party/blink/renderer/core/layout/svg/layout_svg_text.cc
index 118be196..0e72c15 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_text.cc
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_text.cc
@@ -144,7 +144,7 @@
   // TODO(fs): Restore the passing of |reason| here.
   LayoutSVGResourceContainer::MarkForLayoutAndParentResourceInvalidation(*this);
 
-  if (StyleRef().UserModify() != EUserModify::kReadOnly)
+  if (StyleRef().UsedUserModify() != EUserModify::kReadOnly)
     UseCounter::Count(GetDocument(), WebFeature::kSVGTextEdited);
 }
 
diff --git a/third_party/blink/renderer/core/layout/text_autosizer.cc b/third_party/blink/renderer/core/layout/text_autosizer.cc
index 32a70ff..930368c4 100644
--- a/third_party/blink/renderer/core/layout/text_autosizer.cc
+++ b/third_party/blink/renderer/core/layout/text_autosizer.cc
@@ -136,7 +136,7 @@
                                   layout_object->IsHorizontalWritingMode()) ||
          layout_object->StyleRef().IsDisplayReplacedType() ||
          layout_object->IsTextAreaIncludingNG() ||
-         layout_object->StyleRef().UserModify() != EUserModify::kReadOnly;
+         layout_object->StyleRef().UsedUserModify() != EUserModify::kReadOnly;
 }
 
 static bool BlockIsRowOfLinks(const LayoutBlock* block) {
@@ -781,7 +781,7 @@
   // of text content.
   if (root->IsTextAreaIncludingNG() ||
       (root->Style() &&
-       root->StyleRef().UserModify() != EUserModify::kReadOnly)) {
+       root->StyleRef().UsedUserModify() != EUserModify::kReadOnly)) {
     cluster->has_enough_text_to_autosize_ = kHasEnoughText;
     return true;
   }
diff --git a/third_party/blink/renderer/core/style/computed_style.h b/third_party/blink/renderer/core/style/computed_style.h
index 40a87e6..44a8747 100644
--- a/third_party/blink/renderer/core/style/computed_style.h
+++ b/third_party/blink/renderer/core/style/computed_style.h
@@ -147,6 +147,7 @@
 class WebkitTapHighlightColor;
 class WebkitTextFillColor;
 class WebkitTextStrokeColor;
+class WebkitUserModify;
 
 }  // namespace css_longhand
 
@@ -262,6 +263,7 @@
   friend class css_longhand::WebkitTapHighlightColor;
   friend class css_longhand::WebkitTextFillColor;
   friend class css_longhand::WebkitTextStrokeColor;
+  friend class css_longhand::WebkitUserModify;
   // Access to private Appearance() and HasAppearance().
   friend class LayoutTheme;
   friend class StyleAdjuster;
@@ -273,7 +275,7 @@
   friend class CachedUAStyle;
   // Accesses visited and unvisited colors.
   friend class ColorPropertyFunctions;
-  // Edits the background for media controls.
+  // Edits the background for media controls and accesses UserModify().
   friend class StyleAdjuster;
   // Access to private SetFontInternal().
   friend class FontBuilder;
@@ -282,6 +284,8 @@
   friend class FilterOperationResolver;
   // Access to SetInitialData() and GetCurrentColor().
   friend class StyleResolver;
+  // Access to UserModify().
+  friend class MatchedPropertiesCache;
 
  protected:
   mutable std::unique_ptr<StyleCachedData> cached_data_;
@@ -2199,6 +2203,13 @@
     return PointerEventsInternal();
   }
 
+  // User modify utility functions.
+  EUserModify UsedUserModify() const {
+    if (IsInert())
+      return EUserModify::kReadOnly;
+    return UserModifyInternal();
+  }
+
   // User select utility functions.
   EUserSelect UsedUserSelect() const {
     if (IsInert())
@@ -2208,7 +2219,7 @@
 
   bool IsSelectable() const {
     return !IsInert() && !(UserSelectInternal() == EUserSelect::kNone &&
-                           UserModify() == EUserModify::kReadOnly);
+                           UserModifyInternal() == EUserModify::kReadOnly);
   }
 
   bool IsFocusable() const {
@@ -2747,6 +2758,7 @@
   EFloat Floating() const { return FloatingInternal(); }
   EPointerEvents PointerEvents() const { return PointerEventsInternal(); }
   EResize Resize() const { return ResizeInternal(); }
+  EUserModify UserModify() const { return UserModifyInternal(); }
   EUserSelect UserSelect() const { return UserSelectInternal(); }
 
   bool IsInlineSizeContainer() const {
diff --git a/third_party/blink/renderer/modules/webcodecs/codec_pressure_manager_test.cc b/third_party/blink/renderer/modules/webcodecs/codec_pressure_manager_test.cc
index 9e018f67..eef7e25 100644
--- a/third_party/blink/renderer/modules/webcodecs/codec_pressure_manager_test.cc
+++ b/third_party/blink/renderer/modules/webcodecs/codec_pressure_manager_test.cc
@@ -13,6 +13,7 @@
 #include "third_party/blink/renderer/modules/webcodecs/codec_pressure_manager_provider.h"
 #include "third_party/blink/renderer/modules/webcodecs/reclaimable_codec.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
+#include "third_party/blink/renderer/platform/heap/heap_test_utilities.h"
 #include "third_party/blink/renderer/platform/heap/thread_state.h"
 
 namespace blink {
@@ -96,6 +97,7 @@
     base::RunLoop run_loop;
     base::SequencedTaskRunnerHandle::Get()->PostTask(FROM_HERE,
                                                      run_loop.QuitClosure());
+    HeapPointersOnStackScope pointers_on_stack(ThreadState::Current());
     run_loop.Run();
   }
 
diff --git a/third_party/blink/renderer/platform/media/resource_multi_buffer_data_provider.cc b/third_party/blink/renderer/platform/media/resource_multi_buffer_data_provider.cc
index f6723f0..7f71935b 100644
--- a/third_party/blink/renderer/platform/media/resource_multi_buffer_data_provider.cc
+++ b/third_party/blink/renderer/platform/media/resource_multi_buffer_data_provider.cc
@@ -8,7 +8,6 @@
 #include <utility>
 
 #include "base/bind.h"
-#include "base/bits.h"
 #include "base/callback_helpers.h"
 #include "base/location.h"
 #include "base/metrics/histogram_macros.h"
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index 10035d2..4defe52 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -2273,6 +2273,13 @@
     // only if a drag cannot be started).
     {
       name: "TouchDragAndContextMenu",
+      implied_by: ["TouchDragOnShortPress"],
+    },
+    // This feature makes touch dragging to occur at the short-press gesture,
+    // which occurs right before the long-press gesture.  This feature assumes
+    // that TouchDragAndContextMenu is enabled.
+    {
+      name: "TouchDragOnShortPress",
     },
     // Many websites disable mouse support when touch APIs are available.  We'd
     // like to enable this always but can't until more websites fix this bug.
diff --git a/third_party/blink/renderer/platform/wtf/hash_table.h b/third_party/blink/renderer/platform/wtf/hash_table.h
index d587f3aa..45e5829 100644
--- a/third_party/blink/renderer/platform/wtf/hash_table.h
+++ b/third_party/blink/renderer/platform/wtf/hash_table.h
@@ -25,7 +25,6 @@
 
 #include <memory>
 
-#include "base/bits.h"
 #include "base/dcheck_is_on.h"
 #include "base/numerics/checked_math.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 0f04850..4be2387 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -1198,6 +1198,7 @@
 crbug.com/1179497 external/wpt/css/css-tables/col-definite-min-size-001.html [ Failure ]
 crbug.com/1179497 external/wpt/css/css-tables/col-definite-size-001.html [ Failure ]
 crbug.com/910725 external/wpt/css/css-tables/tentative/paint/overflow-hidden-table.html [ Failure ]
+crbug.com/1238243 external/wpt/css/css-tables/collapsed-scroll-overflow.html [ Failure ]
 
 # [css-contain]
 
@@ -1630,6 +1631,7 @@
 virtual/layout_ng_flex_frag/external/wpt/css/css-break/flexbox/multi-line-row-flex-fragmentation-033.html [ Pass ]
 virtual/layout_ng_flex_frag/external/wpt/css/css-break/flexbox/multi-line-row-flex-fragmentation-034.html [ Pass ]
 virtual/layout_ng_flex_frag/external/wpt/css/css-break/flexbox/multi-line-row-flex-fragmentation-035.html [ Pass ]
+virtual/layout_ng_flex_frag/external/wpt/css/css-break/flexbox/multi-line-row-flex-fragmentation-037.html [ Pass ]
 virtual/layout_ng_flex_frag/external/wpt/css/css-break/flexbox/single-line-column-flex-fragmentation-012.html [ Pass ]
 virtual/layout_ng_flex_frag/external/wpt/css/css-break/flexbox/single-line-column-flex-fragmentation-014.html [ Pass ]
 virtual/layout_ng_flex_frag/external/wpt/css/css-break/flexbox/single-line-column-flex-fragmentation-015.html [ Pass ]
@@ -3626,6 +3628,7 @@
 crbug.com/626703 [ Mac10.15 ] external/wpt/mediacapture-insertable-streams/MediaStreamTrackGenerator-audio.https.html [ Crash ]
 crbug.com/626703 external/wpt/webrtc-extensions/transfer-datachannel-service-worker.https.html [ Timeout ]
 crbug.com/626703 external/wpt/webrtc-extensions/transfer-datachannel.html [ Timeout ]
+crbug.com/626703 [ Mac ] virtual/no-auto-wpt-origin-isolation/external/wpt/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-yes-child-no-port.sub.https.html [ Failure Timeout ]
 crbug.com/626703 [ Win ] external/wpt/websockets/Create-blocked-port.any.worker.html?wpt_flags=h2 [ Failure Timeout ]
 crbug.com/626703 external/wpt/FileAPI/file/send-file-formdata-controls.any.html [ Pass Timeout ]
 crbug.com/626703 external/wpt/FileAPI/file/send-file-formdata-controls.any.worker.html [ Pass Timeout ]
@@ -4302,6 +4305,7 @@
 crbug.com/660611 external/wpt/css/css-break/flexbox/multi-line-row-flex-fragmentation-033.html [ Failure ]
 crbug.com/660611 external/wpt/css/css-break/flexbox/multi-line-row-flex-fragmentation-034.html [ Failure ]
 crbug.com/660611 external/wpt/css/css-break/flexbox/multi-line-row-flex-fragmentation-035.html [ Failure ]
+crbug.com/660611 external/wpt/css/css-break/flexbox/multi-line-row-flex-fragmentation-037.html [ Failure ]
 crbug.com/660611 external/wpt/css/css-break/flexbox/single-line-column-flex-fragmentation-012.html [ Failure ]
 crbug.com/660611 external/wpt/css/css-break/flexbox/single-line-column-flex-fragmentation-014.html [ Failure ]
 crbug.com/660611 external/wpt/css/css-break/flexbox/single-line-column-flex-fragmentation-015.html [ Failure ]
@@ -7056,12 +7060,6 @@
 crbug.com/1249176 [ Mac11-arm64 ] virtual/background-svg-in-lcp/external/wpt/largest-contentful-paint/multiple-redirects-TAO.html [ Failure Pass ]
 crbug.com/1249176 [ Mac11-arm64 ] virtual/webrtc-wpt-plan-b/external/wpt/webrtc/protocol/split.https.html [ Pass Timeout ]
 
-# origin-keyed-agent-clusters tests are flaky on all platforms.
-# These tests are skipped everywhere except under the not-site-per-process and
-# no-auto-wpt-origin-isolation virtual test suites (see: NeverFixTests).
-crbug.com/1227911 virtual/not-site-per-process/external/wpt/html/browsers/origin/origin-keyed-agent-clusters/* [ Failure Pass ]
-crbug.com/1227911 virtual/no-auto-wpt-origin-isolation/external/wpt/html/browsers/origin/origin-keyed-agent-clusters/* [ Failure Pass ]
-
 # mac-arm CI 10-01-2021
 crbug.com/1249176 [ Mac11-arm64 ] editing/text-iterator/beforematch-async.html [ Failure ]
 crbug.com/1249176 [ Mac11-arm64 ] external/wpt/event-timing/click-interactionid.html [ Timeout ]
diff --git a/third_party/blink/web_tests/VirtualTestSuites b/third_party/blink/web_tests/VirtualTestSuites
index 8b6b478..8e352e5 100644
--- a/third_party/blink/web_tests/VirtualTestSuites
+++ b/third_party/blink/web_tests/VirtualTestSuites
@@ -518,7 +518,8 @@
       "external/wpt/html/browsers/origin/origin-keyed-agent-clusters",
       "external/wpt/app-history/navigate-event/cross-window"
     ],
-    "args": ["--disable-auto-wpt-origin-isolation"]
+    "args": ["--disable-auto-wpt-origin-isolation",
+             "--reset-browsing-instance-between-tests"]
   },
   {
     "prefix": "focusless-spat-nav",
@@ -578,6 +579,7 @@
               "fast/forms/color-scheme",
               "fast/forms/validation-bubble-appearance-edge.html",
               "fast/forms/validation-bubble-appearance-wrap.html",
+              "fast/loader/plain-text-document-appearance.html",
               "http/tests/eye-dropper"],
     "args": ["--blink-settings=preferredColorScheme=0",
              "--enable-features=EyeDropper"]
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-row-flex-fragmentation-037.html b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-row-flex-fragmentation-037.html
new file mode 100644
index 0000000..62bf337
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-row-flex-fragmentation-037.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<title>
+  Multi-line row flex fragmentation: break-before:avoid and column balancing.
+</title>
+<link rel="help" href="https://drafts.csswg.org/css-flexbox-1/#pagination">
+<link rel="match" href="../../reference/ref-filled-green-100px-square.xht">
+<style>
+  #flex {
+    display: flex;
+    flex-wrap: wrap;
+  }
+  #flex > div {
+    width: 50px;
+  }
+</style>
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div style="width:100px; height:100px; background:red;">
+  <div style="width: 100px; columns: 2; column-gap: 0;position: relative; background: green;">
+    <div id="flex">
+      <div style="height: 25px;"></div>
+      <div style="height: 50px;"></div>
+      <div style="height: 5px; width: 25px;"></div>
+      <div style="height: 25px; width: 25px; break-before: avoid;"></div>
+      <div style="height: 50px;"></div>
+      </div>
+    </div>
+  </div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/inert/inert-and-contenteditable.tentative.html b/third_party/blink/web_tests/external/wpt/inert/inert-and-contenteditable.tentative.html
new file mode 100644
index 0000000..01091d1
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/inert/inert-and-contenteditable.tentative.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Inert and contenteditable</title>
+<link rel="author" title="Oriol Brufau" href="mailto:obrufau@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/interaction.html#inert">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/interaction.html#attr-contenteditable">
+<meta assert="assert" content="
+    Executing an editing command in a node marked as both inert and editable
+    should have the same result regardless of which ancestors trigger each effect.
+    Only testing for consistency, the exact result is not interoperable.">
+
+<div id="log"></div>
+
+<div class="wrapper" contenteditable inert>
+  {<p class="target">target</p>}
+</div>
+
+<div class="wrapper" contenteditable>
+  {<p class="target" inert>target</p>}
+</div>
+
+<div class="wrapper" inert>
+  {<p class="target" contenteditable>target</p>}
+</div>
+
+<div class="wrapper">
+  {<p class="target" contenteditable inert>target</p>}
+</div>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+const results = [];
+const textContents = [];
+setup(function() {
+  const selection = getSelection();
+  for (let wrapper of document.querySelectorAll(".wrapper")) {
+    const target = wrapper.querySelector(".target");
+    selection.collapse(target.firstChild, 3);
+    results.push(document.execCommand("delete"));
+    textContents.push(wrapper.textContent.trim());
+  }
+});
+function testSameValues(array, description) {
+  test(function() {
+    assert_greater_than(array.length, 0, "Array shouldn't be empty");
+    for (let i = 1; i < array.length; ++i) {
+      assert_equals(array[i], array[0], `${JSON.stringify(array)} at index ${i}`);
+    }
+  }, description);
+}
+testSameValues(results, "execCommand should return the same value");
+testSameValues(textContents, "The resulting textContent should be the same value");
+</script>
diff --git a/third_party/blink/web_tests/platform/win/fast/css/caption-width-absolute-position-expected.png b/third_party/blink/web_tests/fast/css/caption-width-absolute-position-expected.png
similarity index 78%
copy from third_party/blink/web_tests/platform/win/fast/css/caption-width-absolute-position-expected.png
copy to third_party/blink/web_tests/fast/css/caption-width-absolute-position-expected.png
index 0f960a1c..9de0173 100644
--- a/third_party/blink/web_tests/platform/win/fast/css/caption-width-absolute-position-expected.png
+++ b/third_party/blink/web_tests/fast/css/caption-width-absolute-position-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/css/caption-width-absolute-position-expected.png b/third_party/blink/web_tests/fast/css/caption-width-absolute-position-offset-top-expected.png
similarity index 78%
rename from third_party/blink/web_tests/platform/win/fast/css/caption-width-absolute-position-expected.png
rename to third_party/blink/web_tests/fast/css/caption-width-absolute-position-offset-top-expected.png
index 0f960a1c..9d745bbc 100644
--- a/third_party/blink/web_tests/platform/win/fast/css/caption-width-absolute-position-expected.png
+++ b/third_party/blink/web_tests/fast/css/caption-width-absolute-position-offset-top-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/css/caption-width-absolute-position-expected.png b/third_party/blink/web_tests/fast/css/caption-width-fixed-position-expected.png
similarity index 78%
copy from third_party/blink/web_tests/platform/win/fast/css/caption-width-absolute-position-expected.png
copy to third_party/blink/web_tests/fast/css/caption-width-fixed-position-expected.png
index 0f960a1c..9de0173 100644
--- a/third_party/blink/web_tests/platform/win/fast/css/caption-width-absolute-position-expected.png
+++ b/third_party/blink/web_tests/fast/css/caption-width-fixed-position-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/css/caption-width-absolute-position-expected.png b/third_party/blink/web_tests/fast/css/caption-width-fixed-position-offset-top-expected.png
similarity index 78%
copy from third_party/blink/web_tests/platform/win/fast/css/caption-width-absolute-position-expected.png
copy to third_party/blink/web_tests/fast/css/caption-width-fixed-position-offset-top-expected.png
index 0f960a1c..9d745bbc 100644
--- a/third_party/blink/web_tests/platform/win/fast/css/caption-width-absolute-position-expected.png
+++ b/third_party/blink/web_tests/fast/css/caption-width-fixed-position-offset-top-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/fast/loader/plain-text-document-appearance.html b/third_party/blink/web_tests/fast/loader/plain-text-document-appearance.html
new file mode 100644
index 0000000..ac5b251
--- /dev/null
+++ b/third_party/blink/web_tests/fast/loader/plain-text-document-appearance.html
@@ -0,0 +1 @@
+<iframe src="resources/plain-text-document.txt">
diff --git a/third_party/blink/web_tests/flag-specific/disable-layout-ng/fast/css/caption-width-absolute-position-expected.png b/third_party/blink/web_tests/flag-specific/disable-layout-ng/fast/css/caption-width-absolute-position-expected.png
new file mode 100644
index 0000000..3f6af4a
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/disable-layout-ng/fast/css/caption-width-absolute-position-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/disable-layout-ng/fast/css/caption-width-absolute-position-offset-top-expected.png b/third_party/blink/web_tests/flag-specific/disable-layout-ng/fast/css/caption-width-absolute-position-offset-top-expected.png
new file mode 100644
index 0000000..46577274
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/disable-layout-ng/fast/css/caption-width-absolute-position-offset-top-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/disable-layout-ng/fast/css/caption-width-fixed-position-expected.png b/third_party/blink/web_tests/flag-specific/disable-layout-ng/fast/css/caption-width-fixed-position-expected.png
new file mode 100644
index 0000000..3f6af4a
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/disable-layout-ng/fast/css/caption-width-fixed-position-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/disable-layout-ng/fast/css/caption-width-fixed-position-offset-top-expected.png b/third_party/blink/web_tests/flag-specific/disable-layout-ng/fast/css/caption-width-fixed-position-offset-top-expected.png
new file mode 100644
index 0000000..46577274
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/disable-layout-ng/fast/css/caption-width-fixed-position-offset-top-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/disable-layout-ng/fast/table/border-collapsing/001-expected.png b/third_party/blink/web_tests/flag-specific/disable-layout-ng/fast/table/border-collapsing/001-expected.png
new file mode 100644
index 0000000..b73441b
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/disable-layout-ng/fast/table/border-collapsing/001-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/disable-layout-ng/fast/table/border-collapsing/001-vertical-expected.png b/third_party/blink/web_tests/flag-specific/disable-layout-ng/fast/table/border-collapsing/001-vertical-expected.png
new file mode 100644
index 0000000..522e9cc
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/disable-layout-ng/fast/table/border-collapsing/001-vertical-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/disable-layout-ng/fast/table/border-collapsing/003-expected.png b/third_party/blink/web_tests/flag-specific/disable-layout-ng/fast/table/border-collapsing/003-expected.png
new file mode 100644
index 0000000..cbe34da0
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/disable-layout-ng/fast/table/border-collapsing/003-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/disable-layout-ng/paint/tables/collapsed-border-corner-conflict-expected.png b/third_party/blink/web_tests/flag-specific/disable-layout-ng/paint/tables/collapsed-border-corner-conflict-expected.png
new file mode 100644
index 0000000..0bb78fbf
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/disable-layout-ng/paint/tables/collapsed-border-corner-conflict-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/paint/tables/collapsed-border-corner-conflict-expected.png b/third_party/blink/web_tests/paint/tables/collapsed-border-corner-conflict-expected.png
index 0bb78fbf..7f88daa 100644
--- a/third_party/blink/web_tests/paint/tables/collapsed-border-corner-conflict-expected.png
+++ b/third_party/blink/web_tests/paint/tables/collapsed-border-corner-conflict-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/fast/loader/plain-text-document-appearance-expected.png b/third_party/blink/web_tests/platform/linux/fast/loader/plain-text-document-appearance-expected.png
new file mode 100644
index 0000000..d92a997
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/fast/loader/plain-text-document-appearance-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/fast/table/backgr_border-table-cell-collapsed-border-expected.png b/third_party/blink/web_tests/platform/linux/fast/table/backgr_border-table-cell-collapsed-border-expected.png
index afc54d19..bfc32f3 100644
--- a/third_party/blink/web_tests/platform/linux/fast/table/backgr_border-table-cell-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/table/backgr_border-table-cell-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/fast/table/backgr_border-table-collapsed-border-expected.png b/third_party/blink/web_tests/platform/linux/fast/table/backgr_border-table-collapsed-border-expected.png
index aad93fd..8ea3b480 100644
--- a/third_party/blink/web_tests/platform/linux/fast/table/backgr_border-table-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/table/backgr_border-table-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/fast/table/backgr_border-table-column-collapsed-border-expected.png b/third_party/blink/web_tests/platform/linux/fast/table/backgr_border-table-column-collapsed-border-expected.png
index fa08eda..c3830287 100644
--- a/third_party/blink/web_tests/platform/linux/fast/table/backgr_border-table-column-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/table/backgr_border-table-column-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/fast/table/backgr_border-table-column-group-collapsed-border-expected.png b/third_party/blink/web_tests/platform/linux/fast/table/backgr_border-table-column-group-collapsed-border-expected.png
index ef5907b..5415ad9 100644
--- a/third_party/blink/web_tests/platform/linux/fast/table/backgr_border-table-column-group-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/table/backgr_border-table-column-group-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/fast/table/backgr_border-table-quirks-collapsed-border-expected.png b/third_party/blink/web_tests/platform/linux/fast/table/backgr_border-table-quirks-collapsed-border-expected.png
index 323c44f9..f5a4a681 100644
--- a/third_party/blink/web_tests/platform/linux/fast/table/backgr_border-table-quirks-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/table/backgr_border-table-quirks-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/fast/table/backgr_border-table-row-collapsed-border-expected.png b/third_party/blink/web_tests/platform/linux/fast/table/backgr_border-table-row-collapsed-border-expected.png
index 178f1813..0a522eb 100644
--- a/third_party/blink/web_tests/platform/linux/fast/table/backgr_border-table-row-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/table/backgr_border-table-row-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/fast/table/backgr_border-table-row-group-collapsed-border-expected.png b/third_party/blink/web_tests/platform/linux/fast/table/backgr_border-table-row-group-collapsed-border-expected.png
index f6b536f..7b15fa35 100644
--- a/third_party/blink/web_tests/platform/linux/fast/table/backgr_border-table-row-group-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/table/backgr_border-table-row-group-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/fast/table/backgr_layers-hide-collapsed-border-expected.png b/third_party/blink/web_tests/platform/linux/fast/table/backgr_layers-hide-collapsed-border-expected.png
index 7bca9869..a6c05e8 100644
--- a/third_party/blink/web_tests/platform/linux/fast/table/backgr_layers-hide-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/table/backgr_layers-hide-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/fast/table/backgr_position-table-cell-collapsed-border-expected.png b/third_party/blink/web_tests/platform/linux/fast/table/backgr_position-table-cell-collapsed-border-expected.png
index 5bb6c17..a2e41b0 100644
--- a/third_party/blink/web_tests/platform/linux/fast/table/backgr_position-table-cell-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/table/backgr_position-table-cell-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/fast/table/backgr_position-table-collapsed-border-expected.png b/third_party/blink/web_tests/platform/linux/fast/table/backgr_position-table-collapsed-border-expected.png
index aee0a8f3..9370739 100644
--- a/third_party/blink/web_tests/platform/linux/fast/table/backgr_position-table-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/table/backgr_position-table-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/fast/table/backgr_position-table-column-collapsed-border-expected.png b/third_party/blink/web_tests/platform/linux/fast/table/backgr_position-table-column-collapsed-border-expected.png
index 06d95b1..d5d8f98 100644
--- a/third_party/blink/web_tests/platform/linux/fast/table/backgr_position-table-column-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/table/backgr_position-table-column-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/fast/table/backgr_position-table-column-group-collapsed-border-expected.png b/third_party/blink/web_tests/platform/linux/fast/table/backgr_position-table-column-group-collapsed-border-expected.png
index fc72725..5b06fc7 100644
--- a/third_party/blink/web_tests/platform/linux/fast/table/backgr_position-table-column-group-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/table/backgr_position-table-column-group-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/fast/table/backgr_position-table-row-collapsed-border-expected.png b/third_party/blink/web_tests/platform/linux/fast/table/backgr_position-table-row-collapsed-border-expected.png
index b8e9d17a..116237f3 100644
--- a/third_party/blink/web_tests/platform/linux/fast/table/backgr_position-table-row-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/table/backgr_position-table-row-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/fast/table/backgr_position-table-row-group-collapsed-border-expected.png b/third_party/blink/web_tests/platform/linux/fast/table/backgr_position-table-row-group-collapsed-border-expected.png
index 16945b3..2ca0ee3 100644
--- a/third_party/blink/web_tests/platform/linux/fast/table/backgr_position-table-row-group-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/table/backgr_position-table-row-group-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/fast/table/backgr_simple-table-cell-collapsed-border-expected.png b/third_party/blink/web_tests/platform/linux/fast/table/backgr_simple-table-cell-collapsed-border-expected.png
index 1a646d4..516e45ca 100644
--- a/third_party/blink/web_tests/platform/linux/fast/table/backgr_simple-table-cell-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/table/backgr_simple-table-cell-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/fast/table/backgr_simple-table-collapsed-border-expected.png b/third_party/blink/web_tests/platform/linux/fast/table/backgr_simple-table-collapsed-border-expected.png
index 072aa70..a6898ec5 100644
--- a/third_party/blink/web_tests/platform/linux/fast/table/backgr_simple-table-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/table/backgr_simple-table-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/fast/table/backgr_simple-table-column-collapsed-border-expected.png b/third_party/blink/web_tests/platform/linux/fast/table/backgr_simple-table-column-collapsed-border-expected.png
index fa9abc9..cc3fdcc 100644
--- a/third_party/blink/web_tests/platform/linux/fast/table/backgr_simple-table-column-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/table/backgr_simple-table-column-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/fast/table/backgr_simple-table-column-group-collapsed-border-expected.png b/third_party/blink/web_tests/platform/linux/fast/table/backgr_simple-table-column-group-collapsed-border-expected.png
index 9973a27b..287d02e 100644
--- a/third_party/blink/web_tests/platform/linux/fast/table/backgr_simple-table-column-group-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/table/backgr_simple-table-column-group-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/fast/table/backgr_simple-table-row-collapsed-border-expected.png b/third_party/blink/web_tests/platform/linux/fast/table/backgr_simple-table-row-collapsed-border-expected.png
index fa20838a..d013d138 100644
--- a/third_party/blink/web_tests/platform/linux/fast/table/backgr_simple-table-row-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/table/backgr_simple-table-row-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/fast/table/backgr_simple-table-row-group-collapsed-border-expected.png b/third_party/blink/web_tests/platform/linux/fast/table/backgr_simple-table-row-group-collapsed-border-expected.png
index 0fa89e9a..7e8a04a9 100644
--- a/third_party/blink/web_tests/platform/linux/fast/table/backgr_simple-table-row-group-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/table/backgr_simple-table-row-group-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/fast/table/border-collapsing/001-expected.png b/third_party/blink/web_tests/platform/linux/fast/table/border-collapsing/001-expected.png
index b73441b..1232278 100644
--- a/third_party/blink/web_tests/platform/linux/fast/table/border-collapsing/001-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/table/border-collapsing/001-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/fast/table/border-collapsing/001-vertical-expected.png b/third_party/blink/web_tests/platform/linux/fast/table/border-collapsing/001-vertical-expected.png
index 522e9cc..9bf2e19 100644
--- a/third_party/blink/web_tests/platform/linux/fast/table/border-collapsing/001-vertical-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/table/border-collapsing/001-vertical-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/fast/table/border-collapsing/003-expected.png b/third_party/blink/web_tests/platform/linux/fast/table/border-collapsing/003-expected.png
index cbe34da0..313e1b8 100644
--- a/third_party/blink/web_tests/platform/linux/fast/table/border-collapsing/003-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/table/border-collapsing/003-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/fast/table/border-collapsing/003-vertical-expected.png b/third_party/blink/web_tests/platform/linux/fast/table/border-collapsing/003-vertical-expected.png
index 113269c..9abff21 100644
--- a/third_party/blink/web_tests/platform/linux/fast/table/border-collapsing/003-vertical-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/table/border-collapsing/003-vertical-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/fast/table/border-collapsing/rtl-border-collapsing-expected.png b/third_party/blink/web_tests/platform/linux/fast/table/border-collapsing/rtl-border-collapsing-expected.png
index c5bf69a3..a5ae5319 100644
--- a/third_party/blink/web_tests/platform/linux/fast/table/border-collapsing/rtl-border-collapsing-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/table/border-collapsing/rtl-border-collapsing-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/dark-color-scheme/fast/loader/plain-text-document-appearance-expected.png b/third_party/blink/web_tests/platform/linux/virtual/dark-color-scheme/fast/loader/plain-text-document-appearance-expected.png
new file mode 100644
index 0000000..daab334e
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/dark-color-scheme/fast/loader/plain-text-document-appearance-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/text-antialias/international/bidi-layout-across-linebreak-expected.png b/third_party/blink/web_tests/platform/linux/virtual/text-antialias/international/bidi-layout-across-linebreak-expected.png
index e62fe7f..0cb45b5 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/text-antialias/international/bidi-layout-across-linebreak-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/text-antialias/international/bidi-layout-across-linebreak-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/text-antialias/international/bidi-layout-across-linebreak-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/text-antialias/international/bidi-layout-across-linebreak-expected.png
index 4535a11..b8fdca8 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/text-antialias/international/bidi-layout-across-linebreak-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/text-antialias/international/bidi-layout-across-linebreak-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.13/virtual/text-antialias/international/bidi-layout-across-linebreak-expected.png b/third_party/blink/web_tests/platform/mac-mac10.13/virtual/text-antialias/international/bidi-layout-across-linebreak-expected.png
index 6587539..5c686ad 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.13/virtual/text-antialias/international/bidi-layout-across-linebreak-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.13/virtual/text-antialias/international/bidi-layout-across-linebreak-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.14/fast/table/backgr_border-table-cell-collapsed-border-expected.png b/third_party/blink/web_tests/platform/mac-mac10.14/fast/table/backgr_border-table-cell-collapsed-border-expected.png
index eac38b2..0dfc26be6 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.14/fast/table/backgr_border-table-cell-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.14/fast/table/backgr_border-table-cell-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.14/fast/table/backgr_border-table-collapsed-border-expected.png b/third_party/blink/web_tests/platform/mac-mac10.14/fast/table/backgr_border-table-collapsed-border-expected.png
index 6314ec8..1577a11 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.14/fast/table/backgr_border-table-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.14/fast/table/backgr_border-table-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.14/fast/table/backgr_border-table-column-collapsed-border-expected.png b/third_party/blink/web_tests/platform/mac-mac10.14/fast/table/backgr_border-table-column-collapsed-border-expected.png
index 01bb27b..20223db 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.14/fast/table/backgr_border-table-column-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.14/fast/table/backgr_border-table-column-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.14/fast/table/backgr_border-table-column-group-collapsed-border-expected.png b/third_party/blink/web_tests/platform/mac-mac10.14/fast/table/backgr_border-table-column-group-collapsed-border-expected.png
index 39cab1d..82748b3a 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.14/fast/table/backgr_border-table-column-group-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.14/fast/table/backgr_border-table-column-group-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.14/fast/table/backgr_border-table-quirks-collapsed-border-expected.png b/third_party/blink/web_tests/platform/mac-mac10.14/fast/table/backgr_border-table-quirks-collapsed-border-expected.png
index 65a6433..fd90750 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.14/fast/table/backgr_border-table-quirks-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.14/fast/table/backgr_border-table-quirks-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.14/fast/table/backgr_border-table-row-collapsed-border-expected.png b/third_party/blink/web_tests/platform/mac-mac10.14/fast/table/backgr_border-table-row-collapsed-border-expected.png
index 1cd1a192..d6c5bc9 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.14/fast/table/backgr_border-table-row-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.14/fast/table/backgr_border-table-row-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.14/fast/table/backgr_border-table-row-group-collapsed-border-expected.png b/third_party/blink/web_tests/platform/mac-mac10.14/fast/table/backgr_border-table-row-group-collapsed-border-expected.png
index b88123e..c0931bbb 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.14/fast/table/backgr_border-table-row-group-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.14/fast/table/backgr_border-table-row-group-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.14/fast/table/backgr_layers-hide-collapsed-border-expected.png b/third_party/blink/web_tests/platform/mac-mac10.14/fast/table/backgr_layers-hide-collapsed-border-expected.png
index e0278ed..d6d8b22 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.14/fast/table/backgr_layers-hide-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.14/fast/table/backgr_layers-hide-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.14/fast/table/backgr_position-table-cell-collapsed-border-expected.png b/third_party/blink/web_tests/platform/mac-mac10.14/fast/table/backgr_position-table-cell-collapsed-border-expected.png
index 0c38f06..dd42665 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.14/fast/table/backgr_position-table-cell-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.14/fast/table/backgr_position-table-cell-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.14/fast/table/backgr_position-table-collapsed-border-expected.png b/third_party/blink/web_tests/platform/mac-mac10.14/fast/table/backgr_position-table-collapsed-border-expected.png
index 7d46478..c058afba 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.14/fast/table/backgr_position-table-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.14/fast/table/backgr_position-table-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.14/fast/table/backgr_position-table-column-collapsed-border-expected.png b/third_party/blink/web_tests/platform/mac-mac10.14/fast/table/backgr_position-table-column-collapsed-border-expected.png
index b3060f3..05eca2c 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.14/fast/table/backgr_position-table-column-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.14/fast/table/backgr_position-table-column-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.14/fast/table/backgr_position-table-column-group-collapsed-border-expected.png b/third_party/blink/web_tests/platform/mac-mac10.14/fast/table/backgr_position-table-column-group-collapsed-border-expected.png
index a509368..769bbb7 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.14/fast/table/backgr_position-table-column-group-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.14/fast/table/backgr_position-table-column-group-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.14/fast/table/backgr_position-table-row-collapsed-border-expected.png b/third_party/blink/web_tests/platform/mac-mac10.14/fast/table/backgr_position-table-row-collapsed-border-expected.png
index b9206bb..ad35499f 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.14/fast/table/backgr_position-table-row-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.14/fast/table/backgr_position-table-row-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.14/fast/table/backgr_position-table-row-group-collapsed-border-expected.png b/third_party/blink/web_tests/platform/mac-mac10.14/fast/table/backgr_position-table-row-group-collapsed-border-expected.png
index d79cbc22..02d4d4f 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.14/fast/table/backgr_position-table-row-group-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.14/fast/table/backgr_position-table-row-group-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.14/fast/table/backgr_simple-table-cell-collapsed-border-expected.png b/third_party/blink/web_tests/platform/mac-mac10.14/fast/table/backgr_simple-table-cell-collapsed-border-expected.png
index 0042a7a..8443077 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.14/fast/table/backgr_simple-table-cell-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.14/fast/table/backgr_simple-table-cell-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.14/fast/table/backgr_simple-table-collapsed-border-expected.png b/third_party/blink/web_tests/platform/mac-mac10.14/fast/table/backgr_simple-table-collapsed-border-expected.png
index 5ad75766..19f4e42 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.14/fast/table/backgr_simple-table-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.14/fast/table/backgr_simple-table-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.14/fast/table/backgr_simple-table-column-collapsed-border-expected.png b/third_party/blink/web_tests/platform/mac-mac10.14/fast/table/backgr_simple-table-column-collapsed-border-expected.png
index 03c9884..a023d61 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.14/fast/table/backgr_simple-table-column-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.14/fast/table/backgr_simple-table-column-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.14/fast/table/backgr_simple-table-column-group-collapsed-border-expected.png b/third_party/blink/web_tests/platform/mac-mac10.14/fast/table/backgr_simple-table-column-group-collapsed-border-expected.png
index 56cec9f..1c77eb2 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.14/fast/table/backgr_simple-table-column-group-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.14/fast/table/backgr_simple-table-column-group-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.14/fast/table/backgr_simple-table-row-collapsed-border-expected.png b/third_party/blink/web_tests/platform/mac-mac10.14/fast/table/backgr_simple-table-row-collapsed-border-expected.png
index 02fac98..9c459ab 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.14/fast/table/backgr_simple-table-row-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.14/fast/table/backgr_simple-table-row-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.14/fast/table/backgr_simple-table-row-group-collapsed-border-expected.png b/third_party/blink/web_tests/platform/mac-mac10.14/fast/table/backgr_simple-table-row-group-collapsed-border-expected.png
index 8f032aad..8e75ae87 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.14/fast/table/backgr_simple-table-row-group-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.14/fast/table/backgr_simple-table-row-group-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/css/caption-width-absolute-position-expected.png b/third_party/blink/web_tests/platform/mac/fast/css/caption-width-absolute-position-expected.png
deleted file mode 100644
index 2d636ac..0000000
--- a/third_party/blink/web_tests/platform/mac/fast/css/caption-width-absolute-position-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/css/caption-width-absolute-position-offset-top-expected.png b/third_party/blink/web_tests/platform/mac/fast/css/caption-width-absolute-position-offset-top-expected.png
deleted file mode 100644
index 4910b2c..0000000
--- a/third_party/blink/web_tests/platform/mac/fast/css/caption-width-absolute-position-offset-top-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/css/caption-width-fixed-position-expected.png b/third_party/blink/web_tests/platform/mac/fast/css/caption-width-fixed-position-expected.png
deleted file mode 100644
index 2d636ac..0000000
--- a/third_party/blink/web_tests/platform/mac/fast/css/caption-width-fixed-position-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/css/caption-width-fixed-position-offset-top-expected.png b/third_party/blink/web_tests/platform/mac/fast/css/caption-width-fixed-position-offset-top-expected.png
deleted file mode 100644
index 4910b2c..0000000
--- a/third_party/blink/web_tests/platform/mac/fast/css/caption-width-fixed-position-offset-top-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/loader/plain-text-document-appearance-expected.png b/third_party/blink/web_tests/platform/mac/fast/loader/plain-text-document-appearance-expected.png
new file mode 100644
index 0000000..aa2f5f58
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac/fast/loader/plain-text-document-appearance-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/table/backgr_border-table-cell-collapsed-border-expected.png b/third_party/blink/web_tests/platform/mac/fast/table/backgr_border-table-cell-collapsed-border-expected.png
index 5c14b9a..c6f4a8b 100644
--- a/third_party/blink/web_tests/platform/mac/fast/table/backgr_border-table-cell-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/table/backgr_border-table-cell-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/table/backgr_border-table-collapsed-border-expected.png b/third_party/blink/web_tests/platform/mac/fast/table/backgr_border-table-collapsed-border-expected.png
index 8bf58821..05f07f6 100644
--- a/third_party/blink/web_tests/platform/mac/fast/table/backgr_border-table-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/table/backgr_border-table-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/table/backgr_border-table-column-collapsed-border-expected.png b/third_party/blink/web_tests/platform/mac/fast/table/backgr_border-table-column-collapsed-border-expected.png
index 3a73b6a..aa8f256c 100644
--- a/third_party/blink/web_tests/platform/mac/fast/table/backgr_border-table-column-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/table/backgr_border-table-column-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/table/backgr_border-table-column-group-collapsed-border-expected.png b/third_party/blink/web_tests/platform/mac/fast/table/backgr_border-table-column-group-collapsed-border-expected.png
index 01ef547c..c52a3e8 100644
--- a/third_party/blink/web_tests/platform/mac/fast/table/backgr_border-table-column-group-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/table/backgr_border-table-column-group-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/table/backgr_border-table-quirks-collapsed-border-expected.png b/third_party/blink/web_tests/platform/mac/fast/table/backgr_border-table-quirks-collapsed-border-expected.png
index 55a095d..5bb4426 100644
--- a/third_party/blink/web_tests/platform/mac/fast/table/backgr_border-table-quirks-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/table/backgr_border-table-quirks-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/table/backgr_border-table-row-collapsed-border-expected.png b/third_party/blink/web_tests/platform/mac/fast/table/backgr_border-table-row-collapsed-border-expected.png
index ebea5c7..a13f001 100644
--- a/third_party/blink/web_tests/platform/mac/fast/table/backgr_border-table-row-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/table/backgr_border-table-row-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/table/backgr_border-table-row-group-collapsed-border-expected.png b/third_party/blink/web_tests/platform/mac/fast/table/backgr_border-table-row-group-collapsed-border-expected.png
index f40aee8e..9d0e5d6 100644
--- a/third_party/blink/web_tests/platform/mac/fast/table/backgr_border-table-row-group-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/table/backgr_border-table-row-group-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/table/backgr_layers-hide-collapsed-border-expected.png b/third_party/blink/web_tests/platform/mac/fast/table/backgr_layers-hide-collapsed-border-expected.png
index ba46737..226af71 100644
--- a/third_party/blink/web_tests/platform/mac/fast/table/backgr_layers-hide-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/table/backgr_layers-hide-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/table/backgr_position-table-cell-collapsed-border-expected.png b/third_party/blink/web_tests/platform/mac/fast/table/backgr_position-table-cell-collapsed-border-expected.png
index cc74298..7107808 100644
--- a/third_party/blink/web_tests/platform/mac/fast/table/backgr_position-table-cell-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/table/backgr_position-table-cell-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/table/backgr_position-table-collapsed-border-expected.png b/third_party/blink/web_tests/platform/mac/fast/table/backgr_position-table-collapsed-border-expected.png
index a3b160e..30f422f 100644
--- a/third_party/blink/web_tests/platform/mac/fast/table/backgr_position-table-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/table/backgr_position-table-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/table/backgr_position-table-column-collapsed-border-expected.png b/third_party/blink/web_tests/platform/mac/fast/table/backgr_position-table-column-collapsed-border-expected.png
index b6e7221..89ff862 100644
--- a/third_party/blink/web_tests/platform/mac/fast/table/backgr_position-table-column-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/table/backgr_position-table-column-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/table/backgr_position-table-column-group-collapsed-border-expected.png b/third_party/blink/web_tests/platform/mac/fast/table/backgr_position-table-column-group-collapsed-border-expected.png
index 3462b7d..9122004 100644
--- a/third_party/blink/web_tests/platform/mac/fast/table/backgr_position-table-column-group-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/table/backgr_position-table-column-group-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/table/backgr_position-table-row-collapsed-border-expected.png b/third_party/blink/web_tests/platform/mac/fast/table/backgr_position-table-row-collapsed-border-expected.png
index 149aafa..31638ed6 100644
--- a/third_party/blink/web_tests/platform/mac/fast/table/backgr_position-table-row-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/table/backgr_position-table-row-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/table/backgr_position-table-row-group-collapsed-border-expected.png b/third_party/blink/web_tests/platform/mac/fast/table/backgr_position-table-row-group-collapsed-border-expected.png
index fdedb6b..08ef4aa 100644
--- a/third_party/blink/web_tests/platform/mac/fast/table/backgr_position-table-row-group-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/table/backgr_position-table-row-group-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/table/backgr_simple-table-cell-collapsed-border-expected.png b/third_party/blink/web_tests/platform/mac/fast/table/backgr_simple-table-cell-collapsed-border-expected.png
index 5354b47..34a20ad 100644
--- a/third_party/blink/web_tests/platform/mac/fast/table/backgr_simple-table-cell-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/table/backgr_simple-table-cell-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/table/backgr_simple-table-collapsed-border-expected.png b/third_party/blink/web_tests/platform/mac/fast/table/backgr_simple-table-collapsed-border-expected.png
index 4769a59..9cdfb9a3 100644
--- a/third_party/blink/web_tests/platform/mac/fast/table/backgr_simple-table-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/table/backgr_simple-table-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/table/backgr_simple-table-column-collapsed-border-expected.png b/third_party/blink/web_tests/platform/mac/fast/table/backgr_simple-table-column-collapsed-border-expected.png
index 7d9ffd433..da41075 100644
--- a/third_party/blink/web_tests/platform/mac/fast/table/backgr_simple-table-column-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/table/backgr_simple-table-column-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/table/backgr_simple-table-column-group-collapsed-border-expected.png b/third_party/blink/web_tests/platform/mac/fast/table/backgr_simple-table-column-group-collapsed-border-expected.png
index 5d7ba72..9827149 100644
--- a/third_party/blink/web_tests/platform/mac/fast/table/backgr_simple-table-column-group-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/table/backgr_simple-table-column-group-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/table/backgr_simple-table-row-collapsed-border-expected.png b/third_party/blink/web_tests/platform/mac/fast/table/backgr_simple-table-row-collapsed-border-expected.png
index 00df7762..f9e072f 100644
--- a/third_party/blink/web_tests/platform/mac/fast/table/backgr_simple-table-row-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/table/backgr_simple-table-row-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/table/backgr_simple-table-row-group-collapsed-border-expected.png b/third_party/blink/web_tests/platform/mac/fast/table/backgr_simple-table-row-group-collapsed-border-expected.png
index 466c21fd..3d8ee02 100644
--- a/third_party/blink/web_tests/platform/mac/fast/table/backgr_simple-table-row-group-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/table/backgr_simple-table-row-group-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/table/border-collapsing/001-expected.png b/third_party/blink/web_tests/platform/mac/fast/table/border-collapsing/001-expected.png
index d7f294a..e8bbc30 100644
--- a/third_party/blink/web_tests/platform/mac/fast/table/border-collapsing/001-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/table/border-collapsing/001-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/table/border-collapsing/001-vertical-expected.png b/third_party/blink/web_tests/platform/mac/fast/table/border-collapsing/001-vertical-expected.png
index 75230ffa..00c8681 100644
--- a/third_party/blink/web_tests/platform/mac/fast/table/border-collapsing/001-vertical-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/table/border-collapsing/001-vertical-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/table/border-collapsing/003-expected.png b/third_party/blink/web_tests/platform/mac/fast/table/border-collapsing/003-expected.png
index f0c0c03..8ea4f8d 100644
--- a/third_party/blink/web_tests/platform/mac/fast/table/border-collapsing/003-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/table/border-collapsing/003-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/table/border-collapsing/003-vertical-expected.png b/third_party/blink/web_tests/platform/mac/fast/table/border-collapsing/003-vertical-expected.png
index 257d4d3f..ed057ee 100644
--- a/third_party/blink/web_tests/platform/mac/fast/table/border-collapsing/003-vertical-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/table/border-collapsing/003-vertical-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/table/border-collapsing/rtl-border-collapsing-expected.png b/third_party/blink/web_tests/platform/mac/fast/table/border-collapsing/rtl-border-collapsing-expected.png
index bf259d5..4eb08d3 100644
--- a/third_party/blink/web_tests/platform/mac/fast/table/border-collapsing/rtl-border-collapsing-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/table/border-collapsing/rtl-border-collapsing-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/paint/invalidation/table/table-cell-collapsed-border-expected.txt b/third_party/blink/web_tests/platform/mac/paint/invalidation/table/table-cell-collapsed-border-expected.txt
index 5b77afb..48e0ff9 100644
--- a/third_party/blink/web_tests/platform/mac/paint/invalidation/table/table-cell-collapsed-border-expected.txt
+++ b/third_party/blink/web_tests/platform/mac/paint/invalidation/table/table-cell-collapsed-border-expected.txt
@@ -6,7 +6,7 @@
       "contentsOpaque": true,
       "backgroundColor": "#FFFFFF",
       "invalidations": [
-        [3, 60, 441, 405]
+        [8, 60, 441, 405]
       ]
     }
   ]
diff --git a/third_party/blink/web_tests/platform/mac/virtual/dark-color-scheme/fast/loader/plain-text-document-appearance-expected.png b/third_party/blink/web_tests/platform/mac/virtual/dark-color-scheme/fast/loader/plain-text-document-appearance-expected.png
new file mode 100644
index 0000000..e2ac537
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac/virtual/dark-color-scheme/fast/loader/plain-text-document-appearance-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/text-antialias/international/bidi-layout-across-linebreak-expected.png b/third_party/blink/web_tests/platform/mac/virtual/text-antialias/international/bidi-layout-across-linebreak-expected.png
index 282aa60..99e1262 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/text-antialias/international/bidi-layout-across-linebreak-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/text-antialias/international/bidi-layout-across-linebreak-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/css/caption-width-absolute-position-offset-top-expected.png b/third_party/blink/web_tests/platform/win/fast/css/caption-width-absolute-position-offset-top-expected.png
deleted file mode 100644
index 4e661d6..0000000
--- a/third_party/blink/web_tests/platform/win/fast/css/caption-width-absolute-position-offset-top-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/css/caption-width-fixed-position-expected.png b/third_party/blink/web_tests/platform/win/fast/css/caption-width-fixed-position-expected.png
deleted file mode 100644
index 0f960a1c..0000000
--- a/third_party/blink/web_tests/platform/win/fast/css/caption-width-fixed-position-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/css/caption-width-fixed-position-offset-top-expected.png b/third_party/blink/web_tests/platform/win/fast/css/caption-width-fixed-position-offset-top-expected.png
deleted file mode 100644
index 4e661d6..0000000
--- a/third_party/blink/web_tests/platform/win/fast/css/caption-width-fixed-position-offset-top-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/loader/plain-text-document-appearance-expected.png b/third_party/blink/web_tests/platform/win/fast/loader/plain-text-document-appearance-expected.png
new file mode 100644
index 0000000..56a7f97
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win/fast/loader/plain-text-document-appearance-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/table/backgr_border-table-cell-collapsed-border-expected.png b/third_party/blink/web_tests/platform/win/fast/table/backgr_border-table-cell-collapsed-border-expected.png
index 276c6b8..002b17d 100644
--- a/third_party/blink/web_tests/platform/win/fast/table/backgr_border-table-cell-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/win/fast/table/backgr_border-table-cell-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/table/backgr_border-table-collapsed-border-expected.png b/third_party/blink/web_tests/platform/win/fast/table/backgr_border-table-collapsed-border-expected.png
index caae3584..d13eaf4 100644
--- a/third_party/blink/web_tests/platform/win/fast/table/backgr_border-table-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/win/fast/table/backgr_border-table-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/table/backgr_border-table-column-collapsed-border-expected.png b/third_party/blink/web_tests/platform/win/fast/table/backgr_border-table-column-collapsed-border-expected.png
index 3e32adb..9810faa 100644
--- a/third_party/blink/web_tests/platform/win/fast/table/backgr_border-table-column-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/win/fast/table/backgr_border-table-column-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/table/backgr_border-table-column-group-collapsed-border-expected.png b/third_party/blink/web_tests/platform/win/fast/table/backgr_border-table-column-group-collapsed-border-expected.png
index a943910..84f0e55 100644
--- a/third_party/blink/web_tests/platform/win/fast/table/backgr_border-table-column-group-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/win/fast/table/backgr_border-table-column-group-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/table/backgr_border-table-quirks-collapsed-border-expected.png b/third_party/blink/web_tests/platform/win/fast/table/backgr_border-table-quirks-collapsed-border-expected.png
index c8f9791..437b5856 100644
--- a/third_party/blink/web_tests/platform/win/fast/table/backgr_border-table-quirks-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/win/fast/table/backgr_border-table-quirks-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/table/backgr_border-table-row-collapsed-border-expected.png b/third_party/blink/web_tests/platform/win/fast/table/backgr_border-table-row-collapsed-border-expected.png
index f846027..12aa39d8 100644
--- a/third_party/blink/web_tests/platform/win/fast/table/backgr_border-table-row-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/win/fast/table/backgr_border-table-row-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/table/backgr_border-table-row-group-collapsed-border-expected.png b/third_party/blink/web_tests/platform/win/fast/table/backgr_border-table-row-group-collapsed-border-expected.png
index 830ee85e..d4a6f48 100644
--- a/third_party/blink/web_tests/platform/win/fast/table/backgr_border-table-row-group-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/win/fast/table/backgr_border-table-row-group-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/table/backgr_layers-hide-collapsed-border-expected.png b/third_party/blink/web_tests/platform/win/fast/table/backgr_layers-hide-collapsed-border-expected.png
index 9445f1d..c9d6322 100644
--- a/third_party/blink/web_tests/platform/win/fast/table/backgr_layers-hide-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/win/fast/table/backgr_layers-hide-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/table/backgr_position-table-cell-collapsed-border-expected.png b/third_party/blink/web_tests/platform/win/fast/table/backgr_position-table-cell-collapsed-border-expected.png
index 67d5971..ca35ae4 100644
--- a/third_party/blink/web_tests/platform/win/fast/table/backgr_position-table-cell-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/win/fast/table/backgr_position-table-cell-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/table/backgr_position-table-collapsed-border-expected.png b/third_party/blink/web_tests/platform/win/fast/table/backgr_position-table-collapsed-border-expected.png
index 46ba0a7..8792b64 100644
--- a/third_party/blink/web_tests/platform/win/fast/table/backgr_position-table-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/win/fast/table/backgr_position-table-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/table/backgr_position-table-column-collapsed-border-expected.png b/third_party/blink/web_tests/platform/win/fast/table/backgr_position-table-column-collapsed-border-expected.png
index 3bb31d1..4159a88 100644
--- a/third_party/blink/web_tests/platform/win/fast/table/backgr_position-table-column-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/win/fast/table/backgr_position-table-column-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/table/backgr_position-table-column-group-collapsed-border-expected.png b/third_party/blink/web_tests/platform/win/fast/table/backgr_position-table-column-group-collapsed-border-expected.png
index 42f19a9..57e6565 100644
--- a/third_party/blink/web_tests/platform/win/fast/table/backgr_position-table-column-group-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/win/fast/table/backgr_position-table-column-group-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/table/backgr_position-table-row-collapsed-border-expected.png b/third_party/blink/web_tests/platform/win/fast/table/backgr_position-table-row-collapsed-border-expected.png
index 79df2a0f..d0436fd5 100644
--- a/third_party/blink/web_tests/platform/win/fast/table/backgr_position-table-row-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/win/fast/table/backgr_position-table-row-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/table/backgr_position-table-row-group-collapsed-border-expected.png b/third_party/blink/web_tests/platform/win/fast/table/backgr_position-table-row-group-collapsed-border-expected.png
index 0cc90b36..2b12fb15 100644
--- a/third_party/blink/web_tests/platform/win/fast/table/backgr_position-table-row-group-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/win/fast/table/backgr_position-table-row-group-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/table/backgr_simple-table-cell-collapsed-border-expected.png b/third_party/blink/web_tests/platform/win/fast/table/backgr_simple-table-cell-collapsed-border-expected.png
index 5ce0b62..10739dc 100644
--- a/third_party/blink/web_tests/platform/win/fast/table/backgr_simple-table-cell-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/win/fast/table/backgr_simple-table-cell-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/table/backgr_simple-table-collapsed-border-expected.png b/third_party/blink/web_tests/platform/win/fast/table/backgr_simple-table-collapsed-border-expected.png
index e23cab6..4f13201 100644
--- a/third_party/blink/web_tests/platform/win/fast/table/backgr_simple-table-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/win/fast/table/backgr_simple-table-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/table/backgr_simple-table-column-collapsed-border-expected.png b/third_party/blink/web_tests/platform/win/fast/table/backgr_simple-table-column-collapsed-border-expected.png
index 7613428..cd3125fe 100644
--- a/third_party/blink/web_tests/platform/win/fast/table/backgr_simple-table-column-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/win/fast/table/backgr_simple-table-column-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/table/backgr_simple-table-column-group-collapsed-border-expected.png b/third_party/blink/web_tests/platform/win/fast/table/backgr_simple-table-column-group-collapsed-border-expected.png
index 612f4ef..4b1a95af 100644
--- a/third_party/blink/web_tests/platform/win/fast/table/backgr_simple-table-column-group-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/win/fast/table/backgr_simple-table-column-group-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/table/backgr_simple-table-row-collapsed-border-expected.png b/third_party/blink/web_tests/platform/win/fast/table/backgr_simple-table-row-collapsed-border-expected.png
index 513b076..a37e8f6 100644
--- a/third_party/blink/web_tests/platform/win/fast/table/backgr_simple-table-row-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/win/fast/table/backgr_simple-table-row-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/table/backgr_simple-table-row-group-collapsed-border-expected.png b/third_party/blink/web_tests/platform/win/fast/table/backgr_simple-table-row-group-collapsed-border-expected.png
index e68c9d9..b2732c0 100644
--- a/third_party/blink/web_tests/platform/win/fast/table/backgr_simple-table-row-group-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/win/fast/table/backgr_simple-table-row-group-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/table/border-collapsing/001-expected.png b/third_party/blink/web_tests/platform/win/fast/table/border-collapsing/001-expected.png
index 869fe38..3830ad9 100644
--- a/third_party/blink/web_tests/platform/win/fast/table/border-collapsing/001-expected.png
+++ b/third_party/blink/web_tests/platform/win/fast/table/border-collapsing/001-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/table/border-collapsing/001-vertical-expected.png b/third_party/blink/web_tests/platform/win/fast/table/border-collapsing/001-vertical-expected.png
index 059986fd..c9ac0b7 100644
--- a/third_party/blink/web_tests/platform/win/fast/table/border-collapsing/001-vertical-expected.png
+++ b/third_party/blink/web_tests/platform/win/fast/table/border-collapsing/001-vertical-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/table/border-collapsing/003-expected.png b/third_party/blink/web_tests/platform/win/fast/table/border-collapsing/003-expected.png
index 9d6b74c..a9e7708e 100644
--- a/third_party/blink/web_tests/platform/win/fast/table/border-collapsing/003-expected.png
+++ b/third_party/blink/web_tests/platform/win/fast/table/border-collapsing/003-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/table/border-collapsing/003-vertical-expected.png b/third_party/blink/web_tests/platform/win/fast/table/border-collapsing/003-vertical-expected.png
index 2a44290f..984468a7e 100644
--- a/third_party/blink/web_tests/platform/win/fast/table/border-collapsing/003-vertical-expected.png
+++ b/third_party/blink/web_tests/platform/win/fast/table/border-collapsing/003-vertical-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/table/border-collapsing/rtl-border-collapsing-expected.png b/third_party/blink/web_tests/platform/win/fast/table/border-collapsing/rtl-border-collapsing-expected.png
index 3baac6a..e1079f5d 100644
--- a/third_party/blink/web_tests/platform/win/fast/table/border-collapsing/rtl-border-collapsing-expected.png
+++ b/third_party/blink/web_tests/platform/win/fast/table/border-collapsing/rtl-border-collapsing-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/paint/invalidation/table/table-cell-collapsed-border-expected.txt b/third_party/blink/web_tests/platform/win/paint/invalidation/table/table-cell-collapsed-border-expected.txt
index e0a6bb5..85ee692 100644
--- a/third_party/blink/web_tests/platform/win/paint/invalidation/table/table-cell-collapsed-border-expected.txt
+++ b/third_party/blink/web_tests/platform/win/paint/invalidation/table/table-cell-collapsed-border-expected.txt
@@ -6,7 +6,7 @@
       "contentsOpaque": true,
       "backgroundColor": "#FFFFFF",
       "invalidations": [
-        [3, 64, 441, 405]
+        [8, 64, 441, 405]
       ]
     }
   ]
diff --git a/third_party/blink/web_tests/platform/win/virtual/dark-color-scheme/fast/loader/plain-text-document-appearance-expected.png b/third_party/blink/web_tests/platform/win/virtual/dark-color-scheme/fast/loader/plain-text-document-appearance-expected.png
new file mode 100644
index 0000000..3d1e57ce
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win/virtual/dark-color-scheme/fast/loader/plain-text-document-appearance-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/text-antialias/international/bidi-layout-across-linebreak-expected.png b/third_party/blink/web_tests/platform/win/virtual/text-antialias/international/bidi-layout-across-linebreak-expected.png
index daf7b77c..50a1460c 100644
--- a/third_party/blink/web_tests/platform/win/virtual/text-antialias/international/bidi-layout-across-linebreak-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/text-antialias/international/bidi-layout-across-linebreak-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/fast/table/backgr_border-table-cell-collapsed-border-expected.png b/third_party/blink/web_tests/platform/win7/fast/table/backgr_border-table-cell-collapsed-border-expected.png
index 1005306..fdb4dcb 100644
--- a/third_party/blink/web_tests/platform/win7/fast/table/backgr_border-table-cell-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/win7/fast/table/backgr_border-table-cell-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/fast/table/backgr_border-table-collapsed-border-expected.png b/third_party/blink/web_tests/platform/win7/fast/table/backgr_border-table-collapsed-border-expected.png
index a352c58..5d57e3e 100644
--- a/third_party/blink/web_tests/platform/win7/fast/table/backgr_border-table-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/win7/fast/table/backgr_border-table-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/fast/table/backgr_border-table-column-collapsed-border-expected.png b/third_party/blink/web_tests/platform/win7/fast/table/backgr_border-table-column-collapsed-border-expected.png
index e56b45d..739e58bb5 100644
--- a/third_party/blink/web_tests/platform/win7/fast/table/backgr_border-table-column-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/win7/fast/table/backgr_border-table-column-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/fast/table/backgr_border-table-column-group-collapsed-border-expected.png b/third_party/blink/web_tests/platform/win7/fast/table/backgr_border-table-column-group-collapsed-border-expected.png
index 5739fb5..6e62d847 100644
--- a/third_party/blink/web_tests/platform/win7/fast/table/backgr_border-table-column-group-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/win7/fast/table/backgr_border-table-column-group-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/fast/table/backgr_border-table-quirks-collapsed-border-expected.png b/third_party/blink/web_tests/platform/win7/fast/table/backgr_border-table-quirks-collapsed-border-expected.png
index e3101c5..65a1674 100644
--- a/third_party/blink/web_tests/platform/win7/fast/table/backgr_border-table-quirks-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/win7/fast/table/backgr_border-table-quirks-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/fast/table/backgr_border-table-row-collapsed-border-expected.png b/third_party/blink/web_tests/platform/win7/fast/table/backgr_border-table-row-collapsed-border-expected.png
index 765fb2af..8eea2783 100644
--- a/third_party/blink/web_tests/platform/win7/fast/table/backgr_border-table-row-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/win7/fast/table/backgr_border-table-row-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/fast/table/backgr_border-table-row-group-collapsed-border-expected.png b/third_party/blink/web_tests/platform/win7/fast/table/backgr_border-table-row-group-collapsed-border-expected.png
index d5ae2c5..70f518e 100644
--- a/third_party/blink/web_tests/platform/win7/fast/table/backgr_border-table-row-group-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/win7/fast/table/backgr_border-table-row-group-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/fast/table/backgr_layers-hide-collapsed-border-expected.png b/third_party/blink/web_tests/platform/win7/fast/table/backgr_layers-hide-collapsed-border-expected.png
index 13f1757..05c02142 100644
--- a/third_party/blink/web_tests/platform/win7/fast/table/backgr_layers-hide-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/win7/fast/table/backgr_layers-hide-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/fast/table/backgr_position-table-cell-collapsed-border-expected.png b/third_party/blink/web_tests/platform/win7/fast/table/backgr_position-table-cell-collapsed-border-expected.png
index 688774b..a335515 100644
--- a/third_party/blink/web_tests/platform/win7/fast/table/backgr_position-table-cell-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/win7/fast/table/backgr_position-table-cell-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/fast/table/backgr_position-table-collapsed-border-expected.png b/third_party/blink/web_tests/platform/win7/fast/table/backgr_position-table-collapsed-border-expected.png
index 55510061..f218cfd 100644
--- a/third_party/blink/web_tests/platform/win7/fast/table/backgr_position-table-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/win7/fast/table/backgr_position-table-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/fast/table/backgr_position-table-column-collapsed-border-expected.png b/third_party/blink/web_tests/platform/win7/fast/table/backgr_position-table-column-collapsed-border-expected.png
index ec746e0..0ff5392 100644
--- a/third_party/blink/web_tests/platform/win7/fast/table/backgr_position-table-column-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/win7/fast/table/backgr_position-table-column-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/fast/table/backgr_position-table-column-group-collapsed-border-expected.png b/third_party/blink/web_tests/platform/win7/fast/table/backgr_position-table-column-group-collapsed-border-expected.png
index 13becee08..8f31574 100644
--- a/third_party/blink/web_tests/platform/win7/fast/table/backgr_position-table-column-group-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/win7/fast/table/backgr_position-table-column-group-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/fast/table/backgr_position-table-row-collapsed-border-expected.png b/third_party/blink/web_tests/platform/win7/fast/table/backgr_position-table-row-collapsed-border-expected.png
index 50d6bd4..f224bc31e 100644
--- a/third_party/blink/web_tests/platform/win7/fast/table/backgr_position-table-row-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/win7/fast/table/backgr_position-table-row-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/fast/table/backgr_position-table-row-group-collapsed-border-expected.png b/third_party/blink/web_tests/platform/win7/fast/table/backgr_position-table-row-group-collapsed-border-expected.png
index 7f84a33..c36af2e 100644
--- a/third_party/blink/web_tests/platform/win7/fast/table/backgr_position-table-row-group-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/win7/fast/table/backgr_position-table-row-group-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/fast/table/backgr_simple-table-cell-collapsed-border-expected.png b/third_party/blink/web_tests/platform/win7/fast/table/backgr_simple-table-cell-collapsed-border-expected.png
index ed4d65c..2ffe4e41 100644
--- a/third_party/blink/web_tests/platform/win7/fast/table/backgr_simple-table-cell-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/win7/fast/table/backgr_simple-table-cell-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/fast/table/backgr_simple-table-collapsed-border-expected.png b/third_party/blink/web_tests/platform/win7/fast/table/backgr_simple-table-collapsed-border-expected.png
index 51b06f736..a8e4c93 100644
--- a/third_party/blink/web_tests/platform/win7/fast/table/backgr_simple-table-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/win7/fast/table/backgr_simple-table-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/fast/table/backgr_simple-table-column-collapsed-border-expected.png b/third_party/blink/web_tests/platform/win7/fast/table/backgr_simple-table-column-collapsed-border-expected.png
index ec83469..ace5cd4 100644
--- a/third_party/blink/web_tests/platform/win7/fast/table/backgr_simple-table-column-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/win7/fast/table/backgr_simple-table-column-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/fast/table/backgr_simple-table-column-group-collapsed-border-expected.png b/third_party/blink/web_tests/platform/win7/fast/table/backgr_simple-table-column-group-collapsed-border-expected.png
index c7a0971..743dbad 100644
--- a/third_party/blink/web_tests/platform/win7/fast/table/backgr_simple-table-column-group-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/win7/fast/table/backgr_simple-table-column-group-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/fast/table/backgr_simple-table-row-collapsed-border-expected.png b/third_party/blink/web_tests/platform/win7/fast/table/backgr_simple-table-row-collapsed-border-expected.png
index a0d3078..d68d67a 100644
--- a/third_party/blink/web_tests/platform/win7/fast/table/backgr_simple-table-row-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/win7/fast/table/backgr_simple-table-row-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/fast/table/backgr_simple-table-row-group-collapsed-border-expected.png b/third_party/blink/web_tests/platform/win7/fast/table/backgr_simple-table-row-group-collapsed-border-expected.png
index fb6bb3a..469ce53b 100644
--- a/third_party/blink/web_tests/platform/win7/fast/table/backgr_simple-table-row-group-collapsed-border-expected.png
+++ b/third_party/blink/web_tests/platform/win7/fast/table/backgr_simple-table-row-group-collapsed-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/virtual/text-antialias/international/bidi-layout-across-linebreak-expected.png b/third_party/blink/web_tests/platform/win7/virtual/text-antialias/international/bidi-layout-across-linebreak-expected.png
index e3e5074..e71339c5 100644
--- a/third_party/blink/web_tests/platform/win7/virtual/text-antialias/international/bidi-layout-across-linebreak-expected.png
+++ b/third_party/blink/web_tests/platform/win7/virtual/text-antialias/international/bidi-layout-across-linebreak-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/resources/gesture-util.js b/third_party/blink/web_tests/resources/gesture-util.js
index ba5d7b9..2f8d8a9 100644
--- a/third_party/blink/web_tests/resources/gesture-util.js
+++ b/third_party/blink/web_tests/resources/gesture-util.js
@@ -150,7 +150,7 @@
   })
 }
 
-function waitForEvent(eventTarget, eventName, timeoutMs = 1000) {
+function waitForEvent(eventTarget, eventName, timeoutMs = 2000) {
   return new Promise((resolve, reject) => {
     const eventListener = (evt) => {
       clearTimeout(timeout);
@@ -165,7 +165,7 @@
   });
 }
 
-function waitForScrollEvent(eventTarget, timeoutMs = 1000) {
+function waitForScrollEvent(eventTarget, timeoutMs = 2000) {
   return waitForEvent(eventTarget, 'scroll', timeoutMs);
 }
 
diff --git a/tools/captured_sites/control.py b/tools/captured_sites/control.py
index 128cbdc..f39829f 100755
--- a/tools/captured_sites/control.py
+++ b/tools/captured_sites/control.py
@@ -9,6 +9,9 @@
   chrome  Starts a Chrome instance with autofill hooks
   wpr     Starts a WPR server instance to record or replay
   run     Starts a test for a single site or "*" for all sites
+  refresh Starts a test for a single site or "*" for all sites, and records new
+          server prediction responses.
+
 Use "captured_sites [command] -h" for more information about each command.',
 
 This script attempts to simplify the various configuration and override options
@@ -87,7 +90,9 @@
 _RUN_DEBUGGING_TESTS = '--gtest_break_on_failure'
 
 _AUTOFILL_TEST = '*/AutofillCapturedSitesInteractiveTest'
+_AUTOFILL_REFRESH = '*/AutofillCapturedSitesRefresh'
 _PASSWORD_MANAGER_TEST = '*/CapturedSitesPasswordManagerBrowserTest'
+_PASSWORD_MANAGER_REFRESH = '*/CapturedSitesPasswordManagerRefresh'
 _VMODULE_AUTOFILL_FILE = 'autofill_captured_sites_interactive_uitest'
 _VMODULE_PASSWORD_FILE = 'password_manager_captured_sites_interactive_uitest'
 
@@ -318,12 +323,22 @@
   _make_process_call(command_args + forward_args, options.print_only)
 
 
+def _launch_refresh(options, forward_args):
+  _launch_test(options, forward_args, _AUTOFILL_REFRESH,
+               _PASSWORD_MANAGER_REFRESH)
+
+
 def _launch_run(options, forward_args):
-  gtest_filter = _AUTOFILL_TEST
+  _launch_test(options, forward_args, _AUTOFILL_TEST, _PASSWORD_MANAGER_TEST)
+
+
+def _launch_test(options, forward_args, gtest_filter_autofill,
+                 gtest_filter_password):
+  gtest_filter = gtest_filter_autofill
   gtest_parameter = options.site_name
   vmodule_name = _VMODULE_AUTOFILL_FILE
   if options.scenario_dir != '':
-    gtest_filter = _PASSWORD_MANAGER_TEST
+    gtest_filter = gtest_filter_password
     gtest_parameter = '%s_%s' % (options.scenario_dir, options.site_name)
     vmodule_name = _VMODULE_PASSWORD_FILE
 
@@ -399,6 +414,10 @@
       Command('Start WPR to replay or record.',
               [_add_wpr_args, _add_shared_args, _add_scenario_site_args],
               _launch_wpr),
+      'refresh':
+      Command('Refresh the Server Predictions of an autofill or password test.',
+              [_add_run_args, _add_shared_args, _add_scenario_site_args],
+              _launch_refresh),
       'run':
       Command('Start an autofill or password test run.',
               [_add_run_args, _add_shared_args, _add_scenario_site_args],
diff --git a/tools/captured_sites/refresh.py b/tools/captured_sites/refresh.py
new file mode 100755
index 0000000..3cbb611
--- /dev/null
+++ b/tools/captured_sites/refresh.py
@@ -0,0 +1,183 @@
+#!/usr/bin/env python3
+# Copyright 2022 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+"""Refreshes existing WPR archive files from live Autofill Server
+
+  $ tools/captured_sites/refresh.py [site_name]
+
+This script attempts to capture the process of refreshing a site's Autofill
+Server Predictions.
+
+It will loop through the given sites and run the refresh process which hits
+the Autofill Server to receive fresh Autofill Server Predictions. It then
+removes the existing WPR file's predictions, and merges in the update ones.
+
+With no arguments or just an '*', the script will run through all non-disabled
+sites in the testcases.json file.
+
+An optional argument of [site_name] can be provided to refresh a single site.
+"""
+
+from __future__ import print_function
+
+import argparse
+import json
+import os
+import signal
+import sys
+import subprocess
+
+_BASE_FOLDER = 'chrome/test/data/autofill/captured_sites'
+_TELEMETRY_BIN_FOLDER = ('third_party/catapult/telemetry/telemetry/bin/'
+                         'linux/x86_64/')
+_TRIMMED_FOLDER = os.path.join(_BASE_FOLDER, 'trimmed')
+_REFRESH_FOLDER = os.path.join(_BASE_FOLDER, 'refresh')
+_MERGED_FOLDER = os.path.join(_BASE_FOLDER, 'merged')
+_PRINT_ONLY = False
+
+
+class Refresh():
+  def collect_sites(self, testcases_file):
+    with open(testcases_file, 'r') as file:
+      content = json.load(file)
+      self.sites = content["tests"]
+    filtered = list(filter(lambda a: 'disabled' not in a, self.sites))
+    return filtered
+
+  def refresh_site(self, site_name):
+    """Run the Refresh test for the given site_name. This process will create
+    a new .wpr archive in the captured_sites/refresh folder. Runs the process
+    with flags:
+       --store-log to keep text log
+       --release to run against release build
+       --background to run with xvfb.py."""
+    command_args = [
+        'tools/captured_sites/control.py', 'refresh', '--store-log',
+        '--release', '--background', site_name
+    ]
+    _make_process_call(command_args, _PRINT_ONLY)
+
+  def delete_existing_predictions(self, site_name):
+    """Use httparchive go tool to remove any existing Server Predictions stored
+    in the current .wpr archive and create a trimmed version in the
+    captured_sites/trimmed folder."""
+    host_domains = ['clients1.google.com', 'content-autofill.googleapis.com']
+    existing_wpr_archive = os.path.join(_BASE_FOLDER, '%s.wpr' % site_name)
+    trimmed_wpr_archive = os.path.join(_TRIMMED_FOLDER, '%s.wpr' % site_name)
+    first_trim = True
+
+    for host_domain in host_domains:
+      to_trim_wpr_archive = trimmed_wpr_archive
+      if first_trim:
+        to_trim_wpr_archive = existing_wpr_archive
+        first_trim = False
+
+      command_args = [
+          _TELEMETRY_BIN_FOLDER + 'httparchive', 'trim', '--host', host_domain,
+          to_trim_wpr_archive, trimmed_wpr_archive
+      ]
+      _make_process_call(command_args, _PRINT_ONLY)
+
+  def merge_new_predictions(self, site_name):
+    """Use httparchive go tool to merge the .wpr file in refresh/ folder with
+    the .wpr file in trimmed/ folder and create a new .wpr file in the
+    merged/ folder."""
+    trimmed_wpr_archive = os.path.join(_TRIMMED_FOLDER, '%s.wpr' % site_name)
+    fresh_wpr_archive = os.path.join(_REFRESH_FOLDER, '%s.wpr' % site_name)
+    merged_wpr_archive = os.path.join(_MERGED_FOLDER, '%s.wpr' % site_name)
+
+    command_args = [
+        _TELEMETRY_BIN_FOLDER + 'httparchive', 'merge', trimmed_wpr_archive,
+        fresh_wpr_archive, merged_wpr_archive
+    ]
+    _make_process_call(command_args, _PRINT_ONLY)
+
+  def update_expectations(self, site_name):
+    """Update .test file expectations to reflect the changes in the newly merged
+    Server Predictions"""
+    cmd = '...'
+    #TODO(crbug.com/1300642)
+    print('Not Implemented')
+
+
+def _parse_args():
+  parser = argparse.ArgumentParser(
+      formatter_class=argparse.RawTextHelpFormatter)
+  parser.usage = __doc__
+  parser.add_argument('site_name',
+                      nargs='?',
+                      default='*',
+                      help=('The site name which should have a match in '
+                            'testcases.json. Use * to indicate all enumerated '
+                            'sites in that file.'))
+  return parser.parse_args()
+
+
+def _make_process_call(command_args, print_only):
+  command_text = ' '.join(command_args)
+  print(command_text)
+  if print_only:
+    return
+
+  if not os.path.exists(command_args[0]):
+    raise EnvironmentError('Cannot locate binary to execute. '
+                           'Ensure that working directory is chromium/src')
+  subprocess.call(command_text, shell=True)
+
+
+def _create_subfolders():
+  assert os.path.isdir(_BASE_FOLDER), ('Expecting path "%s" to exist in your '
+                                       'chromium checkout' % _BASE_FOLDER)
+  if not os.path.isdir(_MERGED_FOLDER):
+    os.mkdir(_MERGED_FOLDER)
+  if not os.path.isdir(_REFRESH_FOLDER):
+    os.mkdir(_REFRESH_FOLDER)
+  if not os.path.isdir(_TRIMMED_FOLDER):
+    os.mkdir(_TRIMMED_FOLDER)
+
+
+def _handle_signal(sig, _):
+  """Handles received signals to make sure spawned test process are killed.
+
+  sig (int): An integer representing the received signal, for example SIGTERM.
+  """
+
+  # Don't do any cleanup here, instead, leave it to the finally blocks.
+  # Assumption is based on https://docs.python.org/3/library/sys.html#sys.exit:
+  # cleanup actions specified by finally clauses of try statements are honored.
+
+  # https://tldp.org/LDP/abs/html/exitcodes.html:
+  # Exit code 128+n -> Fatal error signal "n".
+  print('Signal to quit received')
+  sys.exit(128 + sig)
+
+
+def main():
+  for sig in (signal.SIGTERM, signal.SIGINT):
+    signal.signal(sig, _handle_signal)
+
+  _create_subfolders()
+
+  options = _parse_args()
+
+  r = Refresh()
+
+  if options.site_name == '*':
+    sites = r.collect_sites(os.path.join(_BASE_FOLDER, 'testcases.json'))
+    print('Refreshing %d sites from the testcases file' % len(sites))
+  else:
+    sites = [{'site_name': options.site_name}]
+    print('Refreshing single site "%s"' % options.site_name)
+
+  for site in sites:
+    site_name = site['site_name']
+    print('Refreshing Server Predictions for "%s"' % site_name)
+    r.refresh_site(site_name)
+    r.delete_existing_predictions(site_name)
+    r.merge_new_predictions(site_name)
+  print('Merged WPR archives have been written to "%s"' % _MERGED_FOLDER)
+
+
+if __name__ == '__main__':
+  sys.exit(main())
diff --git a/tools/crbug/pinpoint.js b/tools/crbug/pinpoint.js
index 11f8512b..4e8d9d9 100644
--- a/tools/crbug/pinpoint.js
+++ b/tools/crbug/pinpoint.js
@@ -40,11 +40,16 @@
   listJobs(useremail) {
     const args =
         ['pinpoint', 'list-jobs', '--json', '--filter', `user=${useremail}`];
-    const stdout = process.execSync(args.join(' ')).toString().trim();
-    const json = JSON.parse(stdout);
-    if (json) {
-      const jobs = json.map(j => new PinpointJob(j));
-      return jobs;
+    for (let tries = 0; tries < 3; ++tries) {
+      try {
+        const stdout = process.execSync(args.join(' ')).toString().trim();
+        const json = JSON.parse(stdout);
+        if (json) {
+          const jobs = json.map(j => new PinpointJob(j));
+          return jobs;
+        }
+      } catch (ex) {
+      }
     }
     return [];
   }
diff --git a/tools/grit/grit/format/resource_map.py b/tools/grit/grit/format/resource_map.py
index ed27ff22..2a9e8d69 100644
--- a/tools/grit/grit/format/resource_map.py
+++ b/tools/grit/grit/format/resource_map.py
@@ -85,7 +85,7 @@
 
 #include <stddef.h>
 
-#include "base/cxx17_backports.h"
+#include <iterator>
 
 #include "%(rc_header_file)s"
 
@@ -102,8 +102,10 @@
   return '''\
 };
 
-const size_t %(map_name)sSize = base::size(%(map_name)s);
-''' % { 'map_name': GetMapName(root) }
+const size_t %(map_name)sSize = std::size(%(map_name)s);
+''' % {
+      'map_name': GetMapName(root)
+  }
 
 
 def _FormatSource(get_key, root, lang, output_dir):
diff --git a/tools/grit/grit/format/resource_map_unittest.py b/tools/grit/grit/format/resource_map_unittest.py
index 61d2f28..c34cf354 100755
--- a/tools/grit/grit/format/resource_map_unittest.py
+++ b/tools/grit/grit/format/resource_map_unittest.py
@@ -63,7 +63,7 @@
         '''\
 #include "the_resource_map_header.h"
 #include <stddef.h>
-#include "base/cxx17_backports.h"
+#include <iterator>
 #include "the_rc_header.h"
 const webui::ResourcePath kTheRcHeader[] = {
   {"IDC_KLONKMENU", IDC_KLONKMENU},
@@ -72,14 +72,14 @@
   {"IDS_LANGUAGESPECIFIC", IDS_LANGUAGESPECIFIC},
   {"IDS_THIRDPRESENT", IDS_THIRDPRESENT},
 };
-const size_t kTheRcHeaderSize = base::size(kTheRcHeader);''', output)
+const size_t kTheRcHeaderSize = std::size(kTheRcHeader);''', output)
     output = util.StripBlankLinesAndComments(''.join(
         resource_map.GetFormatter('resource_file_map_source')(grd, 'en', '.')))
     self.assertEqual(
         '''\
 #include "the_resource_map_header.h"
 #include <stddef.h>
-#include "base/cxx17_backports.h"
+#include <iterator>
 #include "the_rc_header.h"
 const webui::ResourcePath kTheRcHeader[] = {
   {"grit/testdata/klonk.rc", IDC_KLONKMENU},
@@ -88,7 +88,7 @@
   {"ghi", IDS_LANGUAGESPECIFIC},
   {"mno", IDS_THIRDPRESENT},
 };
-const size_t kTheRcHeaderSize = base::size(kTheRcHeader);''', output)
+const size_t kTheRcHeaderSize = std::size(kTheRcHeader);''', output)
 
   def testFormatResourceMapWithGeneratedFile(self):
     os.environ["root_gen_dir"] = "gen"
@@ -170,7 +170,7 @@
         '''\
 #include "the_resource_map_header.h"
 #include <stddef.h>
-#include "base/cxx17_backports.h"
+#include <iterator>
 #include "the_rc_header.h"
 const webui::ResourcePath kTheRcHeader[] = {
   {"IDR_KLONKMENU", IDR_KLONKMENU},
@@ -178,14 +178,14 @@
   {"IDR_METEOR", IDR_METEOR},
   {"IDR_LAST", IDR_LAST},
 };
-const size_t kTheRcHeaderSize = base::size(kTheRcHeader);''', output)
+const size_t kTheRcHeaderSize = std::size(kTheRcHeader);''', output)
     output = util.StripBlankLinesAndComments(''.join(
         resource_map.GetFormatter('resource_map_source')(grd, 'en', '.')))
     self.assertEqual(
         '''\
 #include "the_resource_map_header.h"
 #include <stddef.h>
-#include "base/cxx17_backports.h"
+#include <iterator>
 #include "the_rc_header.h"
 const webui::ResourcePath kTheRcHeader[] = {
   {"IDR_KLONKMENU", IDR_KLONKMENU},
@@ -193,7 +193,7 @@
   {"IDR_METEOR", IDR_METEOR},
   {"IDR_LAST", IDR_LAST},
 };
-const size_t kTheRcHeaderSize = base::size(kTheRcHeader);''', output)
+const size_t kTheRcHeaderSize = std::size(kTheRcHeader);''', output)
 
   def testFormatResourceMapWithOutputAllEqualsFalseForIncludes(self):
     grd = util.ParseGrdForUnittest('''
@@ -246,7 +246,7 @@
         '''\
 #include "the_resource_map_header.h"
 #include <stddef.h>
-#include "base/cxx17_backports.h"
+#include <iterator>
 #include "the_rc_header.h"
 const webui::ResourcePath kTheRcHeader[] = {
   {"IDC_KLONKMENU", IDC_KLONKMENU},
@@ -256,14 +256,14 @@
   {"IDS_METEOR", IDS_METEOR},
   {"IDS_LAST", IDS_LAST},
 };
-const size_t kTheRcHeaderSize = base::size(kTheRcHeader);''', output)
+const size_t kTheRcHeaderSize = std::size(kTheRcHeader);''', output)
     output = util.StripBlankLinesAndComments(''.join(
         resource_map.GetFormatter('resource_file_map_source')(grd, 'en', '.')))
     self.assertEqual(
         '''\
 #include "the_resource_map_header.h"
 #include <stddef.h>
-#include "base/cxx17_backports.h"
+#include <iterator>
 #include "the_rc_header.h"
 const webui::ResourcePath kTheRcHeader[] = {
   {"grit/testdata/klonk.rc", IDC_KLONKMENU},
@@ -273,7 +273,7 @@
   {"meteor", IDS_METEOR},
   {"xyz", IDS_LAST},
 };
-const size_t kTheRcHeaderSize = base::size(kTheRcHeader);''', output)
+const size_t kTheRcHeaderSize = std::size(kTheRcHeader);''', output)
 
   def testFormatStringResourceMap(self):
     grd = util.ParseGrdForUnittest('''
@@ -316,13 +316,13 @@
         '''\
 #include "the_rc_map_header.h"
 #include <stddef.h>
-#include "base/cxx17_backports.h"
+#include <iterator>
 #include "the_rc_header.h"
 const webui::ResourcePath kTheRcHeader[] = {
   {"IDS_PRODUCT_NAME", IDS_PRODUCT_NAME},
   {"IDS_DEFAULT_TAB_TITLE_TITLE_CASE", IDS_DEFAULT_TAB_TITLE_TITLE_CASE},
 };
-const size_t kTheRcHeaderSize = base::size(kTheRcHeader);''', output)
+const size_t kTheRcHeaderSize = std::size(kTheRcHeader);''', output)
 
 
 if __name__ == '__main__':
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl
index 4a5e3e05..3f4116b 100644
--- a/tools/mb/mb_config.pyl
+++ b/tools/mb/mb_config.pyl
@@ -836,6 +836,8 @@
       'android-10-arm64-rel': 'android_release_trybot_arm64_fastbuild_webview_trichrome',
       'android-11-x86-rel': 'android_release_trybot_x86_fastbuild_webview_trichrome',
       'android-12-x64-fyi-rel': 'android_release_trybot_x64_fastbuild_webview_trichrome',
+      'android-12-x64-dbg': 'android_debug_trybot_x64_webview_trichrome_webview_shell',
+      'android-webview-12-x64-dbg': 'android_debug_trybot_x64_webview_trichrome_webview_shell',
       'android-webview-marshmallow-arm64-dbg': 'android_release_trybot_arm64_webview_google',
       'android-webview-nougat-arm64-dbg': 'android_release_trybot_arm64_webview_monochrome',
       'android-webview-oreo-arm64-dbg': 'android_release_trybot_arm64_webview_monochrome',
@@ -1147,15 +1149,22 @@
 
     'tryserver.chromium.perf': {
       'Android Compile Perf': 'official_goma_minimal_symbols_android',
+      'Android Compile Perf PGO': 'official_goma_minimal_symbols_android_pgo',
       'Android arm64 Compile Perf': 'official_goma_minimal_symbols_android_arm64',
+      'Android arm64 Compile Perf PGO': 'official_goma_minimal_symbols_android_arm64_pgo',
       'Chromeos Amd64 Generic Lacros Builder Perf': 'chromeos_amd64-generic_lacros_official',
       'Fuchsia Builder Perf': 'official_goma_fuchsia_arm64_perf',
       'Fuchsia Builder Perf x64': 'official_goma_fuchsia_x64_perf',
       'Linux Builder Perf': 'official_goma_linux_perf',
+      'Linux Builder Perf PGO': 'official_goma_linux_perf_pgo',
       'Mac Builder Perf': 'official_goma_mac_perf',
+      'Mac Builder Perf PGO': 'official_goma_mac_perf_pgo',
       'Mac arm Builder Perf': 'official_goma_mac_arm_perf',
+      'Mac arm Builder Perf PGO': 'official_goma_mac_arm_perf_pgo',
       'Win Builder Perf': 'official_goma_x86_perf',
+      'Win Builder Perf PGO': 'official_goma_x86_perf_pgo',
       'Win x64 Builder Perf': 'official_goma_perf',
+      'Win x64 Builder Perf PGO': 'official_goma_perf_pgo',
     },
 
     'tryserver.chromium.rust': {
@@ -1444,6 +1453,10 @@
       'android', 'debug_bot', 'use_java_coverage', 'strip_debug_info',
     ],
 
+    'android_debug_trybot_x64_webview_trichrome_webview_shell': [
+      'android', 'debug_bot', 'x64', 'webview_trichrome', 'webview_shell',
+    ],
+
     'android_incremental_debug_bot_reclient': [
       'android', 'incremental', 'debug_bot_reclient',
     ],
@@ -2797,14 +2810,26 @@
       'official', 'goma', 'minimal_symbols', 'pgo_phase_0',
     ],
 
+    'official_goma_perf_pgo': [
+      'official', 'goma', 'minimal_symbols', 'pgo_phase_1',
+    ],
+
     'official_goma_mac_perf': [
       'official', 'goma', 'no_keystone_registration_framework', 'no_widevine_cdm_host_verification', 'full_symbols', 'pgo_phase_0',
     ],
 
+    'official_goma_mac_perf_pgo': [
+      'official', 'goma', 'no_keystone_registration_framework', 'no_widevine_cdm_host_verification', 'full_symbols', 'pgo_phase_1',
+    ],
+
     'official_goma_mac_arm_perf': [
       'official', 'goma', 'no_keystone_registration_framework', 'no_widevine_cdm_host_verification', 'full_symbols', 'arm64', 'pgo_phase_0',
     ],
 
+    'official_goma_mac_arm_perf_pgo': [
+      'official', 'goma', 'no_keystone_registration_framework', 'no_widevine_cdm_host_verification', 'full_symbols', 'arm64', 'pgo_phase_1',
+    ],
+
     'official_goma_mac_arm_pgo': [
       'official', 'goma', 'no_keystone_registration_framework', 'no_widevine_cdm_host_verification', 'arm64', 'disable_widevine_signing', 'static', 'no_symbols', 'pgo_phase_1'
     ],
@@ -2817,6 +2842,10 @@
       'official', 'goma', 'no_gnome_keyring', 'minimal_symbols', 'pgo_phase_0',
     ],
 
+    'official_goma_linux_perf_pgo': [
+      'official', 'goma', 'no_gnome_keyring', 'minimal_symbols', 'pgo_phase_1',
+    ],
+
     'official_goma_fuchsia_arm64_perf': [
       'official', 'goma', 'minimal_symbols', 'fuchsia', 'fuchsia_include_sd_images', 'arm64', 'ffmpeg_branding_chrome', 'proprietary_codecs', 'test_isolate_no_emulator'
     ],
@@ -2836,10 +2865,18 @@
       'official', 'goma', 'minimal_symbols', 'android', 'pgo_phase_0', 'no_default_afdo'
     ],
 
+    'official_goma_minimal_symbols_android_pgo': [
+      'official', 'goma', 'minimal_symbols', 'android', 'pgo_phase_1', 'no_default_afdo'
+    ],
+
     'official_goma_minimal_symbols_android_arm64': [
       'official', 'goma', 'minimal_symbols', 'android', 'arm64', 'pgo_phase_0', 'no_default_afdo'
     ],
 
+    'official_goma_minimal_symbols_android_arm64_pgo': [
+      'official', 'goma', 'minimal_symbols', 'android', 'arm64', 'pgo_phase_1', 'no_default_afdo'
+    ],
+
     'official_goma_minimal_symbols_android_thin_lto_opt': [
       'official', 'goma', 'minimal_symbols', 'android', 'thin_lto_opt', 'no_default_afdo'
     ],
@@ -2864,6 +2901,10 @@
       'official', 'goma', 'x86', 'minimal_symbols', 'pgo_phase_0',
     ],
 
+    'official_goma_x86_perf_pgo': [
+      'official', 'goma', 'x86', 'minimal_symbols', 'pgo_phase_1',
+    ],
+
     'official_goma_x86_pgo': [
       'official', 'goma', 'x86', 'static', 'no_symbols', 'pgo_phase_1', 'no_resource_allowlisting',
     ],
diff --git a/tools/mb/mb_config_expectations/tryserver.chromium.android.json b/tools/mb/mb_config_expectations/tryserver.chromium.android.json
index 0bf3ace..d5f597db 100644
--- a/tools/mb/mb_config_expectations/tryserver.chromium.android.json
+++ b/tools/mb/mb_config_expectations/tryserver.chromium.android.json
@@ -35,6 +35,20 @@
       "use_goma": true
     }
   },
+  "android-12-x64-dbg": {
+    "gn_args": {
+      "ffmpeg_branding": "Chrome",
+      "is_component_build": true,
+      "is_debug": true,
+      "proprietary_codecs": true,
+      "symbol_level": 1,
+      "system_webview_package_name": "com.google.android.webview.debug",
+      "system_webview_shell_package_name": "com.google.android.webview_shell",
+      "target_cpu": "x64",
+      "target_os": "android",
+      "use_goma": true
+    }
+  },
   "android-12-x64-fyi-rel": {
     "gn_args": {
       "blink_enable_generated_code_formatting": false,
@@ -863,6 +877,20 @@
       "use_goma": true
     }
   },
+  "android-webview-12-x64-dbg": {
+    "gn_args": {
+      "ffmpeg_branding": "Chrome",
+      "is_component_build": true,
+      "is_debug": true,
+      "proprietary_codecs": true,
+      "symbol_level": 1,
+      "system_webview_package_name": "com.google.android.webview.debug",
+      "system_webview_shell_package_name": "com.google.android.webview_shell",
+      "target_cpu": "x64",
+      "target_os": "android",
+      "use_goma": true
+    }
+  },
   "android-webview-marshmallow-arm64-dbg": {
     "gn_args": {
       "blink_enable_generated_code_formatting": false,
diff --git a/tools/mb/mb_config_expectations/tryserver.chromium.perf.json b/tools/mb/mb_config_expectations/tryserver.chromium.perf.json
index ed60412c..1dc4a834 100644
--- a/tools/mb/mb_config_expectations/tryserver.chromium.perf.json
+++ b/tools/mb/mb_config_expectations/tryserver.chromium.perf.json
@@ -13,6 +13,20 @@
       "use_goma": true
     }
   },
+  "Android Compile Perf PGO": {
+    "gn_args": {
+      "chrome_pgo_phase": 1,
+      "clang_use_default_sample_profile": false,
+      "ffmpeg_branding": "Chrome",
+      "is_chrome_branded": true,
+      "is_official_build": true,
+      "proprietary_codecs": true,
+      "strip_absolute_paths_from_debug_symbols": true,
+      "symbol_level": 1,
+      "target_os": "android",
+      "use_goma": true
+    }
+  },
   "Android arm64 Compile Perf": {
     "gn_args": {
       "chrome_pgo_phase": 0,
@@ -28,6 +42,21 @@
       "use_goma": true
     }
   },
+  "Android arm64 Compile Perf PGO": {
+    "gn_args": {
+      "chrome_pgo_phase": 1,
+      "clang_use_default_sample_profile": false,
+      "ffmpeg_branding": "Chrome",
+      "is_chrome_branded": true,
+      "is_official_build": true,
+      "proprietary_codecs": true,
+      "strip_absolute_paths_from_debug_symbols": true,
+      "symbol_level": 1,
+      "target_cpu": "arm64",
+      "target_os": "android",
+      "use_goma": true
+    }
+  },
   "Chromeos Amd64 Generic Lacros Builder Perf": {
     "args_file": "//build/args/chromeos/amd64-generic-crostoolchain.gni",
     "gn_args": {
@@ -91,6 +120,17 @@
       "use_goma": true
     }
   },
+  "Linux Builder Perf PGO": {
+    "gn_args": {
+      "chrome_pgo_phase": 1,
+      "is_chrome_branded": true,
+      "is_official_build": true,
+      "strip_absolute_paths_from_debug_symbols": true,
+      "symbol_level": 1,
+      "use_gnome_keyring": false,
+      "use_goma": true
+    }
+  },
   "Mac Builder Perf": {
     "gn_args": {
       "chrome_pgo_phase": 0,
@@ -103,6 +143,18 @@
       "use_goma": true
     }
   },
+  "Mac Builder Perf PGO": {
+    "gn_args": {
+      "chrome_pgo_phase": 1,
+      "enable_keystone_registration_framework": false,
+      "ignore_missing_widevine_signing_cert": true,
+      "is_chrome_branded": true,
+      "is_official_build": true,
+      "strip_absolute_paths_from_debug_symbols": true,
+      "symbol_level": 2,
+      "use_goma": true
+    }
+  },
   "Mac arm Builder Perf": {
     "gn_args": {
       "chrome_pgo_phase": 0,
@@ -116,6 +168,19 @@
       "use_goma": true
     }
   },
+  "Mac arm Builder Perf PGO": {
+    "gn_args": {
+      "chrome_pgo_phase": 1,
+      "enable_keystone_registration_framework": false,
+      "ignore_missing_widevine_signing_cert": true,
+      "is_chrome_branded": true,
+      "is_official_build": true,
+      "strip_absolute_paths_from_debug_symbols": true,
+      "symbol_level": 2,
+      "target_cpu": "arm64",
+      "use_goma": true
+    }
+  },
   "Win Builder Perf": {
     "gn_args": {
       "chrome_pgo_phase": 0,
@@ -127,6 +192,17 @@
       "use_goma": true
     }
   },
+  "Win Builder Perf PGO": {
+    "gn_args": {
+      "chrome_pgo_phase": 1,
+      "is_chrome_branded": true,
+      "is_official_build": true,
+      "strip_absolute_paths_from_debug_symbols": true,
+      "symbol_level": 1,
+      "target_cpu": "x86",
+      "use_goma": true
+    }
+  },
   "Win x64 Builder Perf": {
     "gn_args": {
       "chrome_pgo_phase": 0,
@@ -136,5 +212,15 @@
       "symbol_level": 1,
       "use_goma": true
     }
+  },
+  "Win x64 Builder Perf PGO": {
+    "gn_args": {
+      "chrome_pgo_phase": 1,
+      "is_chrome_branded": true,
+      "is_official_build": true,
+      "strip_absolute_paths_from_debug_symbols": true,
+      "symbol_level": 1,
+      "use_goma": true
+    }
   }
 }
\ No newline at end of file
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml
index 5c5a10a..b7539eb 100644
--- a/tools/metrics/actions/actions.xml
+++ b/tools/metrics/actions/actions.xml
@@ -16912,7 +16912,6 @@
 
 <action name="MobileCloseAllTabsDialog.Cancelled">
   <owner>ckitagawa@chromium.org</owner>
-  <owner>yashard@chromium.org</owner>
   <owner>fredmello@chromium.org</owner>
   <description>
     Recorded when user cancelled closing all tabs with the cancel button.
@@ -16921,7 +16920,6 @@
 
 <action name="MobileCloseAllTabsDialog.CancelledWithTouchOutside">
   <owner>ckitagawa@chromium.org</owner>
-  <owner>yashard@chromium.org</owner>
   <owner>fredmello@chromium.org</owner>
   <description>
     Recorded when user cancelled closing all tabs by tapping outside the dialog.
@@ -16930,7 +16928,6 @@
 
 <action name="MobileCloseAllTabsDialog.ClosedAllTabs">
   <owner>ckitagawa@chromium.org</owner>
-  <owner>yashard@chromium.org</owner>
   <owner>fredmello@chromium.org</owner>
   <description>
     Recorded when user closed all tabs from the close all tabs dialog.
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index fa34500..c403deb0 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -1941,6 +1941,13 @@
   <int value="16" label="NEW_DOWNLOAD_TAB_OPEN_BUTTON"/>
 </enum>
 
+<enum name="AndroidDragTargetType">
+  <int value="0" label="Invalid"/>
+  <int value="1" label="Text"/>
+  <int value="2" label="Image"/>
+  <int value="3" label="Link"/>
+</enum>
+
 <enum name="AndroidEnhancedProtectionPromoAction">
   <int value="0" label="Promo created">
     The Android Safe Browsing Enhanced Protection promo was created on the new
diff --git a/tools/metrics/histograms/metadata/METRIC_REVIEWER_OWNERS b/tools/metrics/histograms/metadata/METRIC_REVIEWER_OWNERS
index cac6fc56..7dffdfa 100644
--- a/tools/metrics/histograms/metadata/METRIC_REVIEWER_OWNERS
+++ b/tools/metrics/histograms/metadata/METRIC_REVIEWER_OWNERS
@@ -91,7 +91,6 @@
 wychen@chromium.org
 xiaohuic@chromium.org
 xinghuilu@chromium.org
-yashard@chromium.org
 yigu@chromium.org
 yuhsuan@chromium.org
 zentaro@chromium.org
diff --git a/tools/metrics/histograms/metadata/android/histograms.xml b/tools/metrics/histograms/metadata/android/histograms.xml
index 0774e690..0b36217ce 100644
--- a/tools/metrics/histograms/metadata/android/histograms.xml
+++ b/tools/metrics/histograms/metadata/android/histograms.xml
@@ -957,6 +957,38 @@
   </summary>
 </histogram>
 
+<histogram name="Android.DragDrop.FromWebContent.Duration{DropResult}"
+    units="ms" expires_after="2022-07-01">
+  <owner>wenyufu@chromium.org</owner>
+  <owner>clank-large-form-factors@google.com</owner>
+  <summary>
+    Records the duration in ms of drag process when {DropResult}. Recorded for
+    the drag process starts from the web content, and drop happens outside of
+    the web content. Does not capture drag events that end inside the
+    ContentView containing web contents. Recorded when such a drag event ends.
+    Android only.
+
+    For more information regarding drag results, see:
+    https://developer.android.com/reference/android/view/DragEvent#getResult()
+  </summary>
+  <token key="DropResult">
+    <variant name=".Canceled" summary="DragEvent#getResult() returns false."/>
+    <variant name=".Success" summary="DragEvent#getResult() returns true."/>
+  </token>
+</histogram>
+
+<histogram name="Android.DragDrop.FromWebContent.TargetType"
+    enum="AndroidDragTargetType" expires_after="2022-07-01">
+  <owner>wenyufu@chromium.org</owner>
+  <owner>clank-large-form-factors@google.com</owner>
+  <summary>
+    Records the type of object being dragged, for the drag process starts from
+    the web content, and drop happens outside of the web content. Does not
+    capture drag events that end inside the ContentView containing web contents.
+    Recorded when such a drag event ends. Android only.
+  </summary>
+</histogram>
+
 <histogram name="Android.DynamicColors.IsAvailable" enum="Boolean"
     expires_after="2023-02-02">
   <owner>skym@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/autofill/histograms.xml b/tools/metrics/histograms/metadata/autofill/histograms.xml
index 56fdad0..c043fa2 100644
--- a/tools/metrics/histograms/metadata/autofill/histograms.xml
+++ b/tools/metrics/histograms/metadata/autofill/histograms.xml
@@ -124,6 +124,7 @@
   <variant name="Otp" summary="OTP auth only"/>
   <variant name="OtpFallbackFromFido"
       summary="OTP fallback from FIDO authentication"/>
+  <variant name="UnspecifiedFlowType" summary="No authentication specified"/>
 </variants>
 
 <variants name="AutofillCreditCardWebauthnOptInOrigin">
@@ -1961,7 +1962,7 @@
 </histogram>
 
 <histogram name="Autofill.OfferNotificationBubbleOffer.{BubbleType}"
-    enum="BooleanPreviouslyShown" expires_after="2022-04-01">
+    enum="BooleanPreviouslyShown" expires_after="2023-05-01">
   <owner>siyua@chromium.org</owner>
   <owner>jsaul@google.com</owner>
   <owner>payments-autofill-team@google.com</owner>
@@ -1975,7 +1976,7 @@
 
 <histogram
     name="Autofill.OfferNotificationBubblePromoCodeButtonClicked.{BubbleType}"
-    enum="BooleanClicked" expires_after="2022-04-01">
+    enum="BooleanClicked" expires_after="2023-05-01">
   <owner>jsaul@google.com</owner>
   <owner>siyua@chromium.org</owner>
   <owner>payments-autofill-team@google.com</owner>
@@ -1989,7 +1990,7 @@
 
 <histogram
     name="Autofill.OfferNotificationBubbleResult.{BubbleType}.{ShowType}"
-    enum="AutofillOfferNotificationBubbleResult" expires_after="2022-04-01">
+    enum="AutofillOfferNotificationBubbleResult" expires_after="2023-05-01">
   <owner>siyua@chromium.org</owner>
   <owner>jsaul@google.com</owner>
   <owner>payments-autofill-team@google.com</owner>
@@ -2002,9 +2003,10 @@
 </histogram>
 
 <histogram name="Autofill.OfferNotificationBubbleSuppressed.{BubbleType}"
-    enum="BooleanSuppressed" expires_after="2022-04-01">
+    enum="BooleanSuppressed" expires_after="2023-05-01">
   <owner>jsaul@google.com</owner>
   <owner>yuezhanggg@chromium.org</owner>
+  <owner>siyua@chromium.org</owner>
   <owner>payments-autofill-team@google.com</owner>
   <owner>chrome-shopping@google.com</owner>
   <summary>
@@ -2017,7 +2019,7 @@
 </histogram>
 
 <histogram name="Autofill.OfferNotificationInfoBarOffer.{OfferType}"
-    enum="BooleanShown" expires_after="2022-04-01">
+    enum="BooleanShown" expires_after="2023-05-01">
   <owner>siashah@chromium.org</owner>
   <owner>siyua@chromium.org</owner>
   <owner>payments-autofill-team@google.com</owner>
@@ -2029,7 +2031,7 @@
 </histogram>
 
 <histogram name="Autofill.OfferNotificationInfoBarResult.{OfferType}"
-    enum="AutofillOfferNotificationInfoBarResult" expires_after="2022-04-01">
+    enum="AutofillOfferNotificationInfoBarResult" expires_after="2023-05-01">
   <owner>siashah@chromium.org</owner>
   <owner>siyua@chromium.org</owner>
   <owner>payments-autofill-team@google.com</owner>
@@ -2463,7 +2465,7 @@
 </histogram>
 
 <histogram name="Autofill.ProgressDialog.{FlowType}.Result"
-    enum="BooleanCanceled" expires_after="2022-04-01">
+    enum="BooleanCanceled" expires_after="2023-04-01">
   <owner>siashah@chromium.org</owner>
   <owner>siyua@chromium.org</owner>
   <owner>jsaul@google.com</owner>
@@ -2482,7 +2484,7 @@
 </histogram>
 
 <histogram name="Autofill.ProgressDialog.{FlowType}.Shown" enum="BooleanShown"
-    expires_after="2022-04-01">
+    expires_after="2023-04-01">
   <owner>siashah@chromium.org</owner>
   <owner>siyua@chromium.org</owner>
   <owner>jsaul@google.com</owner>
@@ -2676,7 +2678,7 @@
 </histogram>
 
 <histogram name="Autofill.ServerCardUnmask.{CardType}.Attempt"
-    enum="BooleanAttempted" expires_after="2022-04-01">
+    enum="BooleanAttempted" expires_after="2023-06-01">
   <owner>siyua@chromium.org</owner>
   <owner>payments-autofill-team@google.com</owner>
   <summary>
@@ -2687,7 +2689,7 @@
 </histogram>
 
 <histogram name="Autofill.ServerCardUnmask.{CardType}.FormSubmission"
-    enum="BooleanSubmitted" expires_after="2022-04-01">
+    enum="BooleanSubmitted" expires_after="2023-06-01">
   <owner>siyua@chromium.org</owner>
   <owner>payments-autofill-team@google.com</owner>
   <summary>
@@ -2698,7 +2700,7 @@
 </histogram>
 
 <histogram name="Autofill.ServerCardUnmask.{CardType}.Result.{FlowType}"
-    enum="AutofillServerCardUnmaskResult" expires_after="2022-04-01">
+    enum="AutofillServerCardUnmaskResult" expires_after="2023-06-01">
   <owner>siyua@chromium.org</owner>
   <owner>payments-autofill-team@google.com</owner>
   <summary>
@@ -2763,7 +2765,7 @@
 </histogram>
 
 <histogram name="Autofill.StoredCreditCardCount.Server.WithCardArtImage"
-    units="units" expires_after="2022-04-01">
+    units="units" expires_after="2022-12-12">
   <owner>siyua@chromium.org</owner>
   <owner>payments-autofill-team@google.com</owner>
   <summary>
@@ -2773,7 +2775,7 @@
 </histogram>
 
 <histogram name="Autofill.StoredCreditCardCount.Server.WithVirtualCardMetadata"
-    units="units" expires_after="2022-08-07">
+    units="units" expires_after="2022-12-12">
   <owner>siyua@chromium.org</owner>
   <owner>payments-autofill-team@google.com</owner>
   <summary>
@@ -3274,7 +3276,7 @@
 </histogram>
 
 <histogram name="Autofill.UsedCachedVirtualCard" units="uses"
-    expires_after="2022-04-01">
+    expires_after="2022-06-26">
   <owner>jsaul@google.com</owner>
   <owner>siyua@chromium.org</owner>
   <owner>payments-autofill-team@google.com</owner>
@@ -3384,7 +3386,7 @@
 
 <histogram name="Autofill.VirtualCardManualFallbackBubble.FieldClicked"
     enum="AutofillVirtualCardManualFallbackBubbleFieldClicked"
-    expires_after="2022-04-01">
+    expires_after="2023-04-01">
   <owner>siyua@chromium.org</owner>
   <owner>jsaul@google.com</owner>
   <owner>payments-autofill-team@google.com</owner>
@@ -3397,7 +3399,7 @@
 
 <histogram name="Autofill.VirtualCardManualFallbackBubble.Result.{ShowType}"
     enum="AutofillVirtualCardManualFallbackBubbleResult"
-    expires_after="2022-04-01">
+    expires_after="2023-04-01">
   <owner>siyua@chromium.org</owner>
   <owner>jsaul@google.com</owner>
   <owner>payments-autofill-team@google.com</owner>
@@ -3409,7 +3411,7 @@
 </histogram>
 
 <histogram name="Autofill.VirtualCardManualFallbackBubble.Shown"
-    enum="BooleanPreviouslyShown" expires_after="2022-07-31">
+    enum="BooleanPreviouslyShown" expires_after="2023-04-01">
   <owner>siyua@chromium.org</owner>
   <owner>jsaul@google.com</owner>
   <owner>payments-autofill-team@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/blink/histograms.xml b/tools/metrics/histograms/metadata/blink/histograms.xml
index 36899a4..25cbaf2f 100644
--- a/tools/metrics/histograms/metadata/blink/histograms.xml
+++ b/tools/metrics/histograms/metadata/blink/histograms.xml
@@ -750,6 +750,26 @@
   </summary>
 </histogram>
 
+<histogram name="Blink.FedCm.ApprovedClientsExistence" enum="Boolean"
+    expires_after="M110">
+  <owner>yigu@chromium.org</owner>
+  <owner>fedcm-core@google.com</owner>
+  <summary>
+    Records whether an IDP returns an approved clients list in the response.
+    Records at most one sample per API call.
+  </summary>
+</histogram>
+
+<histogram name="Blink.FedCm.ApprovedClientsSize" units="clients"
+    expires_after="M110">
+  <owner>yigu@chromium.org</owner>
+  <owner>fedcm-core@google.com</owner>
+  <summary>
+    Records the size of the approved clients list returned by IDP if applicable.
+    Records at most one sample per API call.
+  </summary>
+</histogram>
+
 <histogram name="Blink.FedCm.IsSignInUser" enum="Boolean" expires_after="M110">
   <owner>yigu@chromium.org</owner>
   <owner>fedcm-core@google.com</owner>
@@ -762,7 +782,7 @@
 <histogram name="Blink.FedCm.Status.RequestIdToken"
     enum="FedCmRequestIdTokenStatus" expires_after="M110">
   <owner>yigu@chromium.org</owner>
-  <owner>webid-core@google.com</owner>
+  <owner>fedcm-core@google.com</owner>
   <summary>
     Records the status of a request id token call to the FedCM API.
   </summary>
@@ -771,14 +791,14 @@
 <histogram name="Blink.FedCm.Status.Revoke" enum="FedCmRevokeStatus"
     expires_after="M110">
   <owner>yigu@chromium.org</owner>
-  <owner>webid-core@google.com</owner>
+  <owner>fedcm-core@google.com</owner>
   <summary>Records the status of a revoke call to the FedCM API.</summary>
 </histogram>
 
 <histogram name="Blink.FedCm.Timing.CancelOnDialog" units="ms"
     expires_after="M110">
   <owner>yigu@chromium.org</owner>
-  <owner>webid-core@google.com</owner>
+  <owner>fedcm-core@google.com</owner>
   <summary>
     Records the time from when the accounts dialog is shown to when the user
     closes the dialog without selecting any account. Only records a sample when
@@ -789,7 +809,7 @@
 <histogram name="Blink.FedCm.Timing.ContinueOnDialog" units="ms"
     expires_after="M110">
   <owner>yigu@chromium.org</owner>
-  <owner>webid-core@google.com</owner>
+  <owner>fedcm-core@google.com</owner>
   <summary>
     Records the time from when the accounts dialog is shown to when the user
     presses the Continue button. Only records a sample when the user sees and
@@ -800,7 +820,7 @@
 <histogram name="Blink.FedCm.Timing.IdTokenResponse" units="ms"
     expires_after="M110">
   <owner>yigu@chromium.org</owner>
-  <owner>webid-core@google.com</owner>
+  <owner>fedcm-core@google.com</owner>
   <summary>
     Records the time from when the user presses the Continue button to when the
     idtoken response is received. Only records a sample when the idtoken
@@ -811,7 +831,7 @@
 <histogram name="Blink.FedCm.Timing.ShowAccountsDialog" units="ms"
     expires_after="M110">
   <owner>yigu@chromium.org</owner>
-  <owner>webid-core@google.com</owner>
+  <owner>fedcm-core@google.com</owner>
   <summary>
     Records the time from when a call to the API was made to when the accounts
     dialog is shown. Only records a sample when the dialog is shown.
@@ -821,7 +841,7 @@
 <histogram name="Blink.FedCm.Timing.TurnaroundTime" units="ms"
     expires_after="M110">
   <owner>yigu@chromium.org</owner>
-  <owner>webid-core@google.com</owner>
+  <owner>fedcm-core@google.com</owner>
   <summary>
     Records the overall time from when the API is called to when the idtoken
     response is received. Only records a sample when the idtoken response is
@@ -832,7 +852,7 @@
 <histogram name="Blink.FedCm.WebContentsVisible" enum="Boolean"
     expires_after="M110">
   <owner>yigu@chromium.org</owner>
-  <owner>webid-core@google.com</owner>
+  <owner>fedcm-core@google.com</owner>
   <summary>
     Records whether the WebContents is visible when the browser is ready to show
     the accounts dialog to the user. Records at most one sample per API call.
diff --git a/tools/metrics/histograms/metadata/browser/OWNERS b/tools/metrics/histograms/metadata/browser/OWNERS
index c234ae84..2e75948b 100644
--- a/tools/metrics/histograms/metadata/browser/OWNERS
+++ b/tools/metrics/histograms/metadata/browser/OWNERS
@@ -1,8 +1,6 @@
 per-file OWNERS=file://tools/metrics/histograms/metadata/METRIC_REVIEWER_OWNERS
 
-# Prefer sending CLs to the owners listed below.
-# Use chromium-metrics-reviews@google.com as a backup.
-yashard@chromium.org
-
 # For Browser.Tabs.* histograms:
-joenotcharles@google.com
\ No newline at end of file
+joenotcharles@google.com
+
+# For other histograms use chromium-metrics-reviews@google.com.
diff --git a/tools/metrics/histograms/metadata/memory/histograms.xml b/tools/metrics/histograms/metadata/memory/histograms.xml
index 0186734e..cf44a1e 100644
--- a/tools/metrics/histograms/metadata/memory/histograms.xml
+++ b/tools/metrics/histograms/metadata/memory/histograms.xml
@@ -1805,7 +1805,6 @@
     units="MB" expires_after="2022-08-14">
   <owner>ckitagawa@chromium.org</owner>
   <owner>fredmello@chromium.org</owner>
-  <owner>yashard@chromium.org</owner>
   <summary>
     A rough estimate of the private memory footprint of the paint preview
     compositor process.
@@ -1826,7 +1825,6 @@
     expires_after="2023-01-10">
   <owner>ckitagawa@chromium.org</owner>
   <owner>fredmello@chromium.org</owner>
-  <owner>yashard@chromium.org</owner>
   <summary>
     An amount of private memory of the paint preview compositor process placed
     in swap (VmSwap).
@@ -1840,7 +1838,6 @@
     expires_after="2022-08-14">
   <owner>ckitagawa@chromium.org</owner>
   <owner>fredmello@chromium.org</owner>
-  <owner>yashard@chromium.org</owner>
   <summary>
     The size of the resident memory in a paint preview compositor process. This
     is influenced by factors we control (e.g. memory that is not accessed can be
@@ -1864,7 +1861,6 @@
     units="MB" expires_after="2023-01-10">
   <owner>ckitagawa@chromium.org</owner>
   <owner>fredmello@chromium.org</owner>
-  <owner>yashard@chromium.org</owner>
   <summary>
     A rough estimate of the shared memory footprint of the paint preview
     compositor process.
diff --git a/tools/metrics/histograms/metadata/sharing/histograms.xml b/tools/metrics/histograms/metadata/sharing/histograms.xml
index 3df317a..607c13e 100644
--- a/tools/metrics/histograms/metadata/sharing/histograms.xml
+++ b/tools/metrics/histograms/metadata/sharing/histograms.xml
@@ -370,7 +370,6 @@
 <histogram name="Sharing.ScrollCapture.BitmapGeneratorStatus"
     enum="SharingScrollCaptureBitmapGenerationStatus"
     expires_after="2022-07-03">
-  <owner>yashard@chromium.org</owner>
   <owner>ckitagawa@chromium.org</owner>
   <owner>src/components/paint_preview/OWNERS</owner>
   <summary>
@@ -383,7 +382,6 @@
 
 <histogram name="Sharing.ScrollCapture.SuccessfulCaptureDuration" units="ms"
     expires_after="2022-07-03">
-  <owner>yashard@chromium.org</owner>
   <owner>ckitagawa@chromium.org</owner>
   <owner>src/components/paint_preview/OWNERS</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/startup/histograms.xml b/tools/metrics/histograms/metadata/startup/histograms.xml
index ba7c9cd..07408a80 100644
--- a/tools/metrics/histograms/metadata/startup/histograms.xml
+++ b/tools/metrics/histograms/metadata/startup/histograms.xml
@@ -135,7 +135,6 @@
   <owner>yfriedman@chromium.org</owner>
   <owner>pasko@chromium.org</owner>
   <owner>fredmello@chromium.org</owner>
-  <owner>yashard@chromium.org</owner>
   <owner>chrome-analysis-team@google.com</owner>
   <summary>
     The time from Chrome tabbed activity creation to the moment the Chrome first
@@ -190,7 +189,6 @@
 
 <histogram name="Startup.Android.Cold.TimeToVisibleContent" units="ms"
     expires_after="2022-08-07">
-  <owner>yashard@chromium.org</owner>
   <owner>ckitagawa@chromium.org</owner>
   <summary>
     Android: The time from the activity creation point to the moment the content
diff --git a/tools/metrics/histograms/metadata/tab/histograms.xml b/tools/metrics/histograms/metadata/tab/histograms.xml
index ae5b853..9d8d8ec2 100644
--- a/tools/metrics/histograms/metadata/tab/histograms.xml
+++ b/tools/metrics/histograms/metadata/tab/histograms.xml
@@ -360,7 +360,6 @@
 <histogram name="Tab.CloseAllTabsDialog.ClosedAllTabs" units="Boolean"
     expires_after="2022-06-30">
   <owner>ckitagawa@chromium.org</owner>
-  <owner>yashard@chromium.org</owner>
   <owner>fredmello@chromium.org</owner>
   <summary>
     Records the whether the &quot;Close all tabs&quot; action was taken when
diff --git a/tools/perfbot-analysis/analyze-new-pdf.js b/tools/perfbot-analysis/analyze-new-pdf.js
index a322da7..8e343b1 100644
--- a/tools/perfbot-analysis/analyze-new-pdf.js
+++ b/tools/perfbot-analysis/analyze-new-pdf.js
@@ -62,16 +62,26 @@
       let msg = '';
 
       if ('error' in histograms) {
-        msg = 'some error happened.';
-      } else if ((oldh in histograms) && (newh in histograms)) {
-        const newv = histograms[newh].avg;
-        const oldv = histograms[oldh].avg;
-        const diff = Math.abs(newv - oldv);
-        msg = `Difference: ${diff.toFixed(2)} (${newv.toFixed(2)} vs ${
-            oldv.toFixed(2)}).`;
-        rows.push([f, oldv, newv]);
-      } else {
+        msg = 'some error happened: ' + histograms.error;
+      } else if (!(oldh in histograms) && !(newh in histograms)) {
         msg = 'no metrics.';
+      } else {
+        if (!(newh in histograms)) {
+          const oldv = histograms[oldh].avg;
+          msg = `No new metric, old metric: ${oldv.toFixed(2)}`;
+          rows.push([f, oldv, -1]);
+        } else if (!(oldh in histograms)) {
+          const newv = histograms[newh].avg;
+          msg = `No old metric, new metric: ${newv.toFixed(2)}`;
+          rows.push([f, -1, newv]);
+        } else {
+          const newv = histograms[newh].avg;
+          const oldv = histograms[oldh].avg;
+          const diff = Math.abs(newv - oldv);
+          msg = `Difference: ${diff.toFixed(2)} (${newv.toFixed(2)} vs ${
+              oldv.toFixed(2)}).`;
+          rows.push([f, oldv, newv]);
+        }
       }
 
       console.log(`${counter++}/${filenames.length} ${f}: ${msg}`);
diff --git a/ui/accessibility/accessibility_features.cc b/ui/accessibility/accessibility_features.cc
index e4a87dd..9e94ccb 100644
--- a/ui/accessibility/accessibility_features.cc
+++ b/ui/accessibility/accessibility_features.cc
@@ -115,19 +115,9 @@
   return base::FeatureList::IsEnabled(::features::kDockedMagnifierResizing);
 }
 
-const base::Feature kExperimentalAccessibilityDictationOffline{
-    "ExperimentalAccessibilityDictationOffline",
-    base::FEATURE_ENABLED_BY_DEFAULT};
-
-bool IsExperimentalAccessibilityDictationOfflineEnabled() {
+bool IsDictationOfflineAvailable() {
   return base::FeatureList::IsEnabled(
-      ::features::kExperimentalAccessibilityDictationOffline);
-}
-
-bool IsDictationOfflineAvailableAndEnabled() {
-  return base::FeatureList::IsEnabled(
-             ash::features::kOnDeviceSpeechRecognition) &&
-         IsExperimentalAccessibilityDictationOfflineEnabled();
+      ash::features::kOnDeviceSpeechRecognition);
 }
 
 const base::Feature kExperimentalAccessibilityDictationCommands{
diff --git a/ui/accessibility/accessibility_features.h b/ui/accessibility/accessibility_features.h
index 32e30a5c..61e1eb1 100644
--- a/ui/accessibility/accessibility_features.h
+++ b/ui/accessibility/accessibility_features.h
@@ -95,14 +95,7 @@
 // bottom of Docked Magnifier is enabled.
 AX_BASE_EXPORT bool IsDockedMagnifierResizingEnabled();
 
-// Enables dictation to use on-device speech recognition when available.
-AX_BASE_EXPORT extern const base::Feature
-    kExperimentalAccessibilityDictationOffline;
-
-// Returns true if experimental accessibility offline dictation is enabled.
-AX_BASE_EXPORT bool IsExperimentalAccessibilityDictationOfflineEnabled();
-
-AX_BASE_EXPORT bool IsDictationOfflineAvailableAndEnabled();
+AX_BASE_EXPORT bool IsDictationOfflineAvailable();
 
 // Enables text-editing commands in the dictation.
 AX_BASE_EXPORT extern const base::Feature
diff --git a/ui/accessibility/ax_node_data.cc b/ui/accessibility/ax_node_data.cc
index f7293fb..77ad61e 100644
--- a/ui/accessibility/ax_node_data.cc
+++ b/ui/accessibility/ax_node_data.cc
@@ -598,6 +598,10 @@
                     &text_attributes.font_weight);
   GetStringAttribute(ax::mojom::StringAttribute::kFontFamily,
                      &text_attributes.font_family);
+  GetIntListAttribute(ax::mojom::IntListAttribute::kMarkerTypes,
+                      &text_attributes.marker_types);
+  GetIntListAttribute(ax::mojom::IntListAttribute::kHighlightTypes,
+                      &text_attributes.highlight_types);
 
   return text_attributes;
 }
diff --git a/ui/accessibility/ax_text_attributes.cc b/ui/accessibility/ax_text_attributes.cc
index b099be0e..1857a950 100644
--- a/ui/accessibility/ax_text_attributes.cc
+++ b/ui/accessibility/ax_text_attributes.cc
@@ -22,7 +22,9 @@
       underline_style(other.underline_style),
       font_size(other.font_size),
       font_weight(other.font_weight),
-      font_family(std::move(other.font_family)) {}
+      font_family(std::move(other.font_family)),
+      marker_types(std::move(other.marker_types)),
+      highlight_types(std::move(other.highlight_types)) {}
 
 AXTextAttributes& AXTextAttributes::operator=(AXTextAttributes&& other) {
   if (this == &other)
@@ -40,21 +42,24 @@
   font_size = other.font_size;
   font_weight = other.font_weight;
   font_family = other.font_family;
+  marker_types = other.marker_types;
+  highlight_types = other.highlight_types;
 
   return *this;
 }
 
 bool AXTextAttributes::operator==(const AXTextAttributes& other) const {
-  return (background_color == other.background_color && color == other.color &&
-          invalid_state == other.invalid_state &&
-          overline_style == other.overline_style &&
-          strikethrough_style == other.strikethrough_style &&
-          text_direction == other.text_direction &&
-          text_position == other.text_position &&
-          font_size == other.font_size && font_weight == other.font_weight &&
-          text_style == other.text_style &&
-          underline_style == other.underline_style &&
-          font_family == other.font_family);
+  return background_color == other.background_color && color == other.color &&
+         invalid_state == other.invalid_state &&
+         overline_style == other.overline_style &&
+         strikethrough_style == other.strikethrough_style &&
+         text_direction == other.text_direction &&
+         text_position == other.text_position && font_size == other.font_size &&
+         font_weight == other.font_weight && text_style == other.text_style &&
+         underline_style == other.underline_style &&
+         font_family == other.font_family &&
+         marker_types == other.marker_types &&
+         highlight_types == other.highlight_types;
 }
 
 bool AXTextAttributes::operator!=(const AXTextAttributes& other) const {
@@ -62,11 +67,12 @@
 }
 
 bool AXTextAttributes::IsUnset() const {
-  return (background_color == kUnsetValue && invalid_state == kUnsetValue &&
-          overline_style == kUnsetValue && strikethrough_style == kUnsetValue &&
-          text_position == kUnsetValue && font_size == kUnsetValue &&
-          font_weight == kUnsetValue && text_style == kUnsetValue &&
-          underline_style == kUnsetValue && font_family.length() == 0);
+  return background_color == kUnsetValue && invalid_state == kUnsetValue &&
+         overline_style == kUnsetValue && strikethrough_style == kUnsetValue &&
+         text_position == kUnsetValue && font_size == kUnsetValue &&
+         font_weight == kUnsetValue && text_style == kUnsetValue &&
+         underline_style == kUnsetValue && font_family.length() == 0 &&
+         marker_types.size() == 0 && highlight_types.size() == 0;
 }
 
 }  // namespace ui
diff --git a/ui/accessibility/ax_text_attributes.h b/ui/accessibility/ax_text_attributes.h
index 5cdb6bb..cc2eac12 100644
--- a/ui/accessibility/ax_text_attributes.h
+++ b/ui/accessibility/ax_text_attributes.h
@@ -6,6 +6,7 @@
 #define UI_ACCESSIBILITY_AX_TEXT_ATTRIBUTES_H_
 
 #include <string>
+#include <vector>
 
 #include "ui/accessibility/ax_base_export.h"
 
@@ -47,6 +48,8 @@
   float font_size = kUnsetValue;
   float font_weight = kUnsetValue;
   std::string font_family;
+  std::vector<int32_t> marker_types;
+  std::vector<int32_t> highlight_types;
 };
 
 }  // namespace ui
diff --git a/ui/android/java/src/org/chromium/ui/base/DragAndDropDelegateImpl.java b/ui/android/java/src/org/chromium/ui/base/DragAndDropDelegateImpl.java
index cc635a4..f121064d 100644
--- a/ui/android/java/src/org/chromium/ui/base/DragAndDropDelegateImpl.java
+++ b/ui/android/java/src/org/chromium/ui/base/DragAndDropDelegateImpl.java
@@ -10,6 +10,7 @@
 import android.graphics.Bitmap;
 import android.net.Uri;
 import android.os.Build.VERSION_CODES;
+import android.os.SystemClock;
 import android.util.DisplayMetrics;
 import android.util.Pair;
 import android.view.DragEvent;
@@ -17,24 +18,54 @@
 import android.view.View.DragShadowBuilder;
 import android.widget.ImageView;
 
+import androidx.annotation.IntDef;
 import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
 import androidx.core.content.res.ResourcesCompat;
 
 import org.chromium.base.ContextUtils;
 import org.chromium.base.compat.ApiHelperForN;
+import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.ui.R;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 /**
  * Drag and drop helper class in charge of building the clip data, wrapping calls to
  * {@link android.view.View#startDragAndDrop}. Also used for mocking out real function calls to
  * Android.
  */
 class DragAndDropDelegateImpl implements ViewAndroidDelegate.DragAndDropDelegate, DragStateTracker {
+    /**
+     * Java Enum of AndroidDragTargetType used for histogram recording for
+     * Android.DragDrop.FromWebContent.TargetType. This is used for histograms and should therefore
+     * be treated as append-only.
+     */
+    @IntDef({DragTargetType.INVALID, DragTargetType.TEXT, DragTargetType.IMAGE, DragTargetType.LINK,
+            DragTargetType.NUM_ENTRIES})
+    @Retention(RetentionPolicy.SOURCE)
+    @interface DragTargetType {
+        int INVALID = 0;
+        int TEXT = 1;
+        int IMAGE = 2;
+        int LINK = 3;
+
+        int NUM_ENTRIES = 4;
+    }
+
     private int mShadowWidth;
     private int mShadowHeight;
     private boolean mIsDragStarted;
 
+    /** Whether the current drop has happened on top of the view this object tracks.  */
+    private boolean mIsDropOnView;
+
+    /** The type of drag target from the view this object tracks. */
+    private @DragTargetType int mDragTargetType;
+
+    private long mDragStartSystemElapsedTime;
+
     // Implements ViewAndroidDelegate.DragAndDropDelegate
     /**
      * Wrapper to call {@link android.view.View#startDragAndDrop}.
@@ -44,12 +75,14 @@
     @RequiresApi(api = VERSION_CODES.N)
     public boolean startDragAndDrop(@NonNull View containerView, @NonNull Bitmap shadowImage,
             @NonNull DropDataAndroid dropData) {
-        mIsDragStarted = true;
-
         ClipData clipdata = buildClipData(dropData);
         if (clipdata == null) {
             return false;
         }
+
+        mIsDragStarted = true;
+        mDragStartSystemElapsedTime = SystemClock.elapsedRealtime();
+        mDragTargetType = getDragTargetType(dropData);
         return ApiHelperForN.startDragAndDrop(containerView, clipdata,
                 createDragShadowBuilder(
                         containerView.getContext(), shadowImage, dropData.hasImage()),
@@ -81,33 +114,34 @@
     @Override
     public boolean onDrag(View view, DragEvent dragEvent) {
         if (dragEvent.getAction() == DragEvent.ACTION_DRAG_ENDED) {
+            onDragEnd(dragEvent);
             reset();
-            if (!dragEvent.getResult()) {
-                // Clear the image data immediately when not used.
-                DropDataContentProvider.clearCache();
-                // TODO: add metric
-            } else {
-                // Otherwise, clear it with a delay to allow asynchronous data transfer.
-                DropDataContentProvider.clearCacheWithDelay();
-                // TODO: add metric
-            }
+        } else if (dragEvent.getAction() == DragEvent.ACTION_DROP) {
+            mIsDropOnView = true;
         }
         // Return false so this listener does not consume the drag event of the view it listened to.
         return false;
     }
 
     protected ClipData buildClipData(DropDataAndroid dropData) {
-        if (dropData.isPlainText()) {
-            return ClipData.newPlainText(null, dropData.text);
-        } else if (dropData.hasImage()) {
-            Uri uri = DropDataContentProvider.cache(
-                    dropData.imageContent, dropData.imageContentExtension);
-            return ClipData.newUri(
-                    ContextUtils.getApplicationContext().getContentResolver(), null, uri);
-            // TODO: ensure MIME type of ClipData is correct
-        } else {
-            // TODO(crbug.com/1289393): handle link dragging
-            return null;
+        @DragTargetType
+        int type = getDragTargetType(dropData);
+        switch (type) {
+            case DragTargetType.TEXT:
+                return ClipData.newPlainText(null, dropData.text);
+            case DragTargetType.IMAGE:
+                Uri uri = DropDataContentProvider.cache(
+                        dropData.imageContent, dropData.imageContentExtension);
+                return ClipData.newUri(
+                        ContextUtils.getApplicationContext().getContentResolver(), null, uri);
+            case DragTargetType.LINK:
+                // TODO(https://crbug.com/1289393): Handle link dragging.
+            case DragTargetType.INVALID:
+                return null;
+            case DragTargetType.NUM_ENTRIES:
+            default:
+                assert false : "Should not be reached!";
+                return null;
         }
     }
 
@@ -180,9 +214,62 @@
         return new Pair<>(Math.round(width), Math.round(height));
     }
 
+    private void onDragEnd(DragEvent dragEndEvent) {
+        boolean dragResult = dragEndEvent.getResult();
+
+        // Only record metrics when drop does not happen for ContentView.
+        if (!mIsDropOnView) {
+            assert mDragStartSystemElapsedTime > 0;
+            assert mDragTargetType != DragTargetType.INVALID;
+            long dragDuration = SystemClock.elapsedRealtime() - mDragStartSystemElapsedTime;
+            recordDragDurationAndResult(dragDuration, dragResult);
+            recordDragTargetType(mDragTargetType);
+        }
+
+        if (!dragResult) {
+            // Clear the image data immediately when not used.
+            DropDataContentProvider.clearCache();
+            // TODO(https://crbug.com/1299143): add metric
+        } else {
+            // Otherwise, clear it with a delay to allow asynchronous data transfer.
+            DropDataContentProvider.clearCacheWithDelay();
+            // TODO(https://crbug.com/1299143): add metric
+        }
+    }
+
+    /**
+     * Return the {@link DragTargetType} based on the content of DropDataAndroid. The result will
+     * bias plain text > image > link.
+     * TODO(https://crbug.com/1299994): Manage the ClipData bias with EventForwarder in one place.
+     */
+    static @DragTargetType int getDragTargetType(DropDataAndroid dropDataAndroid) {
+        if (dropDataAndroid.isPlainText()) {
+            return DragTargetType.TEXT;
+        } else if (dropDataAndroid.hasImage()) {
+            return DragTargetType.IMAGE;
+        } else {
+            // TODO(https://crbug.com/1289393): Handle link dragging.
+            return DragTargetType.INVALID;
+        }
+    }
+
     private void reset() {
         mShadowHeight = 0;
         mShadowWidth = 0;
+        mDragTargetType = DragTargetType.INVALID;
         mIsDragStarted = false;
+        mIsDropOnView = false;
+        mDragStartSystemElapsedTime = -1;
+    }
+
+    private void recordDragTargetType(@DragTargetType int type) {
+        RecordHistogram.recordEnumeratedHistogram(
+                "Android.DragDrop.FromWebContent.TargetType", type, DragTargetType.NUM_ENTRIES);
+    }
+
+    private void recordDragDurationAndResult(long duration, boolean result) {
+        String histogramPrefix = "Android.DragDrop.FromWebContent.Duration.";
+        String suffix = result ? "Success" : "Canceled";
+        RecordHistogram.recordMediumTimesHistogram(histogramPrefix + suffix, duration);
     }
 }
diff --git a/ui/android/junit/src/org/chromium/ui/base/DragAndDropDelegateImplUnitTest.java b/ui/android/junit/src/org/chromium/ui/base/DragAndDropDelegateImplUnitTest.java
index 9935a7d..e048dee 100644
--- a/ui/android/junit/src/org/chromium/ui/base/DragAndDropDelegateImplUnitTest.java
+++ b/ui/android/junit/src/org/chromium/ui/base/DragAndDropDelegateImplUnitTest.java
@@ -23,7 +23,9 @@
 import org.robolectric.annotation.Config;
 import org.robolectric.shadows.ShadowDisplay;
 
+import org.chromium.base.metrics.test.ShadowRecordHistogram;
 import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.ui.base.DragAndDropDelegateImpl.DragTargetType;
 import org.chromium.url.JUnitTestGURLs;
 
 /**
@@ -31,7 +33,8 @@
  * the ease of dp / pixel calculation.
  */
 @RunWith(BaseRobolectricTestRunner.class)
-@Config(shadows = {ShadowDisplay.class}, qualifiers = "w1000dp-h2000dp-mdpi")
+@Config(shadows = {ShadowDisplay.class, ShadowRecordHistogram.class},
+        qualifiers = "w1000dp-h2000dp-mdpi")
 public class DragAndDropDelegateImplUnitTest {
     private Context mContext;
     private DragAndDropDelegateImpl mDragAndDropDelegateImpl;
@@ -45,10 +48,11 @@
     @After
     public void tearDown() {
         DropDataContentProvider.clearCache();
+        ShadowRecordHistogram.reset();
     }
 
     @Test
-    public void testStartDragAndDrop() {
+    public void testStartDragAndDrop_Text() {
         final View containerView = new View(mContext);
         final Bitmap shadowImage = Bitmap.createBitmap(100, 200, Bitmap.Config.ALPHA_8);
         final DropDataAndroid dropData = DropDataAndroid.create("text", null, null, null);
@@ -60,38 +64,24 @@
                 mDragAndDropDelegateImpl.getDragShadowWidth());
         Assert.assertEquals("Drag shadow height not match. Should not resize for text.", 200,
                 mDragAndDropDelegateImpl.getDragShadowHeight());
+        assertDragTypeNotRecorded("Drag didn't end.");
 
-        DragEvent dragEvent = Mockito.mock(DragEvent.class);
-        doReturn(DragEvent.ACTION_DRAG_ENDED).when(dragEvent).getAction();
-        mDragAndDropDelegateImpl.onDrag(containerView, dragEvent);
+        mDragAndDropDelegateImpl.onDrag(containerView, mockDragEvent(DragEvent.ACTION_DRAG_ENDED));
 
         Assert.assertFalse("Drag should end.", mDragAndDropDelegateImpl.isDragStarted());
         Assert.assertEquals("Drag shadow width should be reset.", 0,
                 mDragAndDropDelegateImpl.getDragShadowWidth());
         Assert.assertEquals("Drag shadow height should be reset.", 0,
                 mDragAndDropDelegateImpl.getDragShadowHeight());
+        assertDragTypeRecorded(DragTargetType.TEXT);
+        assertDragDurationRecorded(/*dropResult=*/false, /*recorded=*/true);
     }
 
     @Test
-    public void testResizeShadowForDifferentDropData() {
+    public void testStartDragAndDrop_Image() {
         final View containerView = new View(mContext);
         final Bitmap shadowImage = Bitmap.createBitmap(100, 200, Bitmap.Config.ALPHA_8);
 
-        final DropDataAndroid textDropData = DropDataAndroid.create("text", null, null, null);
-        mDragAndDropDelegateImpl.startDragAndDrop(containerView, shadowImage, textDropData);
-        Assert.assertEquals("Drag shadow width not match. Should not resize for text.", 100,
-                mDragAndDropDelegateImpl.getDragShadowWidth());
-        Assert.assertEquals("Drag shadow height not match. Should not resize for text.", 200,
-                mDragAndDropDelegateImpl.getDragShadowHeight());
-
-        final DropDataAndroid linkDropData = DropDataAndroid.create(
-                "text", JUnitTestGURLs.getGURL(JUnitTestGURLs.EXAMPLE_URL), null, null);
-        mDragAndDropDelegateImpl.startDragAndDrop(containerView, shadowImage, linkDropData);
-        Assert.assertEquals("Drag shadow width not match. Should not resize for link.", 100,
-                mDragAndDropDelegateImpl.getDragShadowWidth());
-        Assert.assertEquals("Drag shadow height not match. Should not resize for link.", 200,
-                mDragAndDropDelegateImpl.getDragShadowHeight());
-
         final DropDataAndroid imageDropData =
                 DropDataAndroid.create("", null, new byte[] {1, 2, 3, 4}, "png");
         mDragAndDropDelegateImpl.startDragAndDrop(containerView, shadowImage, imageDropData);
@@ -99,9 +89,73 @@
                 mDragAndDropDelegateImpl.getDragShadowWidth());
         Assert.assertEquals("Drag shadow height not match. Should do resize for image.", 120,
                 mDragAndDropDelegateImpl.getDragShadowHeight());
-
         Assert.assertNotNull("Cached Image bytes should not be null.",
                 DropDataContentProvider.getImageBytesForTesting());
+        assertDragTypeNotRecorded("Drag didn't end.");
+
+        DragEvent dragEnd = mockDragEvent(DragEvent.ACTION_DRAG_ENDED);
+        mDragAndDropDelegateImpl.onDrag(containerView, dragEnd);
+        Assert.assertNull("Cached Image bytes should be cleaned.",
+                DropDataContentProvider.getImageBytesForTesting());
+        assertDragTypeRecorded(DragTargetType.IMAGE);
+        assertDragDurationRecorded(/*dropResult=*/false, /*recorded=*/true);
+    }
+
+    /**
+     * Link dragging is not supported yet, adding this test to make sure status are handled
+     * correctly.
+     * TODO(https://crbug.com/1289393): Handle link dragging.
+     */
+    @Test
+    public void testStartDragAndDrop_Link() {
+        final View containerView = new View(mContext);
+        final Bitmap shadowImage = Bitmap.createBitmap(100, 200, Bitmap.Config.ALPHA_8);
+
+        final DropDataAndroid linkDropData = DropDataAndroid.create(
+                "", JUnitTestGURLs.getGURL(JUnitTestGURLs.EXAMPLE_URL), null, null);
+        mDragAndDropDelegateImpl.startDragAndDrop(containerView, shadowImage, linkDropData);
+        Assert.assertEquals(
+                "Drag link is not supported.", 0, mDragAndDropDelegateImpl.getDragShadowWidth());
+        Assert.assertEquals(
+                "Drag link is not supported.", 0, mDragAndDropDelegateImpl.getDragShadowHeight());
+        Assert.assertFalse("Drag Link is not supported.", mDragAndDropDelegateImpl.isDragStarted());
+        assertDragTypeNotRecorded("Drag didn't started.");
+    }
+
+    @Test
+    public void testDragImage_DragHandled() {
+        final View containerView = new View(mContext);
+        final Bitmap shadowImage = Bitmap.createBitmap(100, 200, Bitmap.Config.ALPHA_8);
+        final DropDataAndroid imageDropData =
+                DropDataAndroid.create("", null, new byte[] {1, 2, 3, 4}, "png");
+        mDragAndDropDelegateImpl.startDragAndDrop(containerView, shadowImage, imageDropData);
+
+        final DragEvent dragEndEvent = mockDragEvent(DragEvent.ACTION_DRAG_ENDED);
+        doReturn(true).when(dragEndEvent).getResult();
+        mDragAndDropDelegateImpl.onDrag(containerView, dragEndEvent);
+
+        Assert.assertNotNull("Cached Image bytes should not be cleaned, drag is handled.",
+                DropDataContentProvider.getImageBytesForTesting());
+        assertDragTypeRecorded(DragTargetType.IMAGE);
+        assertDragDurationRecorded(/*dropResult=*/true, /*recorded=*/true);
+    }
+
+    @Test
+    public void testDragImage_ReceivedDropBeforeDragEnds() {
+        final View containerView = new View(mContext);
+        final Bitmap shadowImage = Bitmap.createBitmap(100, 200, Bitmap.Config.ALPHA_8);
+        final DropDataAndroid imageDropData =
+                DropDataAndroid.create("", null, new byte[] {1, 2, 3, 4}, "png");
+        mDragAndDropDelegateImpl.startDragAndDrop(containerView, shadowImage, imageDropData);
+
+        mDragAndDropDelegateImpl.onDrag(containerView, mockDragEvent(DragEvent.ACTION_DROP));
+        mDragAndDropDelegateImpl.onDrag(containerView, mockDragEvent(DragEvent.ACTION_DRAG_ENDED));
+
+        // Drop on the same view does not lead to recording of drag duration.
+        assertDragTypeNotRecorded("Drag dropped on the same view.");
+        assertDragDurationRecorded(/*dropResult=*/false, /*recorded=*/false);
+        Assert.assertNull("Cached Image bytes should be cleaned since drop is not handled.",
+                DropDataContentProvider.getImageBytesForTesting());
     }
 
     @Test
@@ -177,4 +231,32 @@
         Assert.assertTrue(assertMsg,
                 expectedWidth == actualResizedWidth && expectedHeight == actualResizedHeight);
     }
+
+    private DragEvent mockDragEvent(int action) {
+        DragEvent event = Mockito.mock(DragEvent.class);
+        doReturn(action).when(event).getAction();
+        return event;
+    }
+
+    private void assertDragTypeNotRecorded(String reason) {
+        final String histogram = "Android.DragDrop.FromWebContent.TargetType";
+        final String errorMsg = "<" + histogram + "> should not recorded. Reason:" + reason;
+        Assert.assertEquals(
+                errorMsg, 0, ShadowRecordHistogram.getHistogramTotalCountForTesting(histogram));
+    }
+
+    private void assertDragTypeRecorded(@DragTargetType int type) {
+        final String histogram = "Android.DragDrop.FromWebContent.TargetType";
+        final String errorMsg = "<" + histogram + "> is not recorded correctly.";
+        Assert.assertEquals(errorMsg, 1,
+                ShadowRecordHistogram.getHistogramValueCountForTesting(histogram, type));
+    }
+
+    private void assertDragDurationRecorded(boolean dropResult, boolean recorded) {
+        final String histogram =
+                "Android.DragDrop.FromWebContent.Duration." + (dropResult ? "Success" : "Canceled");
+        final String errorMsg = "<" + histogram + "> is not recorded correctly.";
+        Assert.assertEquals(errorMsg, recorded ? 1 : 0,
+                ShadowRecordHistogram.getHistogramTotalCountForTesting(histogram));
+    }
 }
diff --git a/ui/base/clipboard/clipboard_data_unittest.cc b/ui/base/clipboard/clipboard_data_unittest.cc
index 1315a96..f2c23de05 100644
--- a/ui/base/clipboard/clipboard_data_unittest.cc
+++ b/ui/base/clipboard/clipboard_data_unittest.cc
@@ -33,14 +33,14 @@
 // Tests that two ClipboardData objects won't be equal if they don't have the
 // same data source.
 TEST(ClipboardDataTest, DataSrcTest) {
-  url::Origin origin(url::Origin::Create(GURL("www.example.com")));
+  GURL url("www.example.com");
   ClipboardData data1;
-  data1.set_source(std::make_unique<DataTransferEndpoint>(origin));
+  data1.set_source(std::make_unique<DataTransferEndpoint>(url));
 
   ClipboardData data2;
   EXPECT_NE(data1, data2);
 
-  data2.set_source(std::make_unique<DataTransferEndpoint>(origin));
+  data2.set_source(std::make_unique<DataTransferEndpoint>(url));
   EXPECT_EQ(data1, data2);
 }
 
diff --git a/ui/base/clipboard/clipboard_test_template.h b/ui/base/clipboard/clipboard_test_template.h
index 47ffefb7..ab4d7d3 100644
--- a/ui/base/clipboard/clipboard_test_template.h
+++ b/ui/base/clipboard/clipboard_test_template.h
@@ -52,7 +52,6 @@
 #include "ui/gfx/geometry/size.h"
 #include "ui/gfx/half_float.h"
 #include "url/gurl.h"
-#include "url/origin.h"
 
 #if BUILDFLAG(IS_MAC)
 #include "base/mac/mac_util.h"
@@ -620,7 +619,7 @@
   {
     ScopedClipboardWriter clipboard_writer(
         ClipboardBuffer::kCopyPaste,
-        std::make_unique<DataTransferEndpoint>(url::Origin()));
+        std::make_unique<DataTransferEndpoint>(GURL()));
     SkBitmap bitmap;
     ASSERT_TRUE(bitmap.setInfo(info));
     bitmap.setPixels(const_cast<void*>(bitmap_data));
@@ -1302,8 +1301,7 @@
   {
     ScopedClipboardWriter writer(
         ClipboardBuffer::kCopyPaste,
-        std::make_unique<DataTransferEndpoint>(
-            url::Origin::Create(GURL("https://www.google.com"))));
+        std::make_unique<DataTransferEndpoint>(GURL("https://www.google.com")));
     writer.WriteText(kTestText);
   }
   EXPECT_CALL(*policy_controller, IsClipboardReadAllowed)
@@ -1321,8 +1319,9 @@
       ui::ClipboardFormatType::DataTransferEndpointDataType(),
       /* data_dst = */ nullptr, &actual_json);
 
-  EXPECT_EQ(R"({"endpoint_type":"url","url_origin":"https://www.google.com"})",
-            actual_json);
+  EXPECT_EQ(
+      R"({"endpoint_type":"url","url":"https://www.google.com/","url_origin":"https://www.google.com"})",
+      actual_json);
 #endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
 
   ::testing::Mock::VerifyAndClearExpectations(policy_controller.get());
@@ -1336,7 +1335,7 @@
   {
     ScopedClipboardWriter writer(
         ClipboardBuffer::kCopyPaste,
-        std::make_unique<DataTransferEndpoint>(url::Origin()));
+        std::make_unique<DataTransferEndpoint>(GURL()));
     writer.WriteText(kTestText);
   }
   EXPECT_CALL(*policy_controller, IsClipboardReadAllowed)
@@ -1373,7 +1372,7 @@
   auto policy_controller = std::make_unique<MockPolicyController>();
   const std::u16string kTestText(u"World");
   const std::string kDteJson(
-      R"({"endpoint_type":"url","url_origin":"https://www.google.com"})");
+      R"({"endpoint_type":"url","url":"https://www.google.com"})");
   {
     // No source DTE provided directly to the Lacros clipboard.
     ScopedClipboardWriter writer(ClipboardBuffer::kCopyPaste);
@@ -1382,14 +1381,14 @@
     writer.WriteEncodedDataTransferEndpointForTesting(kDteJson);
   }
 
-  EXPECT_CALL(
-      *policy_controller,
-      IsClipboardReadAllowed(
-          Pointee(AllOf(Property(&DataTransferEndpoint::IsUrlType, true),
-                        Property(&DataTransferEndpoint::GetOrigin,
-                                 Pointee(Property(&url::Origin::Serialize,
-                                                  "https://www.google.com"))))),
-          _, _))
+  EXPECT_CALL(*policy_controller,
+              IsClipboardReadAllowed(
+                  Pointee(AllOf(
+                      Property(&DataTransferEndpoint::IsUrlType, true),
+                      Property(&DataTransferEndpoint::GetURL,
+                               Pointee(Property(&GURL::spec,
+                                                "https://www.google.com/"))))),
+                  _, _))
       .WillRepeatedly(testing::Return(true));
 
   std::u16string read_result;
diff --git a/ui/base/data_transfer_policy/data_transfer_endpoint.cc b/ui/base/data_transfer_policy/data_transfer_endpoint.cc
index 0c729bc..6b0af86 100644
--- a/ui/base/data_transfer_policy/data_transfer_endpoint.cc
+++ b/ui/base/data_transfer_policy/data_transfer_endpoint.cc
@@ -7,20 +7,20 @@
 #include "base/check_op.h"
 #include "base/stl_util.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
-#include "url/origin.h"
+#include "url/gurl.h"
 
 namespace ui {
 
-DataTransferEndpoint::DataTransferEndpoint(const url::Origin& origin,
+DataTransferEndpoint::DataTransferEndpoint(const GURL& url,
                                            bool notify_if_restricted)
     : type_(EndpointType::kUrl),
-      origin_(origin),
+      url_(url),
       notify_if_restricted_(notify_if_restricted) {}
 
 DataTransferEndpoint::DataTransferEndpoint(EndpointType type,
                                            bool notify_if_restricted)
     : type_(type),
-      origin_(absl::nullopt),
+      url_(absl::nullopt),
       notify_if_restricted_(notify_if_restricted) {
   DCHECK_NE(type, EndpointType::kUrl);
 }
@@ -38,19 +38,19 @@
     DataTransferEndpoint&& other) = default;
 
 bool DataTransferEndpoint::operator==(const DataTransferEndpoint& other) const {
-  return origin_ == other.origin_ && type_ == other.type_ &&
+  return url_ == other.url_ && type_ == other.type_ &&
          notify_if_restricted_ == other.notify_if_restricted_;
 }
 
 DataTransferEndpoint::~DataTransferEndpoint() = default;
 
-const url::Origin* DataTransferEndpoint::GetOrigin() const {
-  return base::OptionalOrNullptr(origin_);
+const GURL* DataTransferEndpoint::GetURL() const {
+  return base::OptionalOrNullptr(url_);
 }
 
-bool DataTransferEndpoint::IsSameOriginWith(
+bool DataTransferEndpoint::IsSameURLWith(
     const DataTransferEndpoint& other) const {
-  return IsUrlType() && (type_ == other.type_) && (origin_ == other.origin_);
+  return IsUrlType() && (type_ == other.type_) && (url_ == other.url_);
 }
 
 }  // namespace ui
diff --git a/ui/base/data_transfer_policy/data_transfer_endpoint.h b/ui/base/data_transfer_policy/data_transfer_endpoint.h
index ac34570..c3cd9d6 100644
--- a/ui/base/data_transfer_policy/data_transfer_endpoint.h
+++ b/ui/base/data_transfer_policy/data_transfer_endpoint.h
@@ -7,7 +7,7 @@
 
 #include "build/build_config.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
-#include "url/origin.h"
+#include "url/gurl.h"
 
 namespace ui {
 
@@ -42,8 +42,8 @@
 class COMPONENT_EXPORT(UI_BASE_DATA_TRANSFER_POLICY) DataTransferEndpoint {
  public:
   // In case DataTransferEndpoint is constructed from a RenderFrameHost object,
-  // please use the origin of its main frame.
-  explicit DataTransferEndpoint(const url::Origin& origin,
+  // please use the url of its main frame.
+  explicit DataTransferEndpoint(const GURL& url,
                                 bool notify_if_restricted = true);
   // This constructor shouldn't be used if |type| == EndpointType::kUrl.
   explicit DataTransferEndpoint(EndpointType type,
@@ -64,7 +64,7 @@
 
   bool IsUrlType() const { return type_ == EndpointType::kUrl; }
 
-  const url::Origin* GetOrigin() const;
+  const GURL* GetURL() const;
 
   EndpointType type() const { return type_; }
 
@@ -72,14 +72,14 @@
 
   // Returns true if both of the endpoints have the same origin_ and type_ ==
   // kUrl.
-  bool IsSameOriginWith(const DataTransferEndpoint& other) const;
+  bool IsSameURLWith(const DataTransferEndpoint& other) const;
 
  private:
   // This variable should always have a value representing the object type.
   EndpointType type_;
-  // The url::Origin of the data endpoint. It always has a value if `type_` ==
+  // The URL of the data endpoint. It always has a value if `type_` ==
   // EndpointType::kUrl, otherwise it's empty.
-  absl::optional<url::Origin> origin_;
+  absl::optional<GURL> url_;
   // This variable should be set to true, if paste is initiated by the user.
   // Otherwise it should be set to false, so the user won't see a notification
   // when the data is restricted by the rules of data leak prevention policy
diff --git a/ui/base/data_transfer_policy/data_transfer_endpoint_serializer.cc b/ui/base/data_transfer_policy/data_transfer_endpoint_serializer.cc
index 361715aa..058d199 100644
--- a/ui/base/data_transfer_policy/data_transfer_endpoint_serializer.cc
+++ b/ui/base/data_transfer_policy/data_transfer_endpoint_serializer.cc
@@ -14,6 +14,7 @@
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "ui/base/data_transfer_policy/data_transfer_endpoint.h"
 #include "url/gurl.h"
+#include "url/origin.h"
 
 namespace ui {
 
@@ -22,6 +23,7 @@
 // JSON Keys
 constexpr char kEndpointTypeKey[] = "endpoint_type";
 constexpr char kUrlOriginKey[] = "url_origin";
+constexpr char kUrlKey[] = "url";
 
 // Endpoint Type Strings
 constexpr char kDefaultString[] = "default";
@@ -94,10 +96,14 @@
 
   encoded_dte.SetStringKey(kEndpointTypeKey, EndpointTypeToString(dte.type()));
 
-  const url::Origin* origin = dte.GetOrigin();
+  const GURL* url = dte.GetURL();
 
-  if (origin)
-    encoded_dte.SetStringKey(kUrlOriginKey, origin->Serialize());
+  if (url && url->is_valid()) {
+    encoded_dte.SetStringKey(kUrlKey, url->spec());
+    // TODO(crbug.com/1300476): remove |kUrlOriginKey| after M102.
+    encoded_dte.SetStringKey(kUrlOriginKey,
+                             url::Origin::Create(*url).Serialize());
+  }
 
   std::string json;
   base::JSONWriter::Write(encoded_dte, &json);
@@ -113,12 +119,16 @@
 
   const std::string* endpoint_type =
       dte_dictionary->FindStringKey(kEndpointTypeKey);
-  const std::string* url_string = dte_dictionary->FindStringKey(kUrlOriginKey);
+  const std::string* url_string = dte_dictionary->FindStringKey(kUrlKey);
+
+  // TODO(crbug.com/1300476): remove |kUrlOriginKey| after M102.
+  if (!url_string)
+    url_string = dte_dictionary->FindStringKey(kUrlOriginKey);
 
   if (url_string) {
-    url::Origin origin = url::Origin::Create(GURL(*url_string));
+    GURL url = GURL(*url_string);
 
-    return std::make_unique<DataTransferEndpoint>(origin);
+    return std::make_unique<DataTransferEndpoint>(url);
   }
 
   if (endpoint_type && *endpoint_type != kUrlString) {
diff --git a/ui/base/data_transfer_policy/data_transfer_endpoint_serializer.h b/ui/base/data_transfer_policy/data_transfer_endpoint_serializer.h
index 2025358b..d6b3651d 100644
--- a/ui/base/data_transfer_policy/data_transfer_endpoint_serializer.h
+++ b/ui/base/data_transfer_policy/data_transfer_endpoint_serializer.h
@@ -14,8 +14,11 @@
 // The MIME type data is a JSON string in the form:
 // {
 //   "endpoint_type": "<endpoint type>",
-//   "url_origin": "https://www.google.com"
+//   "url_origin": "https://www.google.com",
+//   "url": "https://www.google.com"
 // }
+// TODO(crbug.com/1300476): "url_origin" is still being sent because of the
+// version skew between Lacros and ash. It should be removed after M102.
 
 namespace ui {
 
@@ -32,4 +35,4 @@
 
 }  // namespace ui
 
-#endif  // UI_BASE_DATA_TRANSFER_POLICY_DATA_TRANSFER_ENDPOINT_SERIALIZER_H_
\ No newline at end of file
+#endif  // UI_BASE_DATA_TRANSFER_POLICY_DATA_TRANSFER_ENDPOINT_SERIALIZER_H_
diff --git a/ui/base/data_transfer_policy/data_transfer_endpoint_serializer_unittest.cc b/ui/base/data_transfer_policy/data_transfer_endpoint_serializer_unittest.cc
index ba0c1f6c..144ecdb 100644
--- a/ui/base/data_transfer_policy/data_transfer_endpoint_serializer_unittest.cc
+++ b/ui/base/data_transfer_policy/data_transfer_endpoint_serializer_unittest.cc
@@ -8,7 +8,6 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/base/data_transfer_policy/data_transfer_endpoint.h"
 #include "url/gurl.h"
-#include "url/origin.h"
 
 namespace ui {
 
@@ -16,8 +15,8 @@
 
 constexpr char kExampleUrl[] = "https://www.google.com";
 constexpr char kExampleJsonUrlType[] =
-    R"({"endpoint_type":"url","url_origin":"https://www.google.com"})";
-constexpr char kExampleJsonUrlTypeNoOrigin[] = R"({"endpoint_type":"url"})";
+    R"({"endpoint_type":"url","url":"https://www.google.com/","url_origin":"https://www.google.com"})";
+constexpr char kExampleJsonUrlTypeNoUrl[] = R"({"endpoint_type":"url"})";
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 // TODO(crbug.com/1280545): Enable test when VM DataTransferEndpoint endpoint
@@ -28,27 +27,27 @@
 }  // namespace
 
 TEST(DataTransferEndpointSerializerTest, DataTransferEndpointToJsonUrl) {
-  const DataTransferEndpoint example(url::Origin::Create(GURL(kExampleUrl)),
+  const DataTransferEndpoint example(GURL(kExampleUrl),
                                      /*notify_if_restricted=*/true);
   std::string actual = ConvertDataTransferEndpointToJson(example);
   EXPECT_EQ(kExampleJsonUrlType, actual);
 }
 
 TEST(DataTransferEndpointSerializerTest, JsonToDataTransferEndpointUrl) {
-  DataTransferEndpoint expected(url::Origin::Create(GURL(kExampleUrl)),
+  DataTransferEndpoint expected(GURL(kExampleUrl),
                                 /*notify_if_restricted=*/true);
   std::unique_ptr<DataTransferEndpoint> actual =
       ConvertJsonToDataTransferEndpoint(kExampleJsonUrlType);
 
   ASSERT_TRUE(actual);
   EXPECT_EQ(expected.type(), actual->type());
-  EXPECT_TRUE(expected.GetOrigin()->IsSameOriginWith(*actual->GetOrigin()));
+  EXPECT_EQ(*expected.GetURL(), *actual->GetURL());
 }
 
 TEST(DataTransferEndpointSerializerTest,
-     JsonToDataTransferEndpointUrlTypeNoOrigin) {
+     JsonToDataTransferEndpointUrlTypeNoUrl) {
   std::unique_ptr<DataTransferEndpoint> actual =
-      ConvertJsonToDataTransferEndpoint(kExampleJsonUrlTypeNoOrigin);
+      ConvertJsonToDataTransferEndpoint(kExampleJsonUrlTypeNoUrl);
 
   EXPECT_EQ(nullptr, actual);
 }
@@ -70,7 +69,7 @@
 
   ASSERT_TRUE(actual);
   EXPECT_EQ(EndpointType::kCrostini, actual->type());
-  EXPECT_EQ(nullptr, actual->GetOrigin());
+  EXPECT_EQ(nullptr, actual->GetURL());
 }
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
diff --git a/ui/base/data_transfer_policy/data_transfer_endpoint_unittest.cc b/ui/base/data_transfer_policy/data_transfer_endpoint_unittest.cc
index 75587b7..5216199 100644
--- a/ui/base/data_transfer_policy/data_transfer_endpoint_unittest.cc
+++ b/ui/base/data_transfer_policy/data_transfer_endpoint_unittest.cc
@@ -6,7 +6,6 @@
 
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
-#include "url/origin.h"
 
 namespace ui {
 
@@ -25,12 +24,12 @@
   EXPECT_EQ(original1.type(), clone1.type());
   EXPECT_EQ(original1.notify_if_restricted(), clone1.notify_if_restricted());
 
-  DataTransferEndpoint original2(url::Origin::Create(GURL(kExample1Url)),
+  DataTransferEndpoint original2(GURL(kExample1Url),
                                  /*notify_if_restricted=*/false);
   DataTransferEndpoint clone2(original2);
 
   EXPECT_EQ(original2.type(), clone2.type());
-  EXPECT_TRUE(clone2.GetOrigin()->IsSameOriginWith(*original2.GetOrigin()));
+  EXPECT_EQ(*clone2.GetURL(), *original2.GetURL());
   EXPECT_EQ(original2.notify_if_restricted(), clone2.notify_if_restricted());
 }
 
@@ -44,28 +43,28 @@
 
   EXPECT_FALSE(default_endpoint1 == default_endpoint2);
 
-  DataTransferEndpoint url_endpoint1(url::Origin::Create(GURL(kExample1Url)),
+  DataTransferEndpoint url_endpoint1(GURL(kExample1Url),
                                      /*notify_if_restricted=*/true);
-  DataTransferEndpoint url_endpoint2(url::Origin::Create(GURL(kExample1Url)),
+  DataTransferEndpoint url_endpoint2(GURL(kExample1Url),
                                      /*notify_if_restricted=*/true);
 
   EXPECT_TRUE(url_endpoint1 == url_endpoint2);
 }
 
 // Tests DataTransferEndpoint::IsSameOriginWith.
-TEST(DataTransferEndpointTest, IsSameOriginWith) {
+TEST(DataTransferEndpointTest, IsSameURLWith) {
   DataTransferEndpoint default_endpoint(EndpointType::kDefault,
                                         /*notify_if_restricted=*/true);
-  DataTransferEndpoint url_endpoint1(url::Origin::Create(GURL(kExample1Url)),
+  DataTransferEndpoint url_endpoint1(GURL(kExample1Url),
                                      /*notify_if_restricted=*/false);
-  DataTransferEndpoint url_endpoint2(url::Origin::Create(GURL(kExample2Url)),
+  DataTransferEndpoint url_endpoint2(GURL(kExample2Url),
                                      /*notify_if_restricted=*/true);
-  DataTransferEndpoint url_endpoint3(url::Origin::Create(GURL(kExample1Url)),
+  DataTransferEndpoint url_endpoint3(GURL(kExample1Url),
                                      /*notify_if_restricted=*/true);
 
-  EXPECT_FALSE(url_endpoint2.IsSameOriginWith(default_endpoint));
-  EXPECT_FALSE(url_endpoint1.IsSameOriginWith(url_endpoint2));
-  EXPECT_TRUE(url_endpoint1.IsSameOriginWith(url_endpoint3));
+  EXPECT_FALSE(url_endpoint2.IsSameURLWith(default_endpoint));
+  EXPECT_FALSE(url_endpoint1.IsSameURLWith(url_endpoint2));
+  EXPECT_TRUE(url_endpoint1.IsSameURLWith(url_endpoint3));
 }
 
 }  // namespace ui
diff --git a/ui/base/dragdrop/os_exchange_data_provider_non_backed_unittest.cc b/ui/base/dragdrop/os_exchange_data_provider_non_backed_unittest.cc
index 1b66938..d19d396 100644
--- a/ui/base/dragdrop/os_exchange_data_provider_non_backed_unittest.cc
+++ b/ui/base/dragdrop/os_exchange_data_provider_non_backed_unittest.cc
@@ -47,8 +47,8 @@
 #if !BUILDFLAG(IS_CHROMEOS_ASH)
   original.MarkOriginatedFromRenderer();
 #endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
-  url::Origin origin(url::Origin::Create(GURL("www.example.com")));
-  original.SetSource(std::make_unique<DataTransferEndpoint>(origin));
+  GURL url("www.example.com");
+  original.SetSource(std::make_unique<DataTransferEndpoint>(url));
 
   std::unique_ptr<OSExchangeDataProvider> copy = original.Clone();
   std::u16string copy_string;
@@ -88,7 +88,7 @@
   DataTransferEndpoint* data_endpoint = copy->GetSource();
   EXPECT_TRUE(data_endpoint);
   EXPECT_TRUE(data_endpoint->IsUrlType());
-  EXPECT_EQ(origin, *data_endpoint->GetOrigin());
+  EXPECT_EQ(url, *data_endpoint->GetURL());
 }
 
 TEST(OSExchangeDataProviderNonBackedTest, FileNameCloneTest) {
diff --git a/ui/gfx/ca_layer_params.h b/ui/gfx/ca_layer_params.h
index 12e115c..c5fb29b3 100644
--- a/ui/gfx/ca_layer_params.h
+++ b/ui/gfx/ca_layer_params.h
@@ -25,16 +25,6 @@
   CALayerParams& operator=(const CALayerParams& params);
   ~CALayerParams();
 
-  bool operator==(const CALayerParams& params) const {
-    return is_empty == params.is_empty &&
-           ca_context_id == params.ca_context_id &&
-#if BUILDFLAG(IS_MAC)
-           io_surface_mach_port == params.io_surface_mach_port &&
-#endif
-           pixel_size == params.pixel_size &&
-           scale_factor == params.scale_factor;
-  }
-
   // The |is_empty| flag is used to short-circuit code to handle CALayerParams
   // on non-macOS platforms.
   bool is_empty = true;