diff --git a/.vpython3 b/.vpython3
index 8872e87..76fc87e 100644
--- a/.vpython3
+++ b/.vpython3
@@ -260,6 +260,7 @@
 
 # Used by:
 # //third_party/blink/tools/wpt_upload.py
+# //third_party/chromite/bin/cros chrome-sdk
 wheel: <
   name: "infra/python/wheels/crcmod/${vpython_platform}"
   version: "version:1.7.chromium.3"
diff --git a/BUILD.gn b/BUILD.gn
index 449762d..21450b75 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -1129,7 +1129,10 @@
     }
     script_test("wpt_tests_isolate") {
       script = "//third_party/blink/tools/run_wpt_tests.py"
-      args = [ "--product=chrome" ]
+      args = [
+        "--product=chrome",
+        "--no-show-results",
+      ]
       data_deps = [
         "//chrome:chrome",
         "//chrome/test/chromedriver:chromedriver_server",
@@ -1138,6 +1141,7 @@
     }
     script_test("wpt_tests_isolate_content_shell") {
       script = "//third_party/blink/tools/run_wpt_tests.py"
+      args = [ "--no-show-results" ]
       data_deps = [
         ":blink_web_tests_support_data",
         "//third_party/blink/tools:wpt_tests_isolate",
diff --git a/DEPS b/DEPS
index 2982812b..fb645a7a 100644
--- a/DEPS
+++ b/DEPS
@@ -304,15 +304,15 @@
   # 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': 'ecd3a2f865baebb829bdcc37224d1f02e5715e48',
+  'skia_revision': 'e60e02b83f593cb21f60c745790d398fb5dfbf83',
   # 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': '70cd1a917d44a8d3101f5c2ebed022ee60e7b56a',
+  'v8_revision': '4eec6bdd936dc2171b7e6210620212aa101117e7',
   # 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': '3604f6b87112dd33bcbeadcefc547a61933b11df',
+  'angle_revision': 'd0fa9fe214ffb32df35ff3abb27dfa1d6cc0e321',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -331,7 +331,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Fuchsia sdk
   # and whatever else without interference from each other.
-  'fuchsia_version': 'version:11.20230104.1.1',
+  'fuchsia_version': 'version:11.20230105.0.1',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling google-toolbox-for-mac
   # and whatever else without interference from each other.
@@ -355,7 +355,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling freetype
   # and whatever else without interference from each other.
-  'freetype_revision': '63f371367aeefa73541617edfb1dcef9428796fb',
+  'freetype_revision': 'c0b4f6a8625a39ecd4c323d74ddec0e94fca214d',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling freetype
   # and whatever else without interference from each other.
@@ -419,11 +419,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'dawn_revision': 'edbebe3582e600007bb3e6dc5ff0f2349976c014',
+  'dawn_revision': 'f7e6e8c900c7f86c65247f41d9ec3923c35cfcc7',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'quiche_revision': '4bdfa810532a294befb8e8048318309dabd36bd8',
+  'quiche_revision': '9e26f7e9260e94769b9572926574da6a7eef59a8',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ios_webkit
   # and whatever else without interference from each other.
@@ -770,7 +770,7 @@
 
   'src/clank': {
     'url': 'https://chrome-internal.googlesource.com/clank/internal/apps.git' + '@' +
-    '265ed2dd9b6672ec67bba72cf6e3c9825e4b23bd',
+    '92c23bd742f37dd61acdab9cca5db10962e03ee9',
     'condition': 'checkout_android and checkout_src_internal',
   },
 
@@ -959,7 +959,7 @@
     'packages': [
       {
           'package': 'chromium/third_party/androidx',
-          'version': 'LoDIOOQb1UytoPwlIsHvmcBkAKfmkLo7Efb13eCWCvsC',
+          'version': 'TtsLbj2XT2CS11L5aJw_goFx9Xk68cCE47g8a-ygd7QC',
       },
     ],
     'condition': 'checkout_android',
@@ -1176,7 +1176,7 @@
   # Tools used when building Chrome for Chrome OS. This affects both the Simple
   # Chrome workflow, as well as the chromeos-chrome ebuild.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '9780688c7d75305538b1836967679ef47ac06206',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '8c3f4760c825cb3a063758a4beeab96e6aeedfd0',
       'condition': 'checkout_chromeos',
   },
 
@@ -1194,7 +1194,7 @@
 
   # For Linux and Chromium OS.
   'src/third_party/cros_system_api': {
-      'url': Var('chromium_git') + '/chromiumos/platform2/system_api.git' + '@' + 'b33e6de87391826c83fb460690dd3393f53a1eb8',
+      'url': Var('chromium_git') + '/chromiumos/platform2/system_api.git' + '@' + 'a05559581a38748c6669a3ea749f80bf94cbc65c',
       'condition': 'checkout_linux',
   },
 
@@ -1826,10 +1826,10 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + 'd1b65aa5a88f6efd900604dfcda840154e9f16e2',
 
   'src/third_party/webgpu-cts/src':
-    Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '0673bb68c214c0a6c56d00b30e6600dcc9f02b93',
+    Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + 'fd736091bd320166e88fe4c48ec27915abecc752',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '632cd9bb03093333b885a1204adf7a84b442eded',
+    Var('webrtc_git') + '/src.git' + '@' + '7f8680cf6fb239a2118ebae56ad255b9e8ee5fc6',
 
   # Wuffs' canonical repository is at github.com/google/wuffs, but we use
   # Skia's mirror of Wuffs, the same as in upstream Skia's DEPS file.
@@ -1899,7 +1899,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@dec0f00ab1a4d06fb06e3ecd34bc1d8a3d4086df',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@0c791a99ab010a7ca24fdea6653d6f81861c9736',
     'condition': 'checkout_src_internal',
   },
 
@@ -1940,7 +1940,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/media_app/app',
-        'version': 'GSKTn-jOsWa7L__C2ZdOIY5et7zlgCPWOffeEw-1o-MC',
+        'version': '2JWfuHiwvCPuO7eURt2901tKj5rkjSJ_YiDEbVTwAy0C',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
@@ -4700,6 +4700,7 @@
     'pattern': '.',
     'condition': 'checkout_simplechrome_with_vms and not checkout_src_internal',
     'action': [
+      'vpython3',
       'src/third_party/chromite/bin/cros',
       'chrome-sdk',
       '--fallback-versions=20',
@@ -4718,6 +4719,7 @@
     'pattern': '.',
     'condition': 'checkout_simplechrome and not checkout_src_internal',
     'action': [
+      'vpython3',
       'src/third_party/chromite/bin/cros',
       'chrome-sdk',
       '--fallback-versions=20',
@@ -4735,6 +4737,7 @@
     'pattern': '.',
     'condition': 'checkout_simplechrome and checkout_src_internal',
     'action': [
+      'vpython3',
       'src/third_party/chromite/bin/cros',
       'chrome-sdk',
       '--fallback-versions=20',
@@ -4751,6 +4754,7 @@
     'pattern': '.',
     'condition': 'checkout_simplechrome_with_vms and checkout_src_internal',
     'action': [
+      'vpython3',
       'src/third_party/chromite/bin/cros',
       'chrome-sdk',
       '--fallback-versions=20',
@@ -4770,6 +4774,7 @@
     'pattern': '.',
     'condition': 'checkout_simplechrome_with_vms and not checkout_src_internal and checkout_lacros_sdk',
     'action': [
+      'vpython3',
       'src/third_party/chromite/bin/cros',
       'chrome-sdk',
       '--fallback-versions=20',
@@ -4789,6 +4794,7 @@
     'pattern': '.',
     'condition': 'checkout_simplechrome and not checkout_src_internal and checkout_lacros_sdk',
     'action': [
+      'vpython3',
       'src/third_party/chromite/bin/cros',
       'chrome-sdk',
       '--fallback-versions=20',
@@ -4808,6 +4814,7 @@
     'pattern': '.',
     'condition': 'checkout_simplechrome_with_vms and checkout_src_internal and checkout_lacros_sdk',
     'action': [
+      'vpython3',
       'src/third_party/chromite/bin/cros',
       'chrome-sdk',
       '--fallback-versions=20',
@@ -4826,6 +4833,7 @@
     'pattern': '.',
     'condition': 'checkout_simplechrome and checkout_src_internal and checkout_lacros_sdk',
     'action': [
+      'vpython3',
       'src/third_party/chromite/bin/cros',
       'chrome-sdk',
       '--fallback-versions=20',
diff --git a/PRESUBMIT.py b/PRESUBMIT.py
index 352296c2..544a0f1 100644
--- a/PRESUBMIT.py
+++ b/PRESUBMIT.py
@@ -927,7 +927,12 @@
         'absl::Span is banned. Use base::span instead.',
       ),
       True,
-      [_THIRD_PARTY_EXCEPT_BLINK],  # Not an error in third_party folders.
+      [
+        # Needed to use QUICHE API.
+        r'services/network/web_transport\.cc',
+        # Not an error in third_party folders.
+        _THIRD_PARTY_EXCEPT_BLINK
+      ],
     ),
     BanRule(
       r'/\babsl::StatusOr\b',
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index 63ae41d..2476a713 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -1724,6 +1724,8 @@
     "system/privacy_hub/privacy_hub_controller.h",
     "system/privacy_hub/privacy_hub_metrics.cc",
     "system/privacy_hub/privacy_hub_metrics.h",
+    "system/privacy_hub/privacy_hub_notification.cc",
+    "system/privacy_hub/privacy_hub_notification.h",
     "system/privacy_hub/privacy_hub_notification_controller.cc",
     "system/privacy_hub/privacy_hub_notification_controller.h",
     "system/privacy_screen/privacy_screen_feature_pod_controller.cc",
@@ -3166,6 +3168,7 @@
     "system/privacy_hub/microphone_privacy_switch_controller_unittest.cc",
     "system/privacy_hub/privacy_hub_metrics_unittest.cc",
     "system/privacy_hub/privacy_hub_notification_controller_unittest.cc",
+    "system/privacy_hub/privacy_hub_notification_unittest.cc",
     "system/progress_indicator/progress_indicator_animation_registry_unittest.cc",
     "system/progress_indicator/progress_indicator_unittest.cc",
     "system/rotation/rotation_lock_feature_pod_controller_unittest.cc",
diff --git a/ash/public/cpp/fake_hats_bluetooth_revamp_trigger_impl.cc b/ash/public/cpp/fake_hats_bluetooth_revamp_trigger_impl.cc
index 82b899b0..85bac1f 100644
--- a/ash/public/cpp/fake_hats_bluetooth_revamp_trigger_impl.cc
+++ b/ash/public/cpp/fake_hats_bluetooth_revamp_trigger_impl.cc
@@ -11,7 +11,7 @@
     default;
 
 void FakeHatsBluetoothRevampTriggerImpl::TryToShowSurvey() {
-  try_to_show_survey_called_ = true;
+  try_to_show_survey_count_++;
 }
 
 }  // namespace ash
diff --git a/ash/public/cpp/fake_hats_bluetooth_revamp_trigger_impl.h b/ash/public/cpp/fake_hats_bluetooth_revamp_trigger_impl.h
index 64f676b..8aec203 100644
--- a/ash/public/cpp/fake_hats_bluetooth_revamp_trigger_impl.h
+++ b/ash/public/cpp/fake_hats_bluetooth_revamp_trigger_impl.h
@@ -5,6 +5,8 @@
 #ifndef ASH_PUBLIC_CPP_FAKE_HATS_BLUETOOTH_REVAMP_TRIGGER_IMPL_H_
 #define ASH_PUBLIC_CPP_FAKE_HATS_BLUETOOTH_REVAMP_TRIGGER_IMPL_H_
 
+#include <memory>
+
 #include "ash/public/cpp/ash_public_export.h"
 #include "ash/public/cpp/hats_bluetooth_revamp_trigger.h"
 
@@ -21,14 +23,14 @@
       const FakeHatsBluetoothRevampTriggerImpl&) = delete;
   ~FakeHatsBluetoothRevampTriggerImpl() override = default;
 
-  bool try_to_show_survey_called() const { return try_to_show_survey_called_; }
+  size_t try_to_show_survey_count() const { return try_to_show_survey_count_; }
 
   // HatsBluetoothRevampTrigger:
   void TryToShowSurvey() override;
 
  private:
-  // True if `TryToShowSurvey()` was called.
-  bool try_to_show_survey_called_ = false;
+  // Keeps track of how many times `TryToShowSurvey()` was called.
+  size_t try_to_show_survey_count_ = 0;
 };
 
 }  // namespace ash
diff --git a/ash/public/cpp/sensor_disabled_notification_delegate.h b/ash/public/cpp/sensor_disabled_notification_delegate.h
index b86c9a5..cf7869f 100644
--- a/ash/public/cpp/sensor_disabled_notification_delegate.h
+++ b/ash/public/cpp/sensor_disabled_notification_delegate.h
@@ -23,7 +23,9 @@
 
   enum class Sensor {
     kCamera,
+    kMinValue = kCamera,
     kMicrophone,
+    kMaxValue = kMicrophone,
   };
 
   // Returns a list of names of the applications that have attempted to use the
diff --git a/ash/system/bluetooth/bluetooth_detailed_view_controller.cc b/ash/system/bluetooth/bluetooth_detailed_view_controller.cc
index 620740e..9eff3e97 100644
--- a/ash/system/bluetooth/bluetooth_detailed_view_controller.cc
+++ b/ash/system/bluetooth/bluetooth_detailed_view_controller.cc
@@ -113,6 +113,10 @@
 
 void BluetoothDetailedViewController::OnToggleClicked(bool new_state) {
   remote_cros_bluetooth_config_->SetBluetoothEnabledState(new_state);
+
+  if (auto* hats_bluetooth_revamp_trigger = HatsBluetoothRevampTrigger::Get()) {
+    hats_bluetooth_revamp_trigger->TryToShowSurvey();
+  }
 }
 
 void BluetoothDetailedViewController::OnPairNewDeviceRequested() {
diff --git a/ash/system/bluetooth/bluetooth_detailed_view_controller_unittest.cc b/ash/system/bluetooth/bluetooth_detailed_view_controller_unittest.cc
index 49de71c..9731777 100644
--- a/ash/system/bluetooth/bluetooth_detailed_view_controller_unittest.cc
+++ b/ash/system/bluetooth/bluetooth_detailed_view_controller_unittest.cc
@@ -183,8 +183,8 @@
     return bluetooth_config_test_helper()->fake_device_operation_handler();
   }
 
-  FakeHatsBluetoothRevampTriggerImpl* fake_trigger_impl() {
-    return fake_trigger_impl_.get();
+  size_t GetTryToShowSurveyCount() {
+    return fake_trigger_impl_->try_to_show_survey_count();
   }
 
  private:
@@ -246,23 +246,26 @@
 TEST_F(BluetoothDetailedViewControllerTest,
        ChangesBluetoothEnabledStateWhenTogglePressed) {
   EXPECT_EQ(BluetoothSystemState::kEnabled, GetBluetoothAdapterState());
+  EXPECT_EQ(0u, GetTryToShowSurveyCount());
 
   bluetooth_detailed_view_delegate()->OnToggleClicked(/*new_state=*/false);
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(BluetoothSystemState::kDisabling, GetBluetoothAdapterState());
+  EXPECT_EQ(1u, GetTryToShowSurveyCount());
 
   bluetooth_detailed_view_delegate()->OnToggleClicked(/*new_state=*/true);
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(BluetoothSystemState::kEnabling, GetBluetoothAdapterState());
+  EXPECT_EQ(2u, GetTryToShowSurveyCount());
 }
 
 TEST_F(BluetoothDetailedViewControllerTest,
        OnPairNewDeviceRequestedOpensBluetoothDialogWithHatsTrigger) {
-  EXPECT_FALSE(fake_trigger_impl()->try_to_show_survey_called());
+  EXPECT_EQ(0u, GetTryToShowSurveyCount());
   EXPECT_EQ(0, GetSystemTrayClient()->show_bluetooth_pairing_dialog_count());
   bluetooth_detailed_view_delegate()->OnPairNewDeviceRequested();
   EXPECT_EQ(1, GetSystemTrayClient()->show_bluetooth_pairing_dialog_count());
-  EXPECT_TRUE(fake_trigger_impl()->try_to_show_survey_called());
+  EXPECT_EQ(1u, GetTryToShowSurveyCount());
 }
 
 TEST_F(BluetoothDetailedViewControllerTest,
diff --git a/ash/system/bluetooth/bluetooth_feature_pod_controller.cc b/ash/system/bluetooth/bluetooth_feature_pod_controller.cc
index 7a8160a..6e0979a 100644
--- a/ash/system/bluetooth/bluetooth_feature_pod_controller.cc
+++ b/ash/system/bluetooth/bluetooth_feature_pod_controller.cc
@@ -9,6 +9,7 @@
 #include "ash/constants/ash_features.h"
 #include "ash/constants/quick_settings_catalogs.h"
 #include "ash/public/cpp/bluetooth_config_service.h"
+#include "ash/public/cpp/hats_bluetooth_revamp_trigger.h"
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/system/unified/feature_pod_button.h"
@@ -81,6 +82,10 @@
   const bool is_toggled = IsButtonToggled();
   remote_cros_bluetooth_config_->SetBluetoothEnabledState(!is_toggled);
 
+  if (auto* hats_bluetooth_revamp_trigger = HatsBluetoothRevampTrigger::Get()) {
+    hats_bluetooth_revamp_trigger->TryToShowSurvey();
+  }
+
   if (is_toggled) {
     TrackToggleUMA(/*target_toggle_state=*/false);
     return;
@@ -99,6 +104,10 @@
   if (!IsButtonToggled()) {
     remote_cros_bluetooth_config_->SetBluetoothEnabledState(true);
   }
+
+  if (auto* hats_bluetooth_revamp_trigger = HatsBluetoothRevampTrigger::Get()) {
+    hats_bluetooth_revamp_trigger->TryToShowSurvey();
+  }
   tray_controller_->ShowBluetoothDetailedView();
 }
 
diff --git a/ash/system/bluetooth/bluetooth_feature_pod_controller_unittest.cc b/ash/system/bluetooth/bluetooth_feature_pod_controller_unittest.cc
index 8e57286..9517d0df 100644
--- a/ash/system/bluetooth/bluetooth_feature_pod_controller_unittest.cc
+++ b/ash/system/bluetooth/bluetooth_feature_pod_controller_unittest.cc
@@ -10,6 +10,8 @@
 
 #include "ash/constants/ash_features.h"
 #include "ash/constants/quick_settings_catalogs.h"
+#include "ash/public/cpp/fake_hats_bluetooth_revamp_trigger_impl.h"
+#include "ash/public/cpp/hats_bluetooth_revamp_trigger.h"
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/system/unified/detailed_view_controller.h"
@@ -79,6 +81,8 @@
 
     GetPrimaryUnifiedSystemTray()->ShowBubble();
 
+    fake_trigger_impl_ = std::make_unique<FakeHatsBluetoothRevampTriggerImpl>();
+
     bluetooth_pod_controller_ =
         std::make_unique<BluetoothFeaturePodController>(tray_controller());
     if (IsQsRevampEnabled()) {
@@ -268,6 +272,10 @@
     return GetPrimaryUnifiedSystemTray()->bubble()->unified_view();
   }
 
+  size_t GetTryToShowSurveyCount() {
+    return fake_trigger_impl_->try_to_show_survey_count();
+  }
+
  protected:
   std::unique_ptr<FeaturePodButton> feature_pod_button_;
   std::unique_ptr<FeatureTile> feature_tile_;
@@ -277,6 +285,7 @@
     return ash_test_helper()->bluetooth_config_test_helper();
   }
 
+  std::unique_ptr<FakeHatsBluetoothRevampTriggerImpl> fake_trigger_impl_;
   std::unique_ptr<BluetoothFeaturePodController> bluetooth_pod_controller_;
   base::test::ScopedFeatureList feature_list_;
 };
@@ -305,11 +314,15 @@
 }
 
 TEST_P(BluetoothFeaturePodControllerTest, PressingIconOrLabelChangesBluetooth) {
+  EXPECT_EQ(0u, GetTryToShowSurveyCount());
   EXPECT_TRUE(IsButtonToggled());
   PressIcon();
   EXPECT_FALSE(IsButtonToggled());
+  EXPECT_EQ(1u, GetTryToShowSurveyCount());
+
   PressLabel();
   EXPECT_TRUE(IsButtonToggled());
+  EXPECT_EQ(2u, GetTryToShowSurveyCount());
 }
 
 TEST_P(BluetoothFeaturePodControllerTest, HasCorrectMetadataWhenOff) {
diff --git a/ash/system/privacy_hub/privacy_hub_notification.cc b/ash/system/privacy_hub/privacy_hub_notification.cc
new file mode 100644
index 0000000..22d4694e
--- /dev/null
+++ b/ash/system/privacy_hub/privacy_hub_notification.cc
@@ -0,0 +1,116 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/system/privacy_hub/privacy_hub_notification.h"
+
+#include "ash/system/privacy_hub/privacy_hub_notification_controller.h"
+#include "base/containers/contains.h"
+#include "base/dcheck_is_on.h"
+#include "components/vector_icons/vector_icons.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/message_center/message_center.h"
+#include "ui/message_center/public/cpp/notification.h"
+
+namespace ash {
+
+PrivacyHubNotificationClickDelegate::PrivacyHubNotificationClickDelegate(
+    base::RepeatingClosure button_click)
+    : button_callback_(std::move(button_click)) {}
+PrivacyHubNotificationClickDelegate::~PrivacyHubNotificationClickDelegate() =
+    default;
+
+void PrivacyHubNotificationClickDelegate::Click(
+    const absl::optional<int>& button_index,
+    const absl::optional<std::u16string>& reply) {
+  if (button_index.has_value()) {
+    button_callback_.Run();
+  } else {
+    if (!message_callback_.is_null()) {
+      message_callback_.Run();
+    }
+
+    PrivacyHubNotificationController::OpenPrivacyHubSettingsPage();
+  }
+}
+
+void PrivacyHubNotificationClickDelegate::SetMessageClickCallback(
+    base::RepeatingClosure callback) {
+  message_callback_ = std::move(callback);
+}
+
+PrivacyHubNotification::PrivacyHubNotification(
+    const std::string& id,
+    const int title_id,
+    const MessageIds& message_ids,
+    const SensorSet sensors_for_apps,
+    const scoped_refptr<PrivacyHubNotificationClickDelegate> delegate,
+    const ash::NotificationCatalogName catalog_name,
+    const int button_id)
+    : id_(id), message_ids_(message_ids), sensors_for_apps_(sensors_for_apps) {
+  DCHECK(!message_ids_.empty());
+  DCHECK(message_ids_.size() < 2u || !sensors_for_apps_.Empty())
+      << "Specify at least one sensor when providing more than one message ID";
+  DCHECK(delegate);
+
+  message_center::RichNotificationData optional_fields;
+  optional_fields.remove_on_click = true;
+  optional_fields.buttons.emplace_back(l10n_util::GetStringUTF16(button_id));
+
+  builder_.SetId(id)
+      .SetCatalogName(catalog_name)
+      .SetDelegate(std::move(delegate))
+      .SetTitleId(title_id)
+      .SetOptionalFields(optional_fields)
+      .SetSmallImage(vector_icons::kSettingsIcon)
+      .SetWarningLevel(message_center::SystemNotificationWarningLevel::NORMAL);
+}
+
+PrivacyHubNotification::PrivacyHubNotification(PrivacyHubNotification&&) =
+    default;
+PrivacyHubNotification& PrivacyHubNotification::operator=(
+    PrivacyHubNotification&&) = default;
+PrivacyHubNotification::~PrivacyHubNotification() = default;
+
+void PrivacyHubNotification::Show() {
+  const std::vector<std::u16string> apps = GetAppsAccessingSensors();
+
+  if (const size_t num_apps = apps.size(); num_apps < message_ids_.size()) {
+    builder_.SetMessageWithArgs(message_ids_.at(num_apps), apps);
+  } else {
+    builder_.SetMessageId(message_ids_.at(0));
+  }
+
+  message_center::MessageCenter::Get()->AddNotification(builder_.BuildPtr());
+}
+
+void PrivacyHubNotification::Hide() {
+  message_center::MessageCenter::Get()->RemoveNotification(id_,
+                                                           /*by_user=*/false);
+}
+
+std::vector<std::u16string> PrivacyHubNotification::GetAppsAccessingSensors()
+    const {
+  std::vector<std::u16string> app_names;
+
+  if (SensorDisabledNotificationDelegate* delegate =
+          SensorDisabledNotificationDelegate::Get()) {
+    for (SensorDisabledNotificationDelegate::Sensor sensor :
+         sensors_for_apps_) {
+      for (const std::u16string& app :
+           delegate->GetAppsAccessingSensor(sensor)) {
+        if (!base::Contains(app_names, app)) {
+          app_names.push_back(app);
+        }
+        if (app_names.size() == message_ids_.size()) {
+          return app_names;
+        }
+      }
+    }
+  }
+
+  return app_names;
+}
+
+}  // namespace ash
diff --git a/ash/system/privacy_hub/privacy_hub_notification.h b/ash/system/privacy_hub/privacy_hub_notification.h
new file mode 100644
index 0000000..09aa6fa
--- /dev/null
+++ b/ash/system/privacy_hub/privacy_hub_notification.h
@@ -0,0 +1,94 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_SYSTEM_PRIVACY_HUB_PRIVACY_HUB_NOTIFICATION_H_
+#define ASH_SYSTEM_PRIVACY_HUB_PRIVACY_HUB_NOTIFICATION_H_
+
+#include <string>
+#include <vector>
+
+#include "ash/ash_export.h"
+#include "ash/public/cpp/sensor_disabled_notification_delegate.h"
+#include "ash/public/cpp/system_notification_builder.h"
+#include "base/containers/enum_set.h"
+#include "base/functional/callback_forward.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "ui/message_center/public/cpp/notification_delegate.h"
+
+namespace ash {
+
+// A custom delegate that ensures consistent handling of notification
+// interactions across all Privacy Hub notifications.
+class ASH_EXPORT PrivacyHubNotificationClickDelegate
+    : public message_center::NotificationDelegate {
+ public:
+  // The `button_callback` will be executed when the only button of the
+  // notification is clicked.
+  explicit PrivacyHubNotificationClickDelegate(
+      base::RepeatingClosure button_click);
+
+  // message_center::NotificationDelegate:
+  void Click(const absl::optional<int>& button_index,
+             const absl::optional<std::u16string>& reply) override;
+
+  // When clicking on the notification message execute this `callback`.
+  void SetMessageClickCallback(base::RepeatingClosure callback);
+
+ private:
+  ~PrivacyHubNotificationClickDelegate() override;
+
+  base::RepeatingClosure button_callback_;
+  base::RepeatingClosure message_callback_;
+};
+
+// This class wraps `SystemNotificationBuilder` and adds additional constraints
+// and shared behavior that applies to all Privacy Hub notifications.
+class ASH_EXPORT PrivacyHubNotification {
+ public:
+  using MessageIds = std::vector<int>;
+  using SensorSet =
+      base::EnumSet<SensorDisabledNotificationDelegate::Sensor,
+                    SensorDisabledNotificationDelegate::Sensor::kMinValue,
+                    SensorDisabledNotificationDelegate::Sensor::kMaxValue>;
+
+  // Create a new notification. When calling `Show()` and `sensors_for_apps`
+  // contains at least one sensor it will try to replace currently used apps
+  // by the sensor(s) in the message. This is only possible if there are less
+  // than `message_ids.size()` apps active for the sensor(s) otherwise the
+  // generic message at index 0 will be used again. `message_ids` must not be
+  // empty and `delegate` must not be null.
+  PrivacyHubNotification(
+      const std::string& id,
+      int title_id,
+      const MessageIds& message_ids,
+      SensorSet sensors_for_apps,
+      scoped_refptr<PrivacyHubNotificationClickDelegate> delegate,
+      ash::NotificationCatalogName catalog_name,
+      int action_button_id);
+  PrivacyHubNotification(PrivacyHubNotification&&);
+  PrivacyHubNotification& operator=(PrivacyHubNotification&&);
+  ~PrivacyHubNotification();
+
+  // Show the notification to the user. If more than
+  // one `message_ids_` exists will attempt to use the correct one for the
+  // number of apps accessing the `sensors_for_apps_`.
+  void Show();
+
+  // Hide the notification from the user.
+  void Hide();
+
+ private:
+  // Get names of apps accessing the `sensors_for_apps_`. At most
+  // `message_ids_.size()` elements will be returned.
+  std::vector<std::u16string> GetAppsAccessingSensors() const;
+
+  std::string id_;
+  SystemNotificationBuilder builder_;
+  MessageIds message_ids_;
+  SensorSet sensors_for_apps_;
+};
+
+}  // namespace ash
+
+#endif
diff --git a/ash/system/privacy_hub/privacy_hub_notification_unittest.cc b/ash/system/privacy_hub/privacy_hub_notification_unittest.cc
new file mode 100644
index 0000000..985b7e7
--- /dev/null
+++ b/ash/system/privacy_hub/privacy_hub_notification_unittest.cc
@@ -0,0 +1,171 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/system/privacy_hub/privacy_hub_notification.h"
+
+#include "ash/public/cpp/test/test_system_tray_client.h"
+#include "ash/strings/grit/ash_strings.h"
+#include "ash/test/ash_test_base.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/test/bind.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/message_center/message_center.h"
+
+namespace ash {
+namespace {
+
+class FakeSensorDisabledNotificationDelegate
+    : public SensorDisabledNotificationDelegate {
+ public:
+  std::vector<std::u16string> GetAppsAccessingSensor(Sensor sensor) override {
+    return apps_;
+  }
+
+  void LaunchApp(const std::u16string& app_name) { apps_.push_back(app_name); }
+
+ private:
+  std::vector<std::u16string> apps_;
+};
+
+}  // namespace
+
+class PrivacyHubNotificationTest : public AshTestBase {
+ public:
+  static constexpr char kId[] = "unit.test";
+
+  PrivacyHubNotificationTest()
+      : notification_(
+            kId,
+            IDS_PRIVACY_HUB_MICROPHONE_AND_CAMERA_OFF_NOTIFICATION_TITLE,
+            {IDS_PRIVACY_HUB_MICROPHONE_AND_CAMERA_OFF_NOTIFICATION_MESSAGE,
+             IDS_PRIVACY_HUB_MICROPHONE_AND_CAMERA_OFF_NOTIFICATION_MESSAGE_WITH_ONE_APP_NAME,
+             IDS_PRIVACY_HUB_MICROPHONE_AND_CAMERA_OFF_NOTIFICATION_MESSAGE_WITH_TWO_APP_NAMES},
+            {SensorDisabledNotificationDelegate::Sensor::kMicrophone},
+            base::MakeRefCounted<PrivacyHubNotificationClickDelegate>(
+                base::DoNothing()),
+            ash::NotificationCatalogName::kTestCatalogName,
+            IDS_PRIVACY_HUB_MICROPHONE_AND_CAMERA_OFF_NOTIFICATION_BUTTON) {}
+  ~PrivacyHubNotificationTest() override = default;
+
+  PrivacyHubNotification& notification() { return notification_; }
+
+  FakeSensorDisabledNotificationDelegate& sensor_delegate() {
+    return sensor_delegate_;
+  }
+
+ private:
+  FakeSensorDisabledNotificationDelegate sensor_delegate_;
+  PrivacyHubNotification notification_;
+};
+
+using PrivacyHubNotificationClickDelegateTest = AshTestBase;
+
+TEST_F(PrivacyHubNotificationClickDelegateTest, Click) {
+  size_t button_clicked = 0;
+  size_t message_clicked = 0;
+  scoped_refptr<PrivacyHubNotificationClickDelegate> delegate =
+      base::MakeRefCounted<PrivacyHubNotificationClickDelegate>(
+          base::BindLambdaForTesting(
+              [&button_clicked]() { button_clicked++; }));
+
+  ASSERT_EQ(GetSystemTrayClient()->show_os_settings_privacy_hub_count(), 0);
+
+  // Without callback only Privacy Hub should be opened when clicking on the
+  // message.
+  delegate->Click(absl::nullopt, absl::nullopt);
+
+  EXPECT_EQ(GetSystemTrayClient()->show_os_settings_privacy_hub_count(), 1);
+  EXPECT_EQ(button_clicked, 0u);
+  EXPECT_EQ(message_clicked, 0u);
+
+  // Click the button.
+  delegate->Click(0, absl::nullopt);
+
+  EXPECT_EQ(GetSystemTrayClient()->show_os_settings_privacy_hub_count(), 1);
+  EXPECT_EQ(button_clicked, 1u);
+  EXPECT_EQ(message_clicked, 0u);
+
+  // Add a message callback.
+  delegate->SetMessageClickCallback(
+      base::BindLambdaForTesting([&message_clicked]() { message_clicked++; }));
+
+  // When clicking the button, only the button callback should be executed.
+  delegate->Click(0, absl::nullopt);
+
+  EXPECT_EQ(GetSystemTrayClient()->show_os_settings_privacy_hub_count(), 1);
+  EXPECT_EQ(button_clicked, 2u);
+  EXPECT_EQ(message_clicked, 0u);
+
+  // Clicking the message should open Privacy Hub and execute the message
+  // callback.
+  delegate->Click(absl::nullopt, absl::nullopt);
+
+  EXPECT_EQ(GetSystemTrayClient()->show_os_settings_privacy_hub_count(), 2);
+  EXPECT_EQ(button_clicked, 2u);
+  EXPECT_EQ(message_clicked, 1u);
+}
+
+TEST_F(PrivacyHubNotificationTest, ShowAndHide) {
+  EXPECT_FALSE(message_center::MessageCenter::Get()->FindNotificationById(kId));
+
+  notification().Show();
+
+  EXPECT_TRUE(message_center::MessageCenter::Get()->FindNotificationById(kId));
+
+  notification().Hide();
+
+  EXPECT_FALSE(message_center::MessageCenter::Get()->FindNotificationById(kId));
+}
+
+TEST_F(PrivacyHubNotificationTest, WithApps) {
+  // No apps -> generic notification text.
+  notification().Show();
+
+  message_center::Notification* notification_ptr =
+      message_center::MessageCenter::Get()->FindNotificationById(kId);
+
+  ASSERT_TRUE(notification_ptr);
+  EXPECT_EQ(
+      notification_ptr->message(),
+      l10n_util::GetStringUTF16(
+          IDS_PRIVACY_HUB_MICROPHONE_AND_CAMERA_OFF_NOTIFICATION_MESSAGE));
+
+  // Launch a single app -> notification with message for one app.
+  const std::u16string app1 = u"test1";
+  sensor_delegate().LaunchApp(app1);
+
+  notification().Show();
+  notification_ptr =
+      message_center::MessageCenter::Get()->FindNotificationById(kId);
+  EXPECT_EQ(
+      notification_ptr->message(),
+      l10n_util::GetStringFUTF16(
+          IDS_PRIVACY_HUB_MICROPHONE_AND_CAMERA_OFF_NOTIFICATION_MESSAGE_WITH_ONE_APP_NAME,
+          app1));
+
+  // Launch a second app -> notification with message for two apps.
+  const std::u16string app2 = u"test2";
+  sensor_delegate().LaunchApp(app2);
+
+  notification().Show();
+  notification_ptr =
+      message_center::MessageCenter::Get()->FindNotificationById(kId);
+  EXPECT_TRUE(base::Contains(notification_ptr->message(), app1));
+  EXPECT_TRUE(base::Contains(notification_ptr->message(), app2));
+
+  // More than two apps -> generic notification text.
+  const std::u16string app3 = u"test3";
+  sensor_delegate().LaunchApp(app3);
+
+  notification().Show();
+  notification_ptr =
+      message_center::MessageCenter::Get()->FindNotificationById(kId);
+  EXPECT_EQ(
+      notification_ptr->message(),
+      l10n_util::GetStringUTF16(
+          IDS_PRIVACY_HUB_MICROPHONE_AND_CAMERA_OFF_NOTIFICATION_MESSAGE));
+}
+
+}  // namespace ash
diff --git a/ash/test/ash_test_base.cc b/ash/test/ash_test_base.cc
index 459a8b58..3d97c0c 100644
--- a/ash/test/ash_test_base.cc
+++ b/ash/test/ash_test_base.cc
@@ -250,6 +250,7 @@
     int shell_window_id,
     views::WidgetDelegate* delegate) {
   TestWidgetBuilder builder;
+  builder.SetWindowTitle(u"Window " + base::NumberToString16(shell_window_id));
   if (app_type != AppType::NON_APP) {
     builder.SetWindowProperty(aura::client::kAppType,
                               static_cast<int>(app_type));
@@ -322,6 +323,7 @@
       .SetDelegate(delegate)
       .SetWindowType(type)
       .SetWindowId(id)
+      .SetWindowTitle(u"Window " + base::NumberToString16(id))
       .AllowAllWindowStates()
       .Build()
       .release();
diff --git a/ash/test/test_widget_builder.cc b/ash/test/test_widget_builder.cc
index 61d5e011..c634763c 100644
--- a/ash/test/test_widget_builder.cc
+++ b/ash/test/test_widget_builder.cc
@@ -100,6 +100,13 @@
   return *this;
 }
 
+TestWidgetBuilder& TestWidgetBuilder::SetWindowTitle(
+    const std::u16string& title) {
+  DCHECK(!built_);
+  window_title_ = title;
+  return *this;
+}
+
 TestWidgetBuilder& TestWidgetBuilder::SetShow(bool show) {
   DCHECK(!built_);
   show_ = show;
@@ -121,6 +128,9 @@
   widget->Init(std::move(widget_init_params_));
   if (window_id_ != aura::Window::kInitialId)
     widget->GetNativeWindow()->SetId(window_id_);
+  if (!window_title_.empty()) {
+    widget->GetNativeWindow()->SetTitle(window_title_);
+  }
   if (show_)
     widget->Show();
   return widget;
@@ -136,6 +146,9 @@
   widget->Init(std::move(widget_init_params_));
   if (window_id_ != aura::Window::kInitialId)
     widget->GetNativeWindow()->SetId(window_id_);
+  if (!window_title_.empty()) {
+    widget->GetNativeWindow()->SetTitle(window_title_);
+  }
   if (show_)
     widget->Show();
   return widget;
diff --git a/ash/test/test_widget_builder.h b/ash/test/test_widget_builder.h
index 1c387c3..6b4e5b7 100644
--- a/ash/test/test_widget_builder.h
+++ b/ash/test/test_widget_builder.h
@@ -49,6 +49,11 @@
   // Set the window id used on the window of a test widget.
   TestWidgetBuilder& SetWindowId(int window_id);
 
+  // Having a non-empty title helps avoid accessibility paint check failures
+  // in tests. For instance, `WindowMiniView` gets its accessible name from
+  // the window title.
+  TestWidgetBuilder& SetWindowTitle(const std::u16string& title);
+
   // A widget is shown when created by default. Use this if you want not
   // to show when created.
   TestWidgetBuilder& SetShow(bool show);
@@ -85,6 +90,7 @@
  private:
   views::Widget::InitParams widget_init_params_;
   int window_id_ = aura::Window::kInitialId;
+  std::u16string window_title_ = std::u16string();
   bool show_ = true;
   bool built_ = false;
 };
diff --git a/ash/test/test_window_builder.cc b/ash/test/test_window_builder.cc
index fa0d58d..c8978f5 100644
--- a/ash/test/test_window_builder.cc
+++ b/ash/test/test_window_builder.cc
@@ -24,6 +24,7 @@
       bounds_(others.bounds_),
       init_properties_(std::move(others.init_properties_)),
       window_id_(others.window_id_),
+      window_title_(others.window_title_),
       show_(others.show_) {
   DCHECK(!others.built_);
   others.built_ = true;
@@ -51,6 +52,13 @@
   return *this;
 }
 
+TestWindowBuilder& TestWindowBuilder::SetWindowTitle(
+    const std::u16string& title) {
+  DCHECK(!built_);
+  window_title_ = title;
+  return *this;
+}
+
 TestWindowBuilder& TestWindowBuilder::SetBounds(const gfx::Rect& bounds) {
   DCHECK(!built_);
   bounds_ = bounds;
@@ -102,6 +110,9 @@
   window->AcquireAllPropertiesFrom(std::move(init_properties_));
   if (window_id_ != aura::Window::kInitialId)
     window->SetId(window_id_);
+  if (!window_title_.empty()) {
+    window->SetTitle(window_title_);
+  }
   if (parent_) {
     if (!bounds_.IsEmpty())
       window->SetBounds(bounds_);
diff --git a/ash/test/test_window_builder.h b/ash/test/test_window_builder.h
index d7b14d1b..3714149e 100644
--- a/ash/test/test_window_builder.h
+++ b/ash/test/test_window_builder.h
@@ -36,6 +36,11 @@
   TestWindowBuilder& SetWindowId(int id);
   TestWindowBuilder& SetBounds(const gfx::Rect& bounds);
 
+  // Having a non-empty title helps avoid accessibility paint check failures
+  // in tests. For instance, `WindowMiniView` gets its accessible name from
+  // the window title.
+  TestWindowBuilder& SetWindowTitle(const std::u16string& title);
+
   // Set a WindowDelegate used by a test window.
   TestWindowBuilder& SetDelegate(aura::WindowDelegate* delegate);
 
@@ -74,6 +79,7 @@
   gfx::Rect bounds_;
   ui::PropertyHandler init_properties_;
   int window_id_ = aura::Window::kInitialId;
+  std::u16string window_title_ = std::u16string();
   bool show_ = true;
   bool built_ = false;
 };
diff --git a/ash/webui/personalization_app/resources/BUILD.gn b/ash/webui/personalization_app/resources/BUILD.gn
index dfad5dc..50ede63e 100644
--- a/ash/webui/personalization_app/resources/BUILD.gn
+++ b/ash/webui/personalization_app/resources/BUILD.gn
@@ -207,6 +207,7 @@
     "//third_party/polymer/v3_0:library",
     "//ui/webui/resources:library",
     "//ui/webui/resources/cr_components/color_change_listener:build_ts",
+    "//ui/webui/resources/cr_components/localized_link:build_ts",
     "//ui/webui/resources/mojo:library",
   ]
 
diff --git a/ash/webui/shortcut_customization_ui/backend/accelerator_configuration_provider_unittest.cc b/ash/webui/shortcut_customization_ui/backend/accelerator_configuration_provider_unittest.cc
index 79aa475..df53855 100644
--- a/ash/webui/shortcut_customization_ui/backend/accelerator_configuration_provider_unittest.cc
+++ b/ash/webui/shortcut_customization_ui/backend/accelerator_configuration_provider_unittest.cc
@@ -410,17 +410,17 @@
       // search + esc -> search + esc
       {/*trigger_on_press=*/true, ui::VKEY_ESCAPE, ui::EF_COMMAND_DOWN,
        SHOW_TASK_MANAGER},
-      // shift + zoom -> shift + search + F4
-      {/*trigger_on_press=*/true, ui::VKEY_F4,
+      // shift + zoom -> shift + search + F3
+      {/*trigger_on_press=*/true, ui::VKEY_F3,
        ui::EF_SHIFT_DOWN | ui::EF_COMMAND_DOWN, TOGGLE_FULLSCREEN},
-      // zoom -> search + F4
-      {/*trigger_on_press=*/true, ui::VKEY_F4, ui::EF_COMMAND_DOWN,
+      // zoom -> search + F3
+      {/*trigger_on_press=*/true, ui::VKEY_F3, ui::EF_COMMAND_DOWN,
        TOGGLE_FULLSCREEN},
-      // brightness_up -> search + F7
-      {/*trigger_on_press=*/true, ui::VKEY_F7, ui::EF_COMMAND_DOWN,
+      // brightness_up -> search + F6
+      {/*trigger_on_press=*/true, ui::VKEY_F6, ui::EF_COMMAND_DOWN,
        BRIGHTNESS_UP},
-      // alt + brightness_up -> alt + search + F7
-      {/*trigger_on_press=*/true, ui::VKEY_F7,
+      // alt + brightness_up -> alt + search + F6
+      {/*trigger_on_press=*/true, ui::VKEY_F6,
        ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN, KEYBOARD_BRIGHTNESS_UP},
       // When [search] is part of the original accelerator, no remapping is
       // done.
diff --git a/ash/webui/shortcut_customization_ui/resources/js/accelerator_edit_view.html b/ash/webui/shortcut_customization_ui/resources/js/accelerator_edit_view.html
index 98bab9a..46b3dd8 100644
--- a/ash/webui/shortcut_customization_ui/resources/js/accelerator_edit_view.html
+++ b/ash/webui/shortcut_customization_ui/resources/js/accelerator_edit_view.html
@@ -11,6 +11,16 @@
     padding-top: 8px;
   }
 
+  #acceleratorView {
+    align-items: center;
+    display: flex;
+    flex: 1;
+  }
+
+  #acceleratorView accelerator-view {
+    flex: 1;
+  }
+
   #acceleratorInfoText {
     padding-top: 10px
   }
@@ -42,14 +52,12 @@
 </style>
 
 <div id="container">
-  <div id="acceleratorContainer">
-    <div id="acceleratorView">
-      <accelerator-view id="acceleratorItem"
-          accelerator-info=[[acceleratorInfo]] view-state={{viewState}}
-          status-message={{statusMessage}} has-error={{hasError}}
-          action=[[action]] source=[[source]]>
-      </accelerator-view>
-    </div>
+  <div id="acceleratorView">
+    <accelerator-view id="acceleratorItem"
+        accelerator-info="[[acceleratorInfo]]" view-state="{{viewState}}"
+        status-message="{{statusMessage}}" has-error="{{hasError}}"
+        action="[[action]]" source="[[source]]">
+    </accelerator-view>
   </div>
   <template id="buttonContainer" is="dom-if" if="[[!acceleratorInfo.locked]]">
     <div id="editButtonsContainer" hidden="[[showEditView(viewState)]]">
@@ -72,10 +80,6 @@
       </cr-button>
     </div>
   </template>
-  <div id="lockContainer" hidden="[[!acceleratorInfo.locked]]">
-    <iron-icon id="lockedIcon" icon="shortcut-customization:lock">
-    </iron-icon>
-  </div>
 </div>
 
 <div id="acceleratorInfoText" hidden="[[!showEditView(viewState)]]">
diff --git a/ash/webui/shortcut_customization_ui/resources/js/accelerator_row.html b/ash/webui/shortcut_customization_ui/resources/js/accelerator_row.html
index dcacad0..caaef136 100644
--- a/ash/webui/shortcut_customization_ui/resources/js/accelerator_row.html
+++ b/ash/webui/shortcut_customization_ui/resources/js/accelerator_row.html
@@ -1,49 +1,37 @@
 <style include="shortcut-customization-shared">
   #container {
-    border-bottom: var(--cr-separator-line);
-    display: flex;
-    flex-direction: row;
+    align-items: center;
+    display: grid;
+    grid-template-columns: 286px 1fr;
+    grid-template-rows: repeat(var(--accelerator-row-num-accels), 52px);
+  }
+
+  accelerator-view {
+    grid-column-start: 2;
     padding-bottom: 10px;
     padding-top: 10px;
   }
 
-  #acceleratorViewList {
-    display: grid;
-    flex: 1;
-    row-gap: 5px;
+  accelerator-view:not(:first-of-type) {
+    border-top: var(--cr-separator-line);
   }
 
   #descriptionText {
     align-items: center;
     display: flex;
-    width: 320px;
-  }
-
-  #lockIconContainer {
-    align-items: center;
-    display: flex;
-  }
-
-  #lockIconContainer[hidden] {
-    display: none;  /* Required for flexbox hidden. */
   }
 </style>
 
 <div id="container">
   <div id="descriptionText">[[description]]</div>
-  <div id="acceleratorViewList">
-    <template is="dom-if" if="[[isDefaultLayout(layoutStyle)]]">
-      <template is="dom-repeat" items="[[acceleratorInfos]]">
-        <accelerator-view class="accelerator-item" accelerator-info="[[item]]"
-            action="[[action]]" source="[[source]]">
-        </accelerator-view>
-      </template>
+  <template is="dom-if" if="[[isDefaultLayout(layoutStyle)]]">
+    <template is="dom-repeat" items="[[acceleratorInfos]]">
+      <accelerator-view class="accelerator-item" accelerator-info="[[item]]"
+          action="[[action]]" source="[[source]]" source-is-locked="[[isLocked]]">
+      </accelerator-view>
     </template>
-    <template is="dom-if" if="[[isTextLayout(layoutStyle)]]">
-      <text-accelerator text="[[acceleratorText]]"></text-accelerator>
-    </template>
-  </div>
-  <div id="lockIconContainer" hidden="[[!shouldShowLockIcon(isLocked)]]">
-    <iron-icon icon="shortcut-customization:lock"></iron-icon>
-  </div>
+  </template>
+  <template is="dom-if" if="[[isTextLayout(layoutStyle)]]">
+    <text-accelerator text="[[acceleratorText]]"></text-accelerator>
+  </template>
 </div>
\ No newline at end of file
diff --git a/ash/webui/shortcut_customization_ui/resources/js/accelerator_row.ts b/ash/webui/shortcut_customization_ui/resources/js/accelerator_row.ts
index bd43fc9..b3621c1 100644
--- a/ash/webui/shortcut_customization_ui/resources/js/accelerator_row.ts
+++ b/ash/webui/shortcut_customization_ui/resources/js/accelerator_row.ts
@@ -98,6 +98,14 @@
     }
   }
 
+  override ready(): void {
+    super.ready();
+    const numberOfAccelerators = this.layoutStyle == LayoutStyle.kDefault ?
+        this.acceleratorInfos.length :
+        1;
+    this.updateStyles({'--accelerator-row-num-accels': numberOfAccelerators});
+  }
+
   protected onSourceChanged(): void {
     this.shortcutInterfaceProvider.isMutable(this.source)
         .then(({isMutable}) => {
@@ -116,14 +124,6 @@
     return this.layoutStyle === LayoutStyle.kText;
   }
 
-  private shouldShowLockIcon(): boolean {
-    if (isCustomizationDisabled()) {
-      return false;
-    }
-
-    return this.isLocked;
-  }
-
   private showDialog(): void {
     if (isCustomizationDisabled()) {
       return;
diff --git a/ash/webui/shortcut_customization_ui/resources/js/accelerator_subsection.html b/ash/webui/shortcut_customization_ui/resources/js/accelerator_subsection.html
index 2f7c2836..60abc753 100644
--- a/ash/webui/shortcut_customization_ui/resources/js/accelerator_subsection.html
+++ b/ash/webui/shortcut_customization_ui/resources/js/accelerator_subsection.html
@@ -6,6 +6,10 @@
     height: 48px;
   }
 
+  accelerator-row:not(:last-of-type) {
+    border-bottom: var(--cr-separator-line);
+  }
+
   #rowList {
     display: flex;
     flex-direction: column;
@@ -18,7 +22,7 @@
     <template id="list" is="dom-repeat" items="[[accelRowDataArray]]">
       <accelerator-row
           accelerator-infos="[[item.acceleratorInfos]]"
-          accelerator-text="TODO(cambickel) add text from mojo when implemented"
+          accelerator-text="TODO"
           action="[[item.layoutInfo.action]]"
           description="[[item.layoutInfo.description]]"
           layout-style="[[item.layoutInfo.style]]"
diff --git a/ash/webui/shortcut_customization_ui/resources/js/accelerator_view.html b/ash/webui/shortcut_customization_ui/resources/js/accelerator_view.html
index a6ecdf9..84e55df 100644
--- a/ash/webui/shortcut_customization_ui/resources/js/accelerator_view.html
+++ b/ash/webui/shortcut_customization_ui/resources/js/accelerator_view.html
@@ -2,6 +2,12 @@
   .flex-row {
     display: flex;
     flex-direction: row;
+    justify-content: space-between;
+  }
+
+  #accelerator-keys {
+    display: flex;
+    flex-direction: row;
   }
 
   #container:focus {
@@ -11,19 +17,35 @@
   #acceleratorSeparator {
     align-items: center;
     display: flex;
-    padding-inline-start: 5px;
+    padding-inline-end: 5px;
+  }
+
+  #lockIconContainer {
+    align-items: center;
+    display: flex;
+  }
+
+  #lockIconContainer[hidden] {
+    display: none;  /* Required for flexbox hidden. */
   }
 </style>
 
 <div id="container" class="flex-row" tabindex="0">
   <template is="dom-if" if="[[!showEditView(viewState)]]">
-    <template is="dom-repeat" items=[[modifiers]]>
-      <input-key key=[[item]] key-state="modifier-selected"></input-key>
-    </template>
-    <input-key
-        key="[[acceleratorInfo.layoutProperties.standardAccelerator.keyDisplay]]"
-        key-state="alpha-numeric-selected">
-    </input-key>
+    <div id="accelerator-keys">
+      <template is="dom-repeat" items="[[modifiers]]">
+        <input-key key="[[item]]" key-state="modifier-selected"></input-key>
+      </template>
+      <input-key
+          key="[[acceleratorInfo.layoutProperties.standardAccelerator.keyDisplay]]"
+          key-state="alpha-numeric-selected">
+      </input-key>
+    </div>
+    <div 
+      id="lockIconContainer" 
+      hidden="[[!shouldShowLockIcon(acceleratorInfo.locked, sourceIsLocked)]]">
+      <iron-icon icon="shortcut-customization:lock"></iron-icon>
+    </div>
   </template>
   <template is="dom-if" if="[[showEditView(viewState)]]">
     <div id="editContainer" class="flex-row">
diff --git a/ash/webui/shortcut_customization_ui/resources/js/accelerator_view.ts b/ash/webui/shortcut_customization_ui/resources/js/accelerator_view.ts
index fdd57df..907a6be 100644
--- a/ash/webui/shortcut_customization_ui/resources/js/accelerator_view.ts
+++ b/ash/webui/shortcut_customization_ui/resources/js/accelerator_view.ts
@@ -13,7 +13,7 @@
 import {getShortcutProvider} from './mojo_interface_provider.js';
 import {ModifierKeyCodes} from './shortcut_input.js';
 import {Accelerator, AcceleratorConfigResult, AcceleratorSource, Modifier, ShortcutProviderInterface, StandardAcceleratorInfo} from './shortcut_types.js';
-import {areAcceleratorsEqual, createEmptyAcceleratorInfo, getAccelerator} from './shortcut_utils.js';
+import {areAcceleratorsEqual, createEmptyAcceleratorInfo, getAccelerator, isCustomizationDisabled} from './shortcut_utils.js';
 
 export interface AcceleratorViewElement {
   $: {
@@ -116,6 +116,11 @@
         type: Number,
         value: 0,
       },
+
+      sourceIsLocked: {
+        type: Boolean,
+        value: false,
+      },
     };
   }
 
@@ -125,6 +130,7 @@
   hasError: boolean;
   action: number;
   source: AcceleratorSource;
+  sourceIsLocked: boolean;
   protected pendingAcceleratorInfo: StandardAcceleratorInfo;
   private modifiers: string[];
   private acceleratorOnHold: string;
@@ -484,6 +490,15 @@
     this.endCapture();
   }
 
+  private shouldShowLockIcon(): boolean {
+    if (isCustomizationDisabled()) {
+      return false;
+    }
+
+    return (this.acceleratorInfo && this.acceleratorInfo.locked) ||
+        this.sourceIsLocked;
+  }
+
   static get template(): HTMLTemplateElement {
     return getTemplate();
   }
diff --git a/ash/webui/shortcut_customization_ui/resources/js/input_key.html b/ash/webui/shortcut_customization_ui/resources/js/input_key.html
index d4f147e..f84bf1c 100644
--- a/ash/webui/shortcut_customization_ui/resources/js/input_key.html
+++ b/ash/webui/shortcut_customization_ui/resources/js/input_key.html
@@ -7,7 +7,7 @@
     font-weight: 500;
     height: 28px;
     justify-content: center;
-    margin-inline-start: 8px;
+    margin-inline-end: 8px;
     min-width: 28px;
     padding-inline: 8px;
   }
diff --git a/ash/webui/shortcut_customization_ui/resources/js/shortcut_customization_app.html b/ash/webui/shortcut_customization_ui/resources/js/shortcut_customization_app.html
index 9ea69ec..691dcbc 100644
--- a/ash/webui/shortcut_customization_ui/resources/js/shortcut_customization_app.html
+++ b/ash/webui/shortcut_customization_ui/resources/js/shortcut_customization_app.html
@@ -5,10 +5,6 @@
     font-weight: var(--shortcuts-font-weight-regular);
   }
 
-  #navigationPanel::part(top-nav) {
-    height: 72px;
-  }
-
   #navigationPanel::part(toolbar-container) {
     align-items: flex-end;
     display: flex;
diff --git a/ash/webui/shortcut_customization_ui/resources/js/shortcuts_page.html b/ash/webui/shortcut_customization_ui/resources/js/shortcuts_page.html
index 42efb4f7..ac6e767 100644
--- a/ash/webui/shortcut_customization_ui/resources/js/shortcuts_page.html
+++ b/ash/webui/shortcut_customization_ui/resources/js/shortcuts_page.html
@@ -4,7 +4,7 @@
   }
 
   #container accelerator-subsection:not(:first-child)::part(container) {
-    margin-top: 40px;
+    margin-top: 20px;
   }
 </style>
 
diff --git a/ash/wm/tablet_mode/tablet_mode_multitask_menu.cc b/ash/wm/tablet_mode/tablet_mode_multitask_menu.cc
index dd89af6..db9d5543 100644
--- a/ash/wm/tablet_mode/tablet_mode_multitask_menu.cc
+++ b/ash/wm/tablet_mode/tablet_mode_multitask_menu.cc
@@ -214,33 +214,29 @@
       .SetOpacity(view_layer, 0.0f, gfx::Tween::LINEAR);
 }
 
-void TabletModeMultitaskMenu::BeginDrag(float initial_y, bool show) {
-  // Drag up can start from anywhere in the menu; simply save `initial_y` to
-  // update drag relative to it.
-  initial_y_ = initial_y;
-  if (show) {
+void TabletModeMultitaskMenu::BeginDrag(float initial_y, bool down) {
+  if (down) {
     // If we are dragging down, the menu hasn't been created yet, so match the
-    // bottom of the menu with `initial_y`.
-    const float transform_y = initial_y - menu_view_->bounds().bottom();
+    // bottom of the menu with `initial_y` and save it as `initial_y_`.
+    const float translation_y = initial_y - menu_view_->bounds().bottom();
+    initial_y_ = menu_view_->bounds().bottom();
     menu_view_->layer()->SetTransform(
-        gfx::Transform::MakeTranslation(0, transform_y));
+        gfx::Transform::MakeTranslation(0, translation_y));
+  } else {
+    // Drag up can start from anywhere in the menu; simply save `initial_y` to
+    // update drag relative to it.
+    initial_y_ = initial_y;
   }
 }
 
-void TabletModeMultitaskMenu::UpdateDrag(float current_y, bool show) {
-  float transform_y;
-  if (show) {
-    // Continue to match the menu bottom with `current_y`.
-    transform_y = current_y - menu_view_->bounds().bottom();
-    // Stop translating the menu if the drag moves out of bounds.
-    if (transform_y >= 0.f) {
-      return;
-    }
-  } else {
-    transform_y = current_y - initial_y_;
+void TabletModeMultitaskMenu::UpdateDrag(float current_y, bool down) {
+  const float translation_y = current_y - initial_y_;
+  // Stop translating the menu if the drag moves out of bounds.
+  if (down && translation_y >= 0.f) {
+    return;
   }
   menu_view_->layer()->SetTransform(
-      gfx::Transform::MakeTranslation(0, transform_y));
+      gfx::Transform::MakeTranslation(0, translation_y));
 }
 
 void TabletModeMultitaskMenu::EndDrag() {
diff --git a/ash/wm/tablet_mode/tablet_mode_multitask_menu.h b/ash/wm/tablet_mode/tablet_mode_multitask_menu.h
index 64faf72c..bbf2b03 100644
--- a/ash/wm/tablet_mode/tablet_mode_multitask_menu.h
+++ b/ash/wm/tablet_mode/tablet_mode_multitask_menu.h
@@ -50,10 +50,10 @@
   void AnimateFadeOut();
 
   // Actions called by the event handler, where `initial_y` and `current_y` are
-  // in `window_`'s coordinates. If `show` is true, we are dragging down to show
+  // in `window_`'s coordinates. If `down` is true, we are dragging down to show
   // the menu, else we are dragging up to hide the menu.
-  void BeginDrag(float initial_y, bool show);
-  void UpdateDrag(float current_y, bool show);
+  void BeginDrag(float initial_y, bool down);
+  void UpdateDrag(float current_y, bool down);
   void EndDrag();
 
   // Calls the event handler to destroy `this`.
diff --git a/ash/wm/tablet_mode/tablet_mode_multitask_menu_event_handler.cc b/ash/wm/tablet_mode/tablet_mode_multitask_menu_event_handler.cc
index eda884d..3896795c 100644
--- a/ash/wm/tablet_mode/tablet_mode_multitask_menu_event_handler.cc
+++ b/ash/wm/tablet_mode/tablet_mode_multitask_menu_event_handler.cc
@@ -137,10 +137,10 @@
       }
       if (!multitask_menu_ && details.scroll_y_hint() > 0) {
         MaybeCreateMultitaskMenu(active_window);
-        multitask_menu_->BeginDrag(window_location.y(), /*show=*/true);
+        multitask_menu_->BeginDrag(window_location.y(), /*down=*/true);
         event->SetHandled();
       } else if (multitask_menu_ && details.scroll_y_hint() < 0) {
-        multitask_menu_->BeginDrag(window_location.y(), /*show=*/false);
+        multitask_menu_->BeginDrag(window_location.y(), /*down=*/false);
         event->SetHandled();
       }
       break;
@@ -150,13 +150,13 @@
       // menu open. If we are scrolling up, we only handle events inside the
       // menu to avoid consuming them before `OnWidgetActivationChanged()`.
       if (multitask_menu_ && details.scroll_y() > 0) {
-        multitask_menu_->UpdateDrag(window_location.y(), /*show=*/true);
+        multitask_menu_->UpdateDrag(window_location.y(), /*down=*/true);
         event->SetHandled();
       } else if (multitask_menu_ && details.scroll_y() < 0 &&
                  gfx::RectF(
                      multitask_menu_->widget()->GetWindowBoundsInScreen())
                      .Contains(screen_location)) {
-        multitask_menu_->UpdateDrag(window_location.y(), /*show=*/false);
+        multitask_menu_->UpdateDrag(window_location.y(), /*down=*/false);
         event->SetHandled();
       }
       break;
diff --git a/ash/wm/window_cycle/window_cycle_item_view.cc b/ash/wm/window_cycle/window_cycle_item_view.cc
index 29774134..31507ea 100644
--- a/ash/wm/window_cycle/window_cycle_item_view.cc
+++ b/ash/wm/window_cycle/window_cycle_item_view.cc
@@ -13,7 +13,6 @@
 #include "ui/aura/window.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
 #include "ui/gfx/geometry/rect_f.h"
-#include "ui/views/accessibility/accessibility_paint_checks.h"
 #include "ui/views/view.h"
 
 namespace ash {
@@ -32,11 +31,6 @@
     : WindowMiniView(window) {
   SetFocusBehavior(FocusBehavior::ALWAYS);
   SetNotifyEnterExitOnChild(true);
-
-  // TODO(crbug.com/1218186): Remove this, this is in place temporarily to be
-  // able to submit accessibility checks, but this focusable View needs to
-  // add a name so that the screen reader knows what to announce.
-  SetProperty(views::kSkipAccessibilityPaintChecks, true);
 }
 
 void WindowCycleItemView::ShowPreview() {
diff --git a/base/allocator/partition_allocator/partition_alloc_unittest.cc b/base/allocator/partition_allocator/partition_alloc_unittest.cc
index a1c6d019..5e263eb 100644
--- a/base/allocator/partition_allocator/partition_alloc_unittest.cc
+++ b/base/allocator/partition_allocator/partition_alloc_unittest.cc
@@ -2027,14 +2027,14 @@
   for (i = 0; i < num_partition_pages_needed; ++i)
     first_super_page_pages[i] = GetFullSlotSpan(kTestAllocSize);
 
-  uintptr_t slot_spart_start =
+  uintptr_t slot_span_start =
       SlotSpan::ToSlotSpanStart(first_super_page_pages[0]);
   EXPECT_EQ(PartitionPageSize() +
                 partition_alloc::internal::ReservedTagBitmapSize() +
                 partition_alloc::internal::ReservedFreeSlotBitmapSize(),
-            slot_spart_start & kSuperPageOffsetMask);
+            slot_span_start & kSuperPageOffsetMask);
   uintptr_t super_page =
-      slot_spart_start - PartitionPageSize() -
+      slot_span_start - PartitionPageSize() -
       partition_alloc::internal::ReservedTagBitmapSize() -
       partition_alloc::internal::ReservedFreeSlotBitmapSize();
   // Map a single system page either side of the mapping for our allocations,
diff --git a/base/debug/asan_service.cc b/base/debug/asan_service.cc
index 1ec961a4..da7366c7 100644
--- a/base/debug/asan_service.cc
+++ b/base/debug/asan_service.cc
@@ -7,7 +7,6 @@
 #if defined(ADDRESS_SANITIZER)
 #include <sanitizer/asan_interface.h>
 
-#include "base/debug/task_trace.h"
 #include "base/no_destructor.h"
 #include "base/process/process.h"
 #include "base/process/process_handle.h"
@@ -25,35 +24,10 @@
 namespace base {
 namespace debug {
 
-namespace {
-NO_SANITIZE("address")
-void TaskTraceErrorCallback(const char* error, bool*) {
-  // Use the sanitizer api to symbolize the task trace, which otherwise might
-  // not symbolize properly. This also lets us format the task trace in the
-  // same way as the address sanitizer backtraces, which also means that we can
-  // get the stack trace symbolized with asan_symbolize.py in the cases where
-  // symbolization at runtime fails.
-  std::array<const void*, 4> addresses;
-  size_t address_count = TaskTrace().GetAddresses(addresses);
-
-  AsanService::GetInstance()->Log("Task trace:");
-  size_t frame_index = 0;
-  for (size_t i = 0; i < std::min(address_count, addresses.size()); ++i) {
-    char buffer[4096] = {};
-    void* address = const_cast<void*>(addresses[i]);
-    __sanitizer_symbolize_pc(address, "%p %F %L", buffer, sizeof(buffer));
-    for (char* ptr = buffer; *ptr != 0; ptr += strlen(ptr)) {
-      AsanService::GetInstance()->Log("    #%i %s", frame_index++, ptr);
-    }
-  }
-  AsanService::GetInstance()->Log("");
-}
-}  // namespace
-
 // static
 NO_SANITIZE("address")
 AsanService* AsanService::GetInstance() {
-  static NoDestructor<AsanService> instance;
+  static base::NoDestructor<AsanService> instance;
   return instance.get();
 }
 
@@ -61,7 +35,6 @@
   AutoLock lock(lock_);
   if (!is_initialized_) {
     __asan_set_error_report_callback(ErrorReportCallback);
-    error_callbacks_.push_back(TaskTraceErrorCallback);
     is_initialized_ = true;
   }
 }
diff --git a/base/debug/asan_service_unittest.cc b/base/debug/asan_service_unittest.cc
index e462d5a..e2328b6 100644
--- a/base/debug/asan_service_unittest.cc
+++ b/base/debug/asan_service_unittest.cc
@@ -11,10 +11,7 @@
 #include <sstream>
 
 #include "base/debug/asan_invalid_access.h"
-#include "base/run_loop.h"
 #include "base/strings/string_piece.h"
-#include "base/test/bind.h"
-#include "base/test/task_environment.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -96,34 +93,6 @@
   EXPECT_EXIT(AsanHeapUseAfterFree(), ExitedCleanly, "EXITING");
 }
 
-class AsanTaskTraceTest {
- public:
-  AsanTaskTraceTest() {}
-
-  void Run() {
-    task_runner_.PostTask(
-        FROM_HERE, BindOnce(&AsanTaskTraceTest::PostingTask, Unretained(this)));
-    task_environment_.RunUntilIdle();
-  }
-
- private:
-  void PostingTask() {
-    task_runner_.PostTask(FROM_HERE, BindOnce(&AsanHeapUseAfterFree));
-  }
-
-  test::TaskEnvironment task_environment_;
-  SingleThreadTaskRunner& task_runner_ =
-      *task_environment_.GetMainThreadTaskRunner();
-};
-
-TEST_F(AsanServiceTest, TaskTraceCallback) {
-  AsanTaskTraceTest test;
-  // Check that we get the correct task trace entries in our crash output.
-  EXPECT_DEATH(test.Run(),
-               "#0 0x.* in base::debug::AsanTaskTraceTest::PostingTask");
-  EXPECT_DEATH(test.Run(), "#1 0x.* in Run");
-}
-
 }  // namespace debug
 }  // namespace base
 
diff --git a/base/debug/task_trace.cc b/base/debug/task_trace.cc
index 8a9f8a8..901008b 100644
--- a/base/debug/task_trace.cc
+++ b/base/debug/task_trace.cc
@@ -93,16 +93,12 @@
   return stream.str();
 }
 
-size_t TaskTrace::GetAddresses(span<const void*> addresses) const {
+base::span<const void* const> TaskTrace::AddressesForTesting() const {
+  if (empty())
+    return {};
   size_t count = 0;
-  if (empty()) {
-    return count;
-  }
-  const void* const* current_addresses = stack_trace_->Addresses(&count);
-  for (size_t i = 0; i < count && i < addresses.size(); ++i) {
-    addresses[i] = current_addresses[i];
-  }
-  return count;
+  const void* const* addresses = stack_trace_->Addresses(&count);
+  return {addresses, count};
 }
 
 std::ostream& operator<<(std::ostream& os, const TaskTrace& task_trace) {
diff --git a/base/debug/task_trace.h b/base/debug/task_trace.h
index 7cad5315..ebe3862 100644
--- a/base/debug/task_trace.h
+++ b/base/debug/task_trace.h
@@ -49,10 +49,8 @@
   // Resolves trace to symbols and returns as string.
   std::string ToString() const;
 
-  // Reads the list of addresses currently in the task trace into `addresses`,
-  // and returns the maximum length of addresses that could have been read,
-  // which may differ from `addresses.size()`.
-  size_t GetAddresses(span<const void*> addresses) const;
+  // Returns the list of addresses in the task trace for testing.
+  base::span<const void* const> AddressesForTesting() const;
 
  private:
   absl::optional<StackTrace> stack_trace_;
diff --git a/base/debug/task_trace_unittest.cc b/base/debug/task_trace_unittest.cc
index f278a0cf..a394d4c 100644
--- a/base/debug/task_trace_unittest.cc
+++ b/base/debug/task_trace_unittest.cc
@@ -19,8 +19,7 @@
 TEST(TaskTraceTest, NoTask) {
   TaskTrace task_trace;
   EXPECT_TRUE(task_trace.empty());
-  std::array<const void*, 4> addresses = {0};
-  EXPECT_EQ(task_trace.GetAddresses(addresses), 0ul);
+  EXPECT_EQ(task_trace.AddressesForTesting().size(), 0ul);
 }
 
 class ThreeTasksTest {
@@ -36,8 +35,8 @@
   void TaskA() {
     TaskTrace task_trace;
     EXPECT_FALSE(task_trace.empty());
-    std::array<const void*, 4> addresses = {0};
-    EXPECT_EQ(task_trace.GetAddresses(addresses), 1ul);
+    base::span<const void* const> addresses = task_trace.AddressesForTesting();
+    EXPECT_EQ(addresses.size(), 1ul);
     task_a_address = addresses[0];
     task_runner->PostTask(FROM_HERE, base::BindOnce(&ThreeTasksTest::TaskB,
                                                     base::Unretained(this)));
@@ -46,8 +45,8 @@
   void TaskB() {
     TaskTrace task_trace;
     EXPECT_FALSE(task_trace.empty());
-    std::array<const void*, 4> addresses = {0};
-    EXPECT_EQ(task_trace.GetAddresses(addresses), 2ul);
+    base::span<const void* const> addresses = task_trace.AddressesForTesting();
+    EXPECT_EQ(addresses.size(), 2ul);
     task_b_address = addresses[0];
     EXPECT_EQ(addresses[1], task_a_address);
     task_runner->PostTask(FROM_HERE, base::BindOnce(&ThreeTasksTest::TaskC,
@@ -57,8 +56,8 @@
   void TaskC() {
     TaskTrace task_trace;
     EXPECT_FALSE(task_trace.empty());
-    std::array<const void*, 4> addresses;
-    EXPECT_EQ(task_trace.GetAddresses(addresses), 3ul);
+    base::span<const void* const> addresses = task_trace.AddressesForTesting();
+    EXPECT_EQ(addresses.size(), 3ul);
     EXPECT_EQ(addresses[1], task_b_address);
     EXPECT_EQ(addresses[2], task_a_address);
   }
diff --git a/base/message_loop/message_pump_glib_unittest.cc b/base/message_loop/message_pump_glib_unittest.cc
index 142acce7..b41443c 100644
--- a/base/message_loop/message_pump_glib_unittest.cc
+++ b/base/message_loop/message_pump_glib_unittest.cc
@@ -611,7 +611,14 @@
 
   ~DeleteWatcher() override { DCHECK(!controller_); }
 
+  bool HasController() const { return !!controller_; }
+
   void OnFileCanWriteWithoutBlocking(int /* fd */) override {
+    ClearController();
+  }
+
+ protected:
+  void ClearController() {
     DCHECK(owned_controller_);
     controller_ = nullptr;
     owned_controller_.reset();
@@ -657,13 +664,15 @@
   void OnFileCanWriteWithoutBlocking(int /* fd */) override {}
 };
 
-class QuitWatcher : public BaseWatcher {
+class QuitWatcher : public DeleteWatcher {
  public:
-  QuitWatcher(MessagePumpGlib::FdWatchController* controller,
+  QuitWatcher(std::unique_ptr<MessagePumpGlib::FdWatchController> controller,
               base::OnceClosure quit_closure)
-      : BaseWatcher(controller), quit_closure_(std::move(quit_closure)) {}
+      : DeleteWatcher(std::move(controller)),
+        quit_closure_(std::move(quit_closure)) {}
 
-  void OnFileCanReadWithoutBlocking(int /* fd */) override {
+  void OnFileCanReadWithoutBlocking(int fd) override {
+    ClearController();
     if (quit_closure_)
       std::move(quit_closure_).Run();
   }
@@ -682,7 +691,7 @@
 }  // namespace
 
 // Tests that MessagePumpGlib::FdWatcher::OnFileCanReadWithoutBlocking is not
-// called for a READ_WRITE event, when the controller is destroyed in
+// called for a READ_WRITE event, and that the controller is destroyed in
 // OnFileCanWriteWithoutBlocking callback.
 TEST_F(MessagePumpGLibFdWatchTest, DeleteWatcher) {
   auto pump = std::make_unique<MessagePumpGlib>();
@@ -696,6 +705,7 @@
                             &watcher);
 
   SimulateEvent(pump.get(), controller);
+  EXPECT_FALSE(watcher.HasController());
 }
 
 // Tests that MessagePumpGlib::FdWatcher::OnFileCanReadWithoutBlocking is not
@@ -717,8 +727,8 @@
   test::SingleThreadTaskEnvironment task_environment(
       test::SingleThreadTaskEnvironment::MainThreadType::UI);
   std::unique_ptr<MessagePumpGlib> pump(new MessagePumpGlib);
-  MessagePumpGlib::FdWatchController controller(FROM_HERE);
   NestedPumpWatcher watcher;
+  MessagePumpGlib::FdWatchController controller(FROM_HERE);
   pump->WatchFileDescriptor(pipefds_[1], false, MessagePumpGlib::WATCH_READ,
                             &controller, &watcher);
 
@@ -731,16 +741,19 @@
   MessagePumpGlib* pump = new MessagePumpGlib();
   SingleThreadTaskExecutor executor(WrapUnique(pump));
   RunLoop run_loop;
-  MessagePumpGlib::FdWatchController controller(FROM_HERE);
-  QuitWatcher delegate(&controller, run_loop.QuitClosure());
-  WaitableEvent event;
-  auto watcher = std::make_unique<WaitableEventWatcher>();
+
+  auto owned_controller =
+      std::make_unique<MessagePumpGlib::FdWatchController>(FROM_HERE);
+  MessagePumpGlib::FdWatchController* controller = owned_controller.get();
+  QuitWatcher delegate(std::move(owned_controller), run_loop.QuitClosure());
 
   pump->WatchFileDescriptor(pipefds_[0], false, MessagePumpGlib::WATCH_READ,
-                            &controller, &delegate);
+                            controller, &delegate);
 
   // Make the IO thread wait for |event| before writing to pipefds[1].
   const char buf = 0;
+  WaitableEvent event;
+  auto watcher = std::make_unique<WaitableEventWatcher>();
   WaitableEventWatcher::EventCallback write_fd_task =
       BindOnce(&WriteFDWrapper, pipefds_[1], &buf, 1);
   io_runner()->PostTask(
diff --git a/base/power_monitor/power_monitor_device_source.h b/base/power_monitor/power_monitor_device_source.h
index c56f203c..38ee5af 100644
--- a/base/power_monitor/power_monitor_device_source.h
+++ b/base/power_monitor/power_monitor_device_source.h
@@ -125,6 +125,11 @@
   PowerThermalObserver::DeviceThermalState GetCurrentThermalState() override;
   int GetInitialSpeedLimit() override;
 
+  // Retrieves the current battery state to update `is_on_battery_`.
+  void GetBatteryState();
+  void OnBatteryStateReceived(
+      const absl::optional<BatteryLevelProvider::BatteryState>& battery_state);
+
   // Reference to the system IOPMrootDomain port.
   io_connect_t power_manager_port_ = IO_OBJECT_NULL;
 
diff --git a/base/power_monitor/power_monitor_device_source_mac.mm b/base/power_monitor/power_monitor_device_source_mac.mm
index 7d69309..87bd223 100644
--- a/base/power_monitor/power_monitor_device_source_mac.mm
+++ b/base/power_monitor/power_monitor_device_source_mac.mm
@@ -28,6 +28,22 @@
   return thermal_state_observer_->GetCurrentSpeedLimit();
 }
 
+void PowerMonitorDeviceSource::GetBatteryState() {
+  DCHECK(battery_level_provider_);
+  // base::Unretained is safe because the callback is immediately invoked
+  // inside `BatteryLevelProvider::GetBatteryState()`.
+  battery_level_provider_->GetBatteryState(
+      base::BindOnce(&PowerMonitorDeviceSource::OnBatteryStateReceived,
+                     base::Unretained(this)));
+}
+
+void PowerMonitorDeviceSource::OnBatteryStateReceived(
+    const absl::optional<BatteryLevelProvider::BatteryState>& battery_state) {
+  is_on_battery_ =
+      battery_state.has_value() && !battery_state->is_external_power_connected;
+  PowerMonitorSource::ProcessPowerEvent(PowerMonitorSource::POWER_STATE_EVENT);
+}
+
 void PowerMonitorDeviceSource::PlatformInit() {
   power_manager_port_ = IORegisterForSystemPower(
       this,
@@ -42,24 +58,13 @@
       kCFRunLoopCommonModes);
 
   battery_level_provider_ = BatteryLevelProvider::Create();
-
-  // Create and add the power-source-change event source to the runloop.
+  // Get the initial state for `is_on_battery_` and register for all future
+  // power-source-change events.
+  GetBatteryState();
+  // base::Unretained is safe because `this` owns `power_source_event_source_`,
+  // which exclusively owns the callback.
   power_source_event_source_.Start(base::BindRepeating(
-      [](PowerMonitorDeviceSource* self,
-         BatteryLevelProvider* battery_level_provider) {
-        battery_level_provider->GetBatteryState(base::BindOnce(
-            [](PowerMonitorDeviceSource* self,
-               const absl::optional<BatteryLevelProvider::BatteryState>&
-                   battery_state) {
-              self->is_on_battery_ =
-                  battery_state.has_value() &&
-                  !battery_state->is_external_power_connected;
-              PowerMonitorSource::ProcessPowerEvent(
-                  PowerMonitorSource::POWER_STATE_EVENT);
-            },
-            Unretained(self)));
-      },
-      Unretained(this), battery_level_provider_.get()));
+      &PowerMonitorDeviceSource::GetBatteryState, base::Unretained(this)));
 
   thermal_state_observer_ = std::make_unique<ThermalStateObserverMac>(
       BindRepeating(&PowerMonitorSource::ProcessThermalEvent),
diff --git a/base/unguessable_token.cc b/base/unguessable_token.cc
index ebb51d9..af8afb2 100644
--- a/base/unguessable_token.cc
+++ b/base/unguessable_token.cc
@@ -43,6 +43,19 @@
   return UnguessableToken(Token{high, low});
 }
 
+// static
+absl::optional<UnguessableToken> UnguessableToken::Deserialize2(uint64_t high,
+                                                                uint64_t low) {
+  // Receiving a zeroed out UnguessableToken from another process means that it
+  // was never initialized via Create(). Since this method might also be used to
+  // create an UnguessableToken from data on disk, we will handle this case more
+  // gracefully since data could have been corrupted.
+  if (high == 0 && low == 0) {
+    return absl::nullopt;
+  }
+  return UnguessableToken(Token{high, low});
+}
+
 bool UnguessableToken::operator==(const UnguessableToken& other) const {
 #if BUILDFLAG(IS_NACL)
   // BoringSSL is unavailable for NaCl builds so it remains timing dependent.
diff --git a/base/unguessable_token.h b/base/unguessable_token.h
index b3104bb..e8134091 100644
--- a/base/unguessable_token.h
+++ b/base/unguessable_token.h
@@ -56,6 +56,7 @@
   // default constructor.
   static const UnguessableToken& Null();
 
+  // NOTE: This method is deprecated and will soon be replaced by the one below.
   // Return a UnguessableToken built from the high/low bytes provided.
   // It should only be used in deserialization scenarios.
   //
@@ -63,6 +64,16 @@
   // initialized via Create(). This is a security issue, and should be handled.
   static UnguessableToken Deserialize(uint64_t high, uint64_t low);
 
+  // Return a UnguessableToken built from the high/low bytes provided.
+  // It should only be used in deserialization scenarios.
+  //
+  // NOTE: Once `Deserialize` above is removed, this will be renamed to that.
+  //
+  // NOTE: If the deserialized token is empty, it means that it was never
+  // initialized via Create(). This is a security issue, and should be handled.
+  static absl::optional<UnguessableToken> Deserialize2(uint64_t high,
+                                                       uint64_t low);
+
   // Creates an empty UnguessableToken.
   // Assign to it with Create() before using it.
   constexpr UnguessableToken() = default;
diff --git a/base/unguessable_token_unittest.cc b/base/unguessable_token_unittest.cc
index eed139f..ff88f15 100644
--- a/base/unguessable_token_unittest.cc
+++ b/base/unguessable_token_unittest.cc
@@ -20,29 +20,34 @@
 }
 
 TEST(UnguessableTokenTest, VerifyEveryBit) {
-  UnguessableToken token = UnguessableToken::Deserialize(1, 2);
+  absl::optional<UnguessableToken> token = UnguessableToken::Deserialize2(1, 2);
+  ASSERT_TRUE(token.has_value());
   uint64_t high = 1;
   uint64_t low = 2;
 
   for (uint64_t bit = 1; bit != 0; bit <<= 1) {
     uint64_t new_high = high ^ bit;
-    UnguessableToken new_token = UnguessableToken::Deserialize(new_high, low);
-    EXPECT_FALSE(token == new_token);
+    absl::optional<UnguessableToken> new_token =
+        UnguessableToken::Deserialize2(new_high, low);
+    ASSERT_TRUE(new_token.has_value());
+    EXPECT_FALSE(*token == *new_token);
   }
 
   for (uint64_t bit = 1; bit != 0; bit <<= 1) {
     uint64_t new_low = low ^ bit;
-    UnguessableToken new_token = UnguessableToken::Deserialize(high, new_low);
-    EXPECT_FALSE(token == new_token);
+    absl::optional<UnguessableToken> new_token =
+        UnguessableToken::Deserialize2(high, new_low);
+    ASSERT_TRUE(new_token.has_value());
+    EXPECT_FALSE(*token == *new_token);
   }
 }
 
 TEST(UnguessableTokenTest, VerifyEqualityOperators) {
   // Deserialize is used for testing purposes.
   // Use UnguessableToken::Create() in production code instead.
-  UnguessableToken token = UnguessableToken::Deserialize(1, 2);
-  UnguessableToken same_token = UnguessableToken::Deserialize(1, 2);
-  UnguessableToken diff_token = UnguessableToken::Deserialize(1, 3);
+  UnguessableToken token = UnguessableToken::Deserialize2(1, 2).value();
+  UnguessableToken same_token = UnguessableToken::Deserialize2(1, 2).value();
+  UnguessableToken diff_token = UnguessableToken::Deserialize2(1, 3).value();
   UnguessableToken empty_token;
 
   EXPECT_TRUE(token == token);
@@ -90,14 +95,17 @@
   EXPECT_TRUE(high);
   EXPECT_TRUE(low);
 
-  UnguessableToken Deserialized = UnguessableToken::Deserialize(high, low);
-  EXPECT_EQ(token, Deserialized);
+  absl::optional<UnguessableToken> Deserialized =
+      UnguessableToken::Deserialize2(high, low);
+  ASSERT_TRUE(Deserialized.has_value());
+  EXPECT_EQ(token, *Deserialized);
 }
 
 // Common case (~88% of the time) - no leading zeroes in high_ nor low_.
 TEST(UnguessableTokenTest, VerifyToString1) {
   UnguessableToken token =
-      UnguessableToken::Deserialize(0x1234567890ABCDEF, 0xFEDCBA0987654321);
+      UnguessableToken::Deserialize2(0x1234567890ABCDEF, 0xFEDCBA0987654321)
+          .value();
   std::string expected = "1234567890ABCDEFFEDCBA0987654321";
 
   EXPECT_EQ(expected, token.ToString());
@@ -110,7 +118,7 @@
 
 // Less common case - leading zeroes in high_ or low_ (testing with both).
 TEST(UnguessableTokenTest, VerifyToString2) {
-  UnguessableToken token = UnguessableToken::Deserialize(0x123, 0xABC);
+  UnguessableToken token = UnguessableToken::Deserialize2(0x123, 0xABC).value();
   std::string expected = "00000000000001230000000000000ABC";
 
   EXPECT_EQ(expected, token.ToString());
@@ -123,34 +131,42 @@
 
 TEST(UnguessableTokenTest, VerifyToStringUniqueness) {
   const UnguessableToken token1 =
-      UnguessableToken::Deserialize(0x0000000012345678, 0x0000000123456789);
+      UnguessableToken::Deserialize2(0x0000000012345678, 0x0000000123456789)
+          .value();
   const UnguessableToken token2 =
-      UnguessableToken::Deserialize(0x0000000123456781, 0x0000000023456789);
+      UnguessableToken::Deserialize2(0x0000000123456781, 0x0000000023456789)
+          .value();
   EXPECT_NE(token1.ToString(), token2.ToString());
 }
 
+TEST(UnguessableTokenTest, VerifyDeserializeZeroes) {
+  absl::optional<UnguessableToken> token = UnguessableToken::Deserialize2(0, 0);
+
+  EXPECT_FALSE(token.has_value());
+}
+
 TEST(UnguessableTokenTest, VerifySmallerThanOperator) {
   // Deserialize is used for testing purposes.
   // Use UnguessableToken::Create() in production code instead.
   {
     SCOPED_TRACE("a.low < b.low and a.high == b.high.");
-    TestSmallerThanOperator(UnguessableToken::Deserialize(0, 1),
-                            UnguessableToken::Deserialize(0, 5));
+    TestSmallerThanOperator(UnguessableToken::Deserialize2(0, 1).value(),
+                            UnguessableToken::Deserialize2(0, 5).value());
   }
   {
     SCOPED_TRACE("a.low == b.low and a.high < b.high.");
-    TestSmallerThanOperator(UnguessableToken::Deserialize(1, 0),
-                            UnguessableToken::Deserialize(5, 0));
+    TestSmallerThanOperator(UnguessableToken::Deserialize2(1, 0).value(),
+                            UnguessableToken::Deserialize2(5, 0).value());
   }
   {
     SCOPED_TRACE("a.low < b.low and a.high < b.high.");
-    TestSmallerThanOperator(UnguessableToken::Deserialize(1, 1),
-                            UnguessableToken::Deserialize(5, 5));
+    TestSmallerThanOperator(UnguessableToken::Deserialize2(1, 1).value(),
+                            UnguessableToken::Deserialize2(5, 5).value());
   }
   {
     SCOPED_TRACE("a.low > b.low and a.high < b.high.");
-    TestSmallerThanOperator(UnguessableToken::Deserialize(1, 10),
-                            UnguessableToken::Deserialize(10, 1));
+    TestSmallerThanOperator(UnguessableToken::Deserialize2(1, 10).value(),
+                            UnguessableToken::Deserialize2(10, 1).value());
   }
 }
 
diff --git a/base/win/windows_types.h b/base/win/windows_types.h
index 0766f38..41ae16af 100644
--- a/base/win/windows_types.h
+++ b/base/win/windows_types.h
@@ -212,7 +212,10 @@
 #define REG_BINARY ( 3ul )
 #define REG_NONE ( 0ul )
 
+#ifndef STATUS_PENDING
+// Allow people to include ntstatus.h
 #define STATUS_PENDING ((DWORD   )0x00000103L)
+#endif  // STATUS_PENDING
 #define STILL_ACTIVE STATUS_PENDING
 #define SUCCEEDED(hr) (((HRESULT)(hr)) >= 0)
 #define FAILED(hr) (((HRESULT)(hr)) < 0)
diff --git a/chrome/VERSION b/chrome/VERSION
index 4d8237c..274b09c 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=111
 MINOR=0
-BUILD=5521
+BUILD=5522
 PATCH=0
diff --git a/chrome/android/features/start_surface/java/src/org/chromium/chrome/features/start_surface/StartSurfaceMediator.java b/chrome/android/features/start_surface/java/src/org/chromium/chrome/features/start_surface/StartSurfaceMediator.java
index 8c1d0ac0..1f415dd 100644
--- a/chrome/android/features/start_surface/java/src/org/chromium/chrome/features/start_surface/StartSurfaceMediator.java
+++ b/chrome/android/features/start_surface/java/src/org/chromium/chrome/features/start_surface/StartSurfaceMediator.java
@@ -392,7 +392,6 @@
             mUrlFocusChangeListener = new UrlFocusChangeListener() {
                 @Override
                 public void onUrlFocusChange(boolean hasFocus) {
-                    assert !mPropertyModel.get(IS_SECONDARY_SURFACE_VISIBLE);
                     if (hasFakeSearchBox()) {
                         setFakeBoxVisibility(!hasFocus);
                         // TODO(crbug.com/1365694): We should call setLogoVisibility(!hasFocus)
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index fb3b2f6c..07dd7638 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -5730,6 +5730,18 @@
       <message name="IDS_EXTENSIONS_MENU_EXTENSIONS_TAB_DISCOVER_MORE_TITLE" desc="Text of the button in the installed extensions tab used to open the webstore page">
         Discover more extensions
       </message>
+      <message name="IDS_EXTENSIONS_MENU_SITE_SETTINGS_TOGGLE_ON_TOOLTIP" desc="Tooltip text for the site setting toggle button when is on.">
+        Extensions are allowed on this site
+      </message>
+      <message name="IDS_EXTENSIONS_MENU_SITE_SETTINGS_TOGGLE_OFF_TOOLTIP" desc="Tooltip text for the site setting toggle button when is off.'">
+        Extensions are blocked on this site
+      </message>
+      <message name="IDS_EXTENSIONS_MENU_SITE_SETTINGS_TOGGLE_ON_ACCESSIBLE_NAME" desc="Accessibility string for the site setting toggle button when is on.">
+        Select to block all extensions on this site
+      </message>
+      <message name="IDS_EXTENSIONS_MENU_SITE_SETTINGS_TOGGLE_OFF_ACCESSIBLE_NAME" desc="Accessibility string for the site setting toggle button when is off.'">
+        Select to allow extensions on this site
+      </message>
       <message name="IDS_EXTENSIONS_MENU_REQUESTS_ACCESS_SECTION_TITLE" desc="Title of the requests access section that lists the extensions requesting site access">
         Requests access
       </message>
diff --git a/chrome/app/generated_resources_grd/IDS_EXTENSIONS_MENU_SITE_SETTINGS_TOGGLE_OFF_ACCESSIBLE_NAME.png.sha1 b/chrome/app/generated_resources_grd/IDS_EXTENSIONS_MENU_SITE_SETTINGS_TOGGLE_OFF_ACCESSIBLE_NAME.png.sha1
new file mode 100644
index 0000000..ad6e2e6b
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_EXTENSIONS_MENU_SITE_SETTINGS_TOGGLE_OFF_ACCESSIBLE_NAME.png.sha1
@@ -0,0 +1 @@
+16721b8a4a6321ab60eef2bcaea3b75425e8b0ec
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_EXTENSIONS_MENU_SITE_SETTINGS_TOGGLE_OFF_TOOLTIP.png.sha1 b/chrome/app/generated_resources_grd/IDS_EXTENSIONS_MENU_SITE_SETTINGS_TOGGLE_OFF_TOOLTIP.png.sha1
new file mode 100644
index 0000000..4d77d5c7
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_EXTENSIONS_MENU_SITE_SETTINGS_TOGGLE_OFF_TOOLTIP.png.sha1
@@ -0,0 +1 @@
+234671c622e227e54408f485a5a7b4280907cb6c
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_EXTENSIONS_MENU_SITE_SETTINGS_TOGGLE_ON_ACCESSIBLE_NAME.png.sha1 b/chrome/app/generated_resources_grd/IDS_EXTENSIONS_MENU_SITE_SETTINGS_TOGGLE_ON_ACCESSIBLE_NAME.png.sha1
new file mode 100644
index 0000000..ad6e2e6b
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_EXTENSIONS_MENU_SITE_SETTINGS_TOGGLE_ON_ACCESSIBLE_NAME.png.sha1
@@ -0,0 +1 @@
+16721b8a4a6321ab60eef2bcaea3b75425e8b0ec
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_EXTENSIONS_MENU_SITE_SETTINGS_TOGGLE_ON_TOOLTIP.png.sha1 b/chrome/app/generated_resources_grd/IDS_EXTENSIONS_MENU_SITE_SETTINGS_TOGGLE_ON_TOOLTIP.png.sha1
new file mode 100644
index 0000000..6007e29
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_EXTENSIONS_MENU_SITE_SETTINGS_TOGGLE_ON_TOOLTIP.png.sha1
@@ -0,0 +1 @@
+884bcdfe1667bf96b9825dfffe80d8878c30cb2b
\ No newline at end of file
diff --git a/chrome/app/os_settings_search_tag_strings.grdp b/chrome/app/os_settings_search_tag_strings.grdp
index 0c3d2b61..f82df9b 100644
--- a/chrome/app/os_settings_search_tag_strings.grdp
+++ b/chrome/app/os_settings_search_tag_strings.grdp
@@ -188,6 +188,24 @@
   <message name="IDS_OS_SETTINGS_TAG_PROXY_ALT4" desc="Text for search result item which, when clicked, navigates the user to network proxy settings. Alternate phrase for: 'Proxy settings', 'Automatic proxy configuration', 'Direct Internet connection', 'Web proxy autodiscovery'">
     Manual proxy configuration
   </message>
+  <message name="IDS_OS_SETTINGS_TAG_HOTSPOT" desc="Text for search result item which, when clicked, navigates the user to hotspot settings.">
+    Hotspot
+  </message>
+  <message name="IDS_OS_SETTINGS_TAG_HOTSPOT_TURN_ON" desc="Text for search result item which, when clicked, navigates the user to Hotspot settings, with a toggle to turn on Bluetooth.">
+    Turn on hotspot
+  </message>
+  <message name="IDS_OS_SETTINGS_TAG_HOTSPOT_TURN_OFF" desc="Text for search result item which, when clicked, navigates the user to Hotspot settings, with a toggle to turn off Bluetooth.">
+    Turn off hotspot
+  </message>
+  <message name="IDS_OS_SETTINGS_TAG_HOTSPOT_AUTO_DISABLED" desc="Text for search result item which, when clicked, navigates the user to settings to toggle whether the hotspot turns off automatically when no devices is connected to it after a fixed of time. Alternate phrase for: 'Automatically turn off hotspot', 'Automatically disable hotspot'">
+    Turn off hotspot automatically
+  </message>
+  <message name="IDS_OS_SETTINGS_TAG_HOTSPOT_AUTO_DISABLED_ALT1" desc="Text for search result item which, when clicked, navigates the user to settings to toggle whether the hotspot turns off automatically when no devices is connected to it after a fixed of time. Alternate phrase for: 'Automatically turn off hotspot', 'Automatically disable hotspot'">
+    Automatically turn off hotspot
+  </message>
+  <message name="IDS_OS_SETTINGS_TAG_HOTSPOT_AUTO_DISABLED_ALT2" desc="Text for search result item which, when clicked, navigates the user to settings to toggle whether the hotspot turns off automatically when no devices is connected to it after a fixed of time. Alternate phrase for: 'Automatically turn off hotspot', 'Automatically disable hotspot'">
+    Automatically disable hotspot
+  </message>
 
   <!-- Bluetooth section. -->
   <message name="IDS_OS_SETTINGS_TAG_BLUETOOTH" desc="Text for search result item which, when clicked, navigates the user to Bluetooth settings.">
diff --git a/chrome/app/os_settings_search_tag_strings_grdp/IDS_OS_SETTINGS_TAG_HOTSPOT.png.sha1 b/chrome/app/os_settings_search_tag_strings_grdp/IDS_OS_SETTINGS_TAG_HOTSPOT.png.sha1
new file mode 100644
index 0000000..e2a8a4b
--- /dev/null
+++ b/chrome/app/os_settings_search_tag_strings_grdp/IDS_OS_SETTINGS_TAG_HOTSPOT.png.sha1
@@ -0,0 +1 @@
+f37e84508204eae6d9767260363c9dead74377d8
\ No newline at end of file
diff --git a/chrome/app/os_settings_search_tag_strings_grdp/IDS_OS_SETTINGS_TAG_HOTSPOT_AUTO_DISABLED.png.sha1 b/chrome/app/os_settings_search_tag_strings_grdp/IDS_OS_SETTINGS_TAG_HOTSPOT_AUTO_DISABLED.png.sha1
new file mode 100644
index 0000000..f081dd04
--- /dev/null
+++ b/chrome/app/os_settings_search_tag_strings_grdp/IDS_OS_SETTINGS_TAG_HOTSPOT_AUTO_DISABLED.png.sha1
@@ -0,0 +1 @@
+24aeafb2b8811afac80846b87afd636d0f2ac986
\ No newline at end of file
diff --git a/chrome/app/os_settings_search_tag_strings_grdp/IDS_OS_SETTINGS_TAG_HOTSPOT_AUTO_DISABLED_ALT1.png.sha1 b/chrome/app/os_settings_search_tag_strings_grdp/IDS_OS_SETTINGS_TAG_HOTSPOT_AUTO_DISABLED_ALT1.png.sha1
new file mode 100644
index 0000000..f081dd04
--- /dev/null
+++ b/chrome/app/os_settings_search_tag_strings_grdp/IDS_OS_SETTINGS_TAG_HOTSPOT_AUTO_DISABLED_ALT1.png.sha1
@@ -0,0 +1 @@
+24aeafb2b8811afac80846b87afd636d0f2ac986
\ No newline at end of file
diff --git a/chrome/app/os_settings_search_tag_strings_grdp/IDS_OS_SETTINGS_TAG_HOTSPOT_AUTO_DISABLED_ALT2.png.sha1 b/chrome/app/os_settings_search_tag_strings_grdp/IDS_OS_SETTINGS_TAG_HOTSPOT_AUTO_DISABLED_ALT2.png.sha1
new file mode 100644
index 0000000..f081dd04
--- /dev/null
+++ b/chrome/app/os_settings_search_tag_strings_grdp/IDS_OS_SETTINGS_TAG_HOTSPOT_AUTO_DISABLED_ALT2.png.sha1
@@ -0,0 +1 @@
+24aeafb2b8811afac80846b87afd636d0f2ac986
\ No newline at end of file
diff --git a/chrome/app/os_settings_search_tag_strings_grdp/IDS_OS_SETTINGS_TAG_HOTSPOT_TURN_OFF.png.sha1 b/chrome/app/os_settings_search_tag_strings_grdp/IDS_OS_SETTINGS_TAG_HOTSPOT_TURN_OFF.png.sha1
new file mode 100644
index 0000000..a6219ff
--- /dev/null
+++ b/chrome/app/os_settings_search_tag_strings_grdp/IDS_OS_SETTINGS_TAG_HOTSPOT_TURN_OFF.png.sha1
@@ -0,0 +1 @@
+94ab62821f304d69cb2c0c0cb587c3d1f62727dd
\ No newline at end of file
diff --git a/chrome/app/os_settings_search_tag_strings_grdp/IDS_OS_SETTINGS_TAG_HOTSPOT_TURN_ON.png.sha1 b/chrome/app/os_settings_search_tag_strings_grdp/IDS_OS_SETTINGS_TAG_HOTSPOT_TURN_ON.png.sha1
new file mode 100644
index 0000000..9519a40
--- /dev/null
+++ b/chrome/app/os_settings_search_tag_strings_grdp/IDS_OS_SETTINGS_TAG_HOTSPOT_TURN_ON.png.sha1
@@ -0,0 +1 @@
+cbae9d9e5f993988e26352babf8628d574908ec2
\ No newline at end of file
diff --git a/chrome/app/profiles_strings.grdp b/chrome/app/profiles_strings.grdp
index 0d6507de..78f98a3 100644
--- a/chrome/app/profiles_strings.grdp
+++ b/chrome/app/profiles_strings.grdp
@@ -565,7 +565,7 @@
       Create
     </message>
     <message name="IDS_ENTERPRISE_PROFILE_WELCOME_LINK_DATA_CHECKBOX" desc="Label of the checkbox on the enterprise profile welcome screen that allows a user to link existing data to the new profile. The label informs the user that a profile will be created.">
-      Keep local browsing data (bookmarks, passwords, history, etc.)
+      Add existing browsing data to managed profile
     </message>
 
     <!-- Profile Picker -->
diff --git a/chrome/app/profiles_strings_grdp/IDS_ENTERPRISE_PROFILE_WELCOME_LINK_DATA_CHECKBOX.png.sha1 b/chrome/app/profiles_strings_grdp/IDS_ENTERPRISE_PROFILE_WELCOME_LINK_DATA_CHECKBOX.png.sha1
index f2e6acde..5bf9cd5 100644
--- a/chrome/app/profiles_strings_grdp/IDS_ENTERPRISE_PROFILE_WELCOME_LINK_DATA_CHECKBOX.png.sha1
+++ b/chrome/app/profiles_strings_grdp/IDS_ENTERPRISE_PROFILE_WELCOME_LINK_DATA_CHECKBOX.png.sha1
@@ -1 +1 @@
-1a1552ef740e04ca90bc420b75511fed840f35dc
\ No newline at end of file
+4a9974220323737880c9a58dd3403b6830712d02
\ No newline at end of file
diff --git a/chrome/app/settings_chromium_strings_grdp/OWNERS b/chrome/app/settings_chromium_strings_grdp/OWNERS
index 3410dab..f6f6ede 100644
--- a/chrome/app/settings_chromium_strings_grdp/OWNERS
+++ b/chrome/app/settings_chromium_strings_grdp/OWNERS
@@ -1,3 +1,4 @@
 file://chrome/browser/resources/settings/OWNERS
+file://ui/webui/PLATFORM_OWNERS
 
 sauski@google.com
diff --git a/chrome/app/settings_google_chrome_strings_grdp/OWNERS b/chrome/app/settings_google_chrome_strings_grdp/OWNERS
index 3410dab..f6f6ede 100644
--- a/chrome/app/settings_google_chrome_strings_grdp/OWNERS
+++ b/chrome/app/settings_google_chrome_strings_grdp/OWNERS
@@ -1,3 +1,4 @@
 file://chrome/browser/resources/settings/OWNERS
+file://ui/webui/PLATFORM_OWNERS
 
 sauski@google.com
diff --git a/chrome/app/settings_strings.grdp b/chrome/app/settings_strings.grdp
index 131157e4..3acca63 100644
--- a/chrome/app/settings_strings.grdp
+++ b/chrome/app/settings_strings.grdp
@@ -1554,7 +1554,10 @@
     <message name="IDS_SETTINGS_LANGUAGES_NO_LANGUAGES_ADDED" desc="Placeholder for language list settings when no languages have been added, on the Manage Languages page.">
       No languages added
     </message>
-    <message name="IDS_SETTINGS_LANGUAGES_REMOVE_ARIA_LABEL" desc="Text read by screen readers when focusing on the trash can icon for removing a language. Activating this button will remove the selected language from its current list. This text only announced by screen readers and is not visible in the UI.">
+    <message name="IDS_SETTINGS_LANGUAGES_ADD_ARIA_LABEL" desc="Text read by screen readers when focusing on the checkbox for adding a language. This text is only announced by screen readers and is not visible in the UI.">
+      Add <ph name="LANGUAGE_NAME">$1<ex>Swahili</ex></ph>
+    </message>
+    <message name="IDS_SETTINGS_LANGUAGES_REMOVE_ARIA_LABEL" desc="Text read by screen readers when focusing on the trash can icon for removing a language. Activating this button will remove the selected language from its current list. This text is only announced by screen readers and is not visible in the UI.">
       Remove <ph name="LANGUAGE_NAME">$1<ex>Swahili</ex></ph>
     </message>
     <message name="IDS_SETTINGS_TRANSLATE_PAGE_TITLE" desc="Name of the settings page which displays translate preferences.">
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_LANGUAGES_ADD_ARIA_LABEL.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_LANGUAGES_ADD_ARIA_LABEL.png.sha1
new file mode 100644
index 0000000..13e8a3b1
--- /dev/null
+++ b/chrome/app/settings_strings_grdp/IDS_SETTINGS_LANGUAGES_ADD_ARIA_LABEL.png.sha1
@@ -0,0 +1 @@
+8eda1338ad4ba5c30a03ef4996151e59f37c81e7
\ No newline at end of file
diff --git a/chrome/app/settings_strings_grdp/OWNERS b/chrome/app/settings_strings_grdp/OWNERS
index 3410dab..f6f6ede 100644
--- a/chrome/app/settings_strings_grdp/OWNERS
+++ b/chrome/app/settings_strings_grdp/OWNERS
@@ -1,3 +1,4 @@
 file://chrome/browser/resources/settings/OWNERS
+file://ui/webui/PLATFORM_OWNERS
 
 sauski@google.com
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 639be009..99890c7 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -9394,6 +9394,12 @@
      FEATURE_VALUE_TYPE(features::kAppDeduplicationServiceFondue)},
 #endif
 
+    {"autofill-suggest-server-card-instead-of-local-card",
+     flag_descriptions::kAutofillSuggestServerCardInsteadOfLocalCardName,
+     flag_descriptions::kAutofillSuggestServerCardInsteadOfLocalCardDescription,
+     kOsAll,
+     FEATURE_VALUE_TYPE(
+         autofill::features::kAutofillSuggestServerCardInsteadOfLocalCard)},
     // NOTE: Adding a new flag requires adding a corresponding entry to enum
     // "LoginCustomFlags" in tools/metrics/histograms/enums.xml. See "Flag
     // Histograms" in tools/metrics/histograms/README.md (run the
diff --git a/chrome/browser/accessibility/accessibility_ui.cc b/chrome/browser/accessibility/accessibility_ui.cc
index 4f0fcb6..b49d7402 100644
--- a/chrome/browser/accessibility/accessibility_ui.cc
+++ b/chrome/browser/accessibility/accessibility_ui.cc
@@ -340,7 +340,9 @@
     : WebUIController(web_ui) {
   // Set up the chrome://accessibility source.
   content::WebUIDataSource* html_source =
-      content::WebUIDataSource::Create(chrome::kChromeUIAccessibilityHost);
+      content::WebUIDataSource::CreateAndAdd(
+          web_ui->GetWebContents()->GetBrowserContext(),
+          chrome::kChromeUIAccessibilityHost);
 
   // Add required resources.
   html_source->UseStringsJs();
@@ -352,10 +354,6 @@
       base::BindRepeating(&HandleAccessibilityRequestCallback,
                           web_ui->GetWebContents()->GetBrowserContext()));
 
-  content::BrowserContext* browser_context =
-      web_ui->GetWebContents()->GetBrowserContext();
-  content::WebUIDataSource::Add(browser_context, html_source);
-
   web_ui->AddMessageHandler(std::make_unique<AccessibilityUIMessageHandler>());
 }
 
diff --git a/chrome/browser/ash/app_list/search/burn_in_controller.cc b/chrome/browser/ash/app_list/search/burn_in_controller.cc
index 73cc0c1..5e66a70 100644
--- a/chrome/browser/ash/app_list/search/burn_in_controller.cc
+++ b/chrome/browser/ash/app_list/search/burn_in_controller.cc
@@ -61,9 +61,10 @@
         ids_to_burnin_iteration_.end()) {
       // Result has been seen before. Set burnin_iteration, since the result
       // object has changed since last seen.
-      result->scoring().burnin_iteration = ids_to_burnin_iteration_[result_id];
+      result->scoring().set_burnin_iteration(
+          ids_to_burnin_iteration_[result_id]);
     } else {
-      result->scoring().burnin_iteration = burnin_iteration_counter_;
+      result->scoring().set_burnin_iteration(burnin_iteration_counter_);
       ids_to_burnin_iteration_[result_id] = burnin_iteration_counter_;
     }
   }
diff --git a/chrome/browser/ash/app_list/search/games/game_result.cc b/chrome/browser/ash/app_list/search/games/game_result.cc
index ab6a643..c179524 100644
--- a/chrome/browser/ash/app_list/search/games/game_result.cc
+++ b/chrome/browser/ash/app_list/search/games/game_result.cc
@@ -127,7 +127,7 @@
   LogIconLoadStatus(error);
   if (error != apps::DiscoveryError::kSuccess) {
     // Don't display results that have no icon.
-    scoring().filter = true;
+    scoring().set_filtered(true);
     return;
   }
 
diff --git a/chrome/browser/ash/app_list/search/games/game_result_unittest.cc b/chrome/browser/ash/app_list/search/games/game_result_unittest.cc
index 72aef5b..f659e88 100644
--- a/chrome/browser/ash/app_list/search/games/game_result_unittest.cc
+++ b/chrome/browser/ash/app_list/search/games/game_result_unittest.cc
@@ -164,7 +164,7 @@
                             app_discovery_service_.get(), no_icon_app, 0.6,
                             u"SomeGame");
 
-  EXPECT_TRUE(no_icon_result.scoring().filter);
+  EXPECT_TRUE(no_icon_result.scoring().filtered());
 }
 
 TEST_F(GameResultTest, OpensDeepLinkURLWhenAppNotFound) {
diff --git a/chrome/browser/ash/app_list/search/omnibox/omnibox_answer_result.cc b/chrome/browser/ash/app_list/search/omnibox/omnibox_answer_result.cc
index 7a4d1d7..dc939a80 100644
--- a/chrome/browser/ash/app_list/search/omnibox/omnibox_answer_result.cc
+++ b/chrome/browser/ash/app_list/search/omnibox/omnibox_answer_result.cc
@@ -197,7 +197,7 @@
           {CreateStringTextItem(temperature->second)});
     } else {
       // If the temperature can't be parsed, don't display this result.
-      scoring().filter = true;
+      scoring().set_filtered(true);
     }
 
     if (search_result_->description_a11y_label.has_value()) {
diff --git a/chrome/browser/ash/app_list/search/omnibox/omnibox_result.cc b/chrome/browser/ash/app_list/search/omnibox/omnibox_result.cc
index 80b3c23..df28890 100644
--- a/chrome/browser/ash/app_list/search/omnibox/omnibox_result.cc
+++ b/chrome/browser/ash/app_list/search/omnibox/omnibox_result.cc
@@ -145,7 +145,7 @@
   double title_relevance = CalculateTitleRelevance();
   if (fuzzy_match_cutoff_enabled) {
     if (title_relevance < kRelevanceThreshold) {
-      scoring().filter = true;
+      scoring().set_filtered(true);
     }
     set_relevance(normalized_autocomplete_relevance);
   } else {
diff --git a/chrome/browser/ash/app_list/search/omnibox/omnibox_result_unittest.cc b/chrome/browser/ash/app_list/search/omnibox/omnibox_result_unittest.cc
index a262829..3a2a317 100644
--- a/chrome/browser/ash/app_list/search/omnibox/omnibox_result_unittest.cc
+++ b/chrome/browser/ash/app_list/search/omnibox/omnibox_result_unittest.cc
@@ -250,7 +250,7 @@
   EXPECT_EQ(kExampleContents, result->details());
   EXPECT_EQ(kExampleDescription, result->title());
   EXPECT_EQ(kAppListRelevance, result->relevance());
-  EXPECT_FALSE(result->scoring().filter);
+  EXPECT_FALSE(result->scoring().filtered());
 
   result->Open(0);
   EXPECT_EQ(kExampleUrl, GetLastOpenedUrl().spec());
@@ -488,8 +488,8 @@
 
   EXPECT_EQ(kAppListRelevance, result_high_fuzzy_relevance->relevance());
   EXPECT_EQ(kAppListRelevance, result_low_fuzzy_relevance->relevance());
-  EXPECT_TRUE(result_low_fuzzy_relevance->scoring().filter);
-  EXPECT_FALSE(result_high_fuzzy_relevance->scoring().filter);
+  EXPECT_TRUE(result_low_fuzzy_relevance->scoring().filtered());
+  EXPECT_FALSE(result_high_fuzzy_relevance->scoring().filtered());
 }
 
 TEST_F(OmniboxResultTest, RelevanceWithFuzzyMatchRelevance) {
@@ -510,9 +510,9 @@
             result_high_fuzzy_relevance->relevance());
   EXPECT_EQ(kAppListRelevance / 2, result_low_fuzzy_relevance->relevance());
   EXPECT_EQ(kAppListRelevance / 2, result_empty_query->relevance());
-  EXPECT_FALSE(result_low_fuzzy_relevance->scoring().filter);
-  EXPECT_FALSE(result_high_fuzzy_relevance->scoring().filter);
-  EXPECT_FALSE(result_empty_query->scoring().filter);
+  EXPECT_FALSE(result_low_fuzzy_relevance->scoring().filtered());
+  EXPECT_FALSE(result_high_fuzzy_relevance->scoring().filtered());
+  EXPECT_FALSE(result_empty_query->scoring().filtered());
 }
 
 }  // namespace app_list::test
diff --git a/chrome/browser/ash/app_list/search/ranking/answer_ranker.cc b/chrome/browser/ash/app_list/search/ranking/answer_ranker.cc
index 6aa4abb..3f2bfd6 100644
--- a/chrome/browser/ash/app_list/search/ranking/answer_ranker.cc
+++ b/chrome/browser/ash/app_list/search/ranking/answer_ranker.cc
@@ -88,7 +88,7 @@
   if (provider == ProviderType::kOmnibox) {
     for (auto& result : new_results) {
       if (result->display_type() == DisplayType::kAnswerCard) {
-        result->scoring().filter = true;
+        result->scoring().set_filtered(true);
       }
     }
   }
@@ -140,7 +140,7 @@
   chosen_answer_->SetDisplayType(DisplayType::kAnswerCard);
   chosen_answer_->SetMultilineTitle(true);
   chosen_answer_->SetIconDimension(kAnswerCardIconDimension);
-  chosen_answer_->scoring().filter = false;
+  chosen_answer_->scoring().set_filtered(false);
 }
 
 }  // namespace app_list
diff --git a/chrome/browser/ash/app_list/search/ranking/answer_ranker_unittest.cc b/chrome/browser/ash/app_list/search/ranking/answer_ranker_unittest.cc
index 17f6e8e..61706e6e 100644
--- a/chrome/browser/ash/app_list/search/ranking/answer_ranker_unittest.cc
+++ b/chrome/browser/ash/app_list/search/ranking/answer_ranker_unittest.cc
@@ -42,7 +42,7 @@
   return result->display_type() == ash::SearchResultDisplayType::kAnswerCard &&
          result->multiline_title() &&
          result->icon().dimension == kAnswerCardIconDimension &&
-         !result->scoring().filter;
+         !result->scoring().filtered();
 }
 
 }  // namespace
@@ -64,8 +64,8 @@
   EXPECT_TRUE(AnswerFieldsAreSet(results[1]));
 
   // Others are filtered out.
-  EXPECT_TRUE(results[0]->scoring().filter);
-  EXPECT_TRUE(results[2]->scoring().filter);
+  EXPECT_TRUE(results[0]->scoring().filtered());
+  EXPECT_TRUE(results[2]->scoring().filtered());
 }
 
 // Tests that a best match shortcut is selected.
@@ -152,7 +152,7 @@
 
   const auto& omnibox_results = results_map[ResultType::kOmnibox];
   ASSERT_EQ(omnibox_results.size(), 1u);
-  EXPECT_TRUE(omnibox_results[0]->scoring().filter);
+  EXPECT_TRUE(omnibox_results[0]->scoring().filtered());
 }
 
 }  // namespace app_list::test
diff --git a/chrome/browser/ash/app_list/search/ranking/best_match_ranker.cc b/chrome/browser/ash/app_list/search/ranking/best_match_ranker.cc
index 70d5da4..a8a23bfc 100644
--- a/chrome/browser/ash/app_list/search/ranking/best_match_ranker.cc
+++ b/chrome/browser/ash/app_list/search/ranking/best_match_ranker.cc
@@ -152,8 +152,8 @@
     // results can technically be destroyed at any time.
     std::sort(best_matches_.begin(), best_matches_.end(),
               [](const auto& a, const auto& b) {
-                const int a_rank = a->scoring().best_match_rank;
-                const int b_rank = b->scoring().best_match_rank;
+                const int a_rank = a->scoring().best_match_rank();
+                const int b_rank = b->scoring().best_match_rank();
                 // Sort order: 0, 1, 2, 3, ... then -1.
                 // N.B. (a ^ b) < 0 checks for opposite sign.
                 return (a_rank ^ b_rank) < 0 ? a_rank > b_rank
@@ -161,15 +161,15 @@
               });
     std::sort(best_matches_.begin() + kNumBestMatchesToStabilize,
               best_matches_.end(), [](const auto& a, const auto& b) {
-                return a->scoring().normalized_relevance >
-                       b->scoring().normalized_relevance;
+                return a->scoring().normalized_relevance() >
+                       b->scoring().normalized_relevance();
               });
   }
 
   // For the first kNumBestMatches of best_matches_, renumber their best match
   // rank.
   for (size_t i = 0; i < std::min(kNumBestMatches, best_matches_.size()); ++i) {
-    best_matches_[i]->scoring().best_match_rank = i;
+    best_matches_[i]->scoring().set_best_match_rank(i);
     best_matches_[i]->SetBestMatch(true);
   }
 
@@ -177,7 +177,7 @@
   // and remove them from the vector.
   if (best_matches_.size() > kNumBestMatches) {
     for (size_t i = kNumBestMatches; i < best_matches_.size(); ++i) {
-      best_matches_[i]->scoring().best_match_rank = -1;
+      best_matches_[i]->scoring().set_best_match_rank(-1);
       best_matches_[i]->SetBestMatch(false);
     }
     best_matches_.resize(kNumBestMatches);
diff --git a/chrome/browser/ash/app_list/search/ranking/best_match_ranker_unittest.cc b/chrome/browser/ash/app_list/search/ranking/best_match_ranker_unittest.cc
index 43cecaa..8ce3fe3 100644
--- a/chrome/browser/ash/app_list/search/ranking/best_match_ranker_unittest.cc
+++ b/chrome/browser/ash/app_list/search/ranking/best_match_ranker_unittest.cc
@@ -46,7 +46,7 @@
     std::transform(ranker_.best_matches_.begin(), ranker_.best_matches_.end(),
                    std::back_inserter(actual_ids_ranks),
                    [](auto res) -> const std::pair<std::string, int> {
-                     return {res->id(), res->scoring().best_match_rank};
+                     return {res->id(), res->scoring().best_match_rank()};
                    });
     EXPECT_THAT(actual_ids_ranks, ElementsAreArray(expected_ids_ranks));
   }
@@ -81,7 +81,7 @@
 
   for (const auto& res : results) {
     result_map_ids.push_back(res->id());
-    result_map_ranks.push_back(res->scoring().best_match_rank);
+    result_map_ranks.push_back(res->scoring().best_match_rank());
     shared_metadata_best_match_status.push_back(res->best_match());
   }
 
@@ -218,7 +218,7 @@
 
   for (const auto& res : results) {
     result_map_ids.push_back(res->id());
-    result_map_ranks.push_back(res->scoring().best_match_rank);
+    result_map_ranks.push_back(res->scoring().best_match_rank());
     shared_metadata_best_match_status.push_back(res->best_match());
   }
 
diff --git a/chrome/browser/ash/app_list/search/ranking/continue_ranker.cc b/chrome/browser/ash/app_list/search/ranking/continue_ranker.cc
index fa64776..bbb9f8a 100644
--- a/chrome/browser/ash/app_list/search/ranking/continue_ranker.cc
+++ b/chrome/browser/ash/app_list/search/ranking/continue_ranker.cc
@@ -21,13 +21,13 @@
   // giving them a higher continue_rank.
   if (provider == ProviderType::kZeroStateFile) {
     for (auto& result : it->second)
-      result->scoring().continue_rank = 1;
+      result->scoring().set_continue_rank(1);
   } else if (provider == ProviderType::kZeroStateDrive) {
     for (auto& result : it->second)
-      result->scoring().continue_rank = 2;
+      result->scoring().set_continue_rank(2);
   } else if (provider == ProviderType::kZeroStateHelpApp) {
     for (auto& result : it->second)
-      result->scoring().continue_rank = 3;
+      result->scoring().set_continue_rank(3);
   }
 }
 
diff --git a/chrome/browser/ash/app_list/search/ranking/filtering_ranker.cc b/chrome/browser/ash/app_list/search/ranking/filtering_ranker.cc
index bbc8010..c9238f9a4 100644
--- a/chrome/browser/ash/app_list/search/ranking/filtering_ranker.cc
+++ b/chrome/browser/ash/app_list/search/ranking/filtering_ranker.cc
@@ -41,7 +41,7 @@
 
   for (auto& result : second_results) {
     if (first_ids.contains(result->id()))
-      result->scoring().filter = true;
+      result->scoring().set_filtered(true);
   }
 }
 
@@ -62,7 +62,7 @@
   for (auto& result : drive_results) {
     const auto& drive_id = result->DriveId();
     if (drive_id && drive_tab_ids.contains(drive_id.value()))
-      result->scoring().filter = true;
+      result->scoring().set_filtered(true);
   }
 }
 
@@ -87,7 +87,7 @@
               CrosApiSearchResult::AnswerType::kTranslation;
       if (omnibox_result->display_type() == DisplayType::kAnswerCard &&
           is_type_dictionary_or_translation)
-        scoring.filter = true;
+        scoring.set_filtered(true);
     }
   }
 
@@ -111,9 +111,10 @@
   // but never remove best matches  or answer cards.
   for (size_t i = kMaxOmniboxResults; i < omnibox_results.size(); ++i) {
     auto& scoring = omnibox_results[i]->scoring();
-    if (scoring.best_match_rank == -1 &&
-        omnibox_results[i]->display_type() != DisplayType::kAnswerCard)
-      scoring.filter = true;
+    if (scoring.best_match_rank() == -1 &&
+        omnibox_results[i]->display_type() != DisplayType::kAnswerCard) {
+      scoring.set_filtered(true);
+    }
   }
 }
 
diff --git a/chrome/browser/ash/app_list/search/ranking/filtering_ranker_unittest.cc b/chrome/browser/ash/app_list/search/ranking/filtering_ranker_unittest.cc
index 76c2755..145f3ba 100644
--- a/chrome/browser/ash/app_list/search/ranking/filtering_ranker_unittest.cc
+++ b/chrome/browser/ash/app_list/search/ranking/filtering_ranker_unittest.cc
@@ -90,11 +90,11 @@
   ranker.Start(u"query", results, categories);
   ranker.UpdateResultRanks(results, ProviderType::kKeyboardShortcut);
 
-  EXPECT_FALSE(results[drive][0]->scoring().filter);
-  EXPECT_TRUE(results[drive][1]->scoring().filter);
-  EXPECT_FALSE(results[drive][2]->scoring().filter);
-  EXPECT_FALSE(results[drive][3]->scoring().filter);
-  EXPECT_FALSE(results[drive][4]->scoring().filter);
+  EXPECT_FALSE(results[drive][0]->scoring().filtered());
+  EXPECT_TRUE(results[drive][1]->scoring().filtered());
+  EXPECT_FALSE(results[drive][2]->scoring().filtered());
+  EXPECT_FALSE(results[drive][3]->scoring().filtered());
+  EXPECT_FALSE(results[drive][4]->scoring().filtered());
 }
 
 // Test that answers of certain kinds (that tend to over-trigger) aren't shown
@@ -118,11 +118,11 @@
   ranker.UpdateResultRanks(results, ProviderType::kOmnibox);
 
   // All results except dictionary and translate answers are allowed.
-  EXPECT_FALSE(results[web][0]->scoring().filter);
-  EXPECT_TRUE(results[web][1]->scoring().filter);
-  EXPECT_FALSE(results[web][2]->scoring().filter);
-  EXPECT_TRUE(results[web][3]->scoring().filter);
-  EXPECT_FALSE(results[web][4]->scoring().filter);
+  EXPECT_FALSE(results[web][0]->scoring().filtered());
+  EXPECT_TRUE(results[web][1]->scoring().filtered());
+  EXPECT_FALSE(results[web][2]->scoring().filtered());
+  EXPECT_TRUE(results[web][3]->scoring().filtered());
+  EXPECT_FALSE(results[web][4]->scoring().filtered());
 }
 
 }  // namespace app_list::test
diff --git a/chrome/browser/ash/app_list/search/ranking/ftrl_ranker.cc b/chrome/browser/ash/app_list/search/ranking/ftrl_ranker.cc
index 091bf7012..8e7a736 100644
--- a/chrome/browser/ash/app_list/search/ranking/ftrl_ranker.cc
+++ b/chrome/browser/ash/app_list/search/ranking/ftrl_ranker.cc
@@ -69,7 +69,7 @@
       ftrl_->Score(std::move(ids), std::move(expert_scores));
   DCHECK_EQ(new_results.size(), result_scores.size());
   for (size_t i = 0; i < new_results.size(); ++i)
-    new_results[i]->scoring().ftrl_result_score = result_scores[i];
+    new_results[i]->scoring().set_ftrl_result_score(result_scores[i]);
 }
 
 void FtrlRanker::UpdateCategoryRanks(const ResultsMap& results,
@@ -114,10 +114,10 @@
     double score;
     switch (member_) {
       case ResultScoringShim::ScoringMember::kNormalizedRelevance:
-        score = result->scoring().normalized_relevance;
+        score = result->scoring().normalized_relevance();
         break;
       case ResultScoringShim::ScoringMember::kMrfuResultScore:
-        score = result->scoring().mrfu_result_score;
+        score = result->scoring().mrfu_result_score();
         break;
     }
     scores.push_back(score);
@@ -149,13 +149,13 @@
     // Ignore best match, answer card results, and filtered results for the
     // purposes of deciding category scores, because they are not displayed in
     // their category.
-    if (result->best_match() || result->scoring().filter ||
+    if (result->best_match() || result->scoring().filtered() ||
         result->display_type() ==
             ChromeSearchResult::DisplayType::kAnswerCard) {
       continue;
     }
     current_category_scores_[result->category()] =
-        std::max(result->scoring().normalized_relevance,
+        std::max(result->scoring().normalized_relevance(),
                  current_category_scores_[result->category()]);
   }
 
diff --git a/chrome/browser/ash/app_list/search/ranking/ftrl_ranker_unittest.cc b/chrome/browser/ash/app_list/search/ranking/ftrl_ranker_unittest.cc
index 6cebeada..8d632be99 100644
--- a/chrome/browser/ash/app_list/search/ranking/ftrl_ranker_unittest.cc
+++ b/chrome/browser/ash/app_list/search/ranking/ftrl_ranker_unittest.cc
@@ -170,9 +170,9 @@
 
   auto results = MakeResults({"a", "b", "c"});
   ASSERT_EQ(results.size(), 3u);
-  results[0]->scoring().normalized_relevance = 0.2;
-  results[1]->scoring().normalized_relevance = 0.5;
-  results[2]->scoring().normalized_relevance = 0.1;
+  results[0]->scoring().set_normalized_relevance(0.2);
+  results[1]->scoring().set_normalized_relevance(0.5);
+  results[2]->scoring().set_normalized_relevance(0.1);
 
   ResultsMap results_map;
   results_map[ResultType::kInstalledApp] = std::move(results);
diff --git a/chrome/browser/ash/app_list/search/ranking/mrfu_ranker.cc b/chrome/browser/ash/app_list/search/ranking/mrfu_ranker.cc
index 1a083546..c29e377 100644
--- a/chrome/browser/ash/app_list/search/ranking/mrfu_ranker.cc
+++ b/chrome/browser/ash/app_list/search/ranking/mrfu_ranker.cc
@@ -38,7 +38,7 @@
     return;
 
   for (auto& result : it->second)
-    result->scoring().mrfu_result_score = mrfu_->Get(result->id());
+    result->scoring().set_mrfu_result_score(mrfu_->Get(result->id()));
 }
 
 void MrfuResultRanker::Train(const LaunchData& launch) {
diff --git a/chrome/browser/ash/app_list/search/ranking/removed_results_ranker.cc b/chrome/browser/ash/app_list/search/ranking/removed_results_ranker.cc
index b7c3e33..f48601f 100644
--- a/chrome/browser/ash/app_list/search/ranking/removed_results_ranker.cc
+++ b/chrome/browser/ash/app_list/search/ranking/removed_results_ranker.cc
@@ -42,11 +42,11 @@
   const bool proto_initialized = initialized();
   for (const auto& result : it->second) {
     if (!proto_initialized) {
-      result->scoring().filter =
-          result->display_type() != DisplayType::kRecentApps;
+      result->scoring().set_filtered(result->display_type() !=
+                                     DisplayType::kRecentApps);
     } else {
-      result->scoring().filter =
-          (*proto_)->removed_ids().contains(result->id());
+      result->scoring().set_filtered(
+          (*proto_)->removed_ids().contains(result->id()));
     }
   }
 }
diff --git a/chrome/browser/ash/app_list/search/ranking/removed_results_ranker_unittest.cc b/chrome/browser/ash/app_list/search/ranking/removed_results_ranker_unittest.cc
index 7fec639..8107330 100644
--- a/chrome/browser/ash/app_list/search/ranking/removed_results_ranker_unittest.cc
+++ b/chrome/browser/ash/app_list/search/ranking/removed_results_ranker_unittest.cc
@@ -91,13 +91,13 @@
 
   // Installed apps: The 0th result ("A") is marked to be filtered.
   ranker_->UpdateResultRanks(results_map, ResultType::kInstalledApp);
-  EXPECT_TRUE(results_map[ResultType::kInstalledApp][0]->scoring().filter);
-  EXPECT_FALSE(results_map[ResultType::kInstalledApp][1]->scoring().filter);
+  EXPECT_TRUE(results_map[ResultType::kInstalledApp][0]->scoring().filtered());
+  EXPECT_FALSE(results_map[ResultType::kInstalledApp][1]->scoring().filtered());
 
   // Internal apps: The 0th result ("C") is marked to be filtered.
   ranker_->UpdateResultRanks(results_map, ResultType::kInternalApp);
-  EXPECT_TRUE(results_map[ResultType::kInternalApp][0]->scoring().filter);
-  EXPECT_FALSE(results_map[ResultType::kInternalApp][1]->scoring().filter);
+  EXPECT_TRUE(results_map[ResultType::kInternalApp][0]->scoring().filtered());
+  EXPECT_FALSE(results_map[ResultType::kInternalApp][1]->scoring().filtered());
 
   // Omnibox: The 0th result ("C") is marked to be filtered.
   //
@@ -105,7 +105,7 @@
   // after support is added to the autocomplete controller for removal of
   // non-zero state Omnibox results.
   ranker_->UpdateResultRanks(results_map, ResultType::kOmnibox);
-  EXPECT_TRUE(results_map[ResultType::kOmnibox][0]->scoring().filter);
+  EXPECT_TRUE(results_map[ResultType::kOmnibox][0]->scoring().filtered());
 }
 
 TEST_F(RemovedResultsRankerTest, RankEmptyResults) {
@@ -134,14 +134,14 @@
 
   // Installed apps: The 0th and 1st results ("A") are marked to be filtered.
   ranker_->UpdateResultRanks(results_map, ResultType::kInstalledApp);
-  EXPECT_TRUE(results_map[ResultType::kInstalledApp][0]->scoring().filter);
-  EXPECT_TRUE(results_map[ResultType::kInstalledApp][1]->scoring().filter);
-  EXPECT_FALSE(results_map[ResultType::kInstalledApp][2]->scoring().filter);
+  EXPECT_TRUE(results_map[ResultType::kInstalledApp][0]->scoring().filtered());
+  EXPECT_TRUE(results_map[ResultType::kInstalledApp][1]->scoring().filtered());
+  EXPECT_FALSE(results_map[ResultType::kInstalledApp][2]->scoring().filtered());
 
   // Internal apps: The 0th result ("C") is marked to be filtered.
   ranker_->UpdateResultRanks(results_map, ResultType::kInternalApp);
-  EXPECT_TRUE(results_map[ResultType::kInternalApp][0]->scoring().filter);
-  EXPECT_FALSE(results_map[ResultType::kInternalApp][1]->scoring().filter);
+  EXPECT_TRUE(results_map[ResultType::kInternalApp][0]->scoring().filtered());
+  EXPECT_FALSE(results_map[ResultType::kInternalApp][1]->scoring().filtered());
 }
 
 // Verifies that the ranker removes a result through the file suggest keyed
@@ -195,10 +195,10 @@
   ranker_->UpdateResultRanks(results_map, ResultType::kOmnibox);
 
   // All results should be filtered out except for the recent app.
-  EXPECT_FALSE(results_map[ResultType::kInstalledApp][0]->scoring().filter);
-  EXPECT_TRUE(results_map[ResultType::kInstalledApp][1]->scoring().filter);
-  EXPECT_TRUE(results_map[ResultType::kOmnibox][0]->scoring().filter);
-  EXPECT_TRUE(results_map[ResultType::kOmnibox][1]->scoring().filter);
+  EXPECT_FALSE(results_map[ResultType::kInstalledApp][0]->scoring().filtered());
+  EXPECT_TRUE(results_map[ResultType::kInstalledApp][1]->scoring().filtered());
+  EXPECT_TRUE(results_map[ResultType::kOmnibox][0]->scoring().filtered());
+  EXPECT_TRUE(results_map[ResultType::kOmnibox][1]->scoring().filtered());
 }
 
 }  // namespace app_list::test
diff --git a/chrome/browser/ash/app_list/search/ranking/score_normalizing_ranker.cc b/chrome/browser/ash/app_list/search/ranking/score_normalizing_ranker.cc
index 8e920a9d..c905009 100644
--- a/chrome/browser/ash/app_list/search/ranking/score_normalizing_ranker.cc
+++ b/chrome/browser/ash/app_list/search/ranking/score_normalizing_ranker.cc
@@ -59,8 +59,8 @@
   }
 
   for (auto& result : it->second) {
-    result->scoring().normalized_relevance =
-        normalizer_.Normalize(provider_string, result->relevance());
+    result->scoring().set_normalized_relevance(
+        normalizer_.Normalize(provider_string, result->relevance()));
   }
 }
 
diff --git a/chrome/browser/ash/app_list/search/ranking/score_normalizing_ranker_unittest.cc b/chrome/browser/ash/app_list/search/ranking/score_normalizing_ranker_unittest.cc
index f621b6b..aceaecc6 100644
--- a/chrome/browser/ash/app_list/search/ranking/score_normalizing_ranker_unittest.cc
+++ b/chrome/browser/ash/app_list/search/ranking/score_normalizing_ranker_unittest.cc
@@ -72,7 +72,7 @@
   // are ordered by relevance after normalization.
   const auto& app_results = results[ResultType::kInstalledApp];
   for (size_t i = 0; i < 4; ++i) {
-    EXPECT_GT(app_results[i]->scoring().normalized_relevance, 0.0);
+    EXPECT_GT(app_results[i]->scoring().normalized_relevance(), 0.0);
   }
 
   std::vector<ChromeSearchResult*> ordered_app_results;
@@ -82,8 +82,8 @@
 
   std::sort(ordered_app_results.begin(), ordered_app_results.end(),
             [](const auto& r1, const auto& r2) {
-              return r1->scoring().normalized_relevance >
-                     r2->scoring().normalized_relevance;
+              return r1->scoring().normalized_relevance() >
+                     r2->scoring().normalized_relevance();
             });
 
   std::vector<std::string> ordered_app_result_ids;
@@ -96,8 +96,8 @@
 
   // File results should not have been scored.
   const auto& file_results = results[ResultType::kFileSearch];
-  EXPECT_FLOAT_EQ(file_results[0]->scoring().normalized_relevance, 0.0);
-  EXPECT_FLOAT_EQ(file_results[1]->scoring().normalized_relevance, 0.0);
+  EXPECT_FLOAT_EQ(file_results[0]->scoring().normalized_relevance(), 0.0);
+  EXPECT_FLOAT_EQ(file_results[1]->scoring().normalized_relevance(), 0.0);
 }
 
 }  // namespace app_list::test
diff --git a/chrome/browser/ash/app_list/search/ranking/sorting.cc b/chrome/browser/ash/app_list/search/ranking/sorting.cc
index 415e51f..57aca5c0 100644
--- a/chrome/browser/ash/app_list/search/ranking/sorting.cc
+++ b/chrome/browser/ash/app_list/search/ranking/sorting.cc
@@ -43,8 +43,8 @@
   std::sort(
       results.begin(), results.end(),
       [&](const ChromeSearchResult* a, const ChromeSearchResult* b) {
-        const int a_best_match_rank = a->scoring().best_match_rank;
-        const int b_best_match_rank = b->scoring().best_match_rank;
+        const int a_best_match_rank = a->scoring().best_match_rank();
+        const int b_best_match_rank = b->scoring().best_match_rank();
         if (a_best_match_rank != b_best_match_rank) {
           // First, sort by best match. All best matches are brought to
           // the front of the list and ordered by best_match_rank.
@@ -76,18 +76,20 @@
           return false;
         }
 
-        if (a->scoring().burnin_iteration != b->scoring().burnin_iteration) {
+        if (a->scoring().burnin_iteration() !=
+            b->scoring().burnin_iteration()) {
           // Next, sort by burn-in iteration number. This has no effect on
           // results which arrive pre-burn-in. For post-burn-in results
           // for a given category, later-arriving results are placed below
           // earlier-arriving results.
           // This happens before sorting on display_score, as a trade-off
           // between ranking accuracy and UX pop-in mitigation.
-          return a->scoring().burnin_iteration < b->scoring().burnin_iteration;
+          return a->scoring().burnin_iteration() <
+                 b->scoring().burnin_iteration();
         }
 
-        if (a->scoring().continue_rank != b->scoring().continue_rank) {
-          return a->scoring().continue_rank > b->scoring().continue_rank;
+        if (a->scoring().continue_rank() != b->scoring().continue_rank()) {
+          return a->scoring().continue_rank() > b->scoring().continue_rank();
         }
 
         // Lastly, sort by display score.
diff --git a/chrome/browser/ash/app_list/search/scoring.cc b/chrome/browser/ash/app_list/search/scoring.cc
index ad95be0..46b2bce 100644
--- a/chrome/browser/ash/app_list/search/scoring.cc
+++ b/chrome/browser/ash/app_list/search/scoring.cc
@@ -11,26 +11,62 @@
 namespace app_list {
 
 double Scoring::FinalScore() const {
-  if (filter && !override_filter_for_test)
+  if (filtered_ && !override_filter_for_test_) {
     return -1.0;
-  return ftrl_result_score;
+  }
+  return ftrl_result_score_;
 }
 
 double Scoring::BestMatchScore() const {
-  if (filter)
+  if (filtered_) {
     return -1.0;
-  else
-    return std::max(mrfu_result_score, normalized_relevance);
+  } else {
+    return std::max(mrfu_result_score_, normalized_relevance_);
+  }
 }
 
 ::std::ostream& operator<<(::std::ostream& os, const Scoring& scoring) {
-  if (scoring.filter)
+  if (scoring.filtered()) {
     return os << "{" << scoring.FinalScore() << " | filtered}";
+  }
+
   return os << base::StringPrintf(
              "{%.2f | nr:%.2f rs:%.2f bm:%d cr:%d bi:%d}", scoring.FinalScore(),
-             scoring.normalized_relevance, scoring.ftrl_result_score,
-             scoring.best_match_rank, scoring.continue_rank,
-             scoring.burnin_iteration);
+             scoring.normalized_relevance(), scoring.ftrl_result_score(),
+             scoring.best_match_rank(), scoring.continue_rank(),
+             scoring.burnin_iteration());
+}
+
+void Scoring::set_filtered(bool filtered) {
+  filtered_ = filtered;
+}
+
+void Scoring::set_normalized_relevance(double normalized_relevance) {
+  normalized_relevance_ = normalized_relevance;
+}
+
+void Scoring::set_mrfu_result_score(double mrfu_result_score) {
+  mrfu_result_score_ = mrfu_result_score;
+}
+
+void Scoring::set_ftrl_result_score(double ftrl_result_score) {
+  ftrl_result_score_ = ftrl_result_score;
+}
+
+void Scoring::set_continue_rank(int continue_rank) {
+  continue_rank_ = continue_rank;
+}
+
+void Scoring::set_best_match_rank(int best_match_rank) {
+  best_match_rank_ = best_match_rank;
+}
+
+void Scoring::set_burnin_iteration(int burnin_iteration) {
+  burnin_iteration_ = burnin_iteration;
+}
+
+void Scoring::override_filter_for_test(bool override) {
+  override_filter_for_test_ = override;
 }
 
 }  // namespace app_list
diff --git a/chrome/browser/ash/app_list/search/scoring.h b/chrome/browser/ash/app_list/search/scoring.h
index 02f7b1e..d2721f4 100644
--- a/chrome/browser/ash/app_list/search/scoring.h
+++ b/chrome/browser/ash/app_list/search/scoring.h
@@ -12,36 +12,9 @@
 // All score information for a single result. This is stored with a result, and
 // is used as 'scratch space' for ranking calculations to pass information
 // between rankers. Generally, each member is controlled by one ranker.
-struct Scoring {
-  // = Members used to compute the display score of a result ===================
-  bool filter = false;
-  double normalized_relevance = 0.0;
-  double mrfu_result_score = 0.0;
-  double ftrl_result_score = 0.0;
-  // TODO(b/259607603) remove 'override_filter_for_test'. This field is used
-  // to temporarily disable filtering for a specific result. This is needed
-  // due to a race condition with the test beginning before the
-  // RemovedResultsRanker is initialized.
-  bool override_filter_for_test = false;
-
-  // Used only for results in the Continue section. Continue results are first
-  // ordered by |continue_rank|, and then by their display score. -1 indicates
-  // this is unset.
-  int continue_rank = -1;
-
-  // = Members used for sorting in SearchController ============================
-  // The rank (0, 1, 2, ...) of this result within
-  // the Best Match collection of results, or -1 if this result is not a Best
-  // Match.
-  int best_match_rank = -1;
-  // A counter for the burn-in iteration number, where 0 signifies the
-  // pre-burn-in state, and 1 and above signify the post-burn-in state.
-  // Incremented during the post-burn-in period each time a provider
-  // returns. Not applicable to zero-state search.
-  int burnin_iteration = 0;
-
+class Scoring {
+ public:
   Scoring() = default;
-
   Scoring(const Scoring&) = delete;
   Scoring& operator=(const Scoring&) = delete;
 
@@ -50,6 +23,57 @@
 
   // Score used to determine if a result should be considered a best match.
   double BestMatchScore() const;
+
+  void set_filtered(bool filtered);
+  bool filtered() const { return filtered_; }
+
+  void set_normalized_relevance(double normalized_relevance);
+  double normalized_relevance() const { return normalized_relevance_; }
+
+  void set_mrfu_result_score(double mrfu_result_score);
+  double mrfu_result_score() const { return mrfu_result_score_; }
+
+  void set_ftrl_result_score(double ftrl_result_score);
+  double ftrl_result_score() const { return ftrl_result_score_; }
+
+  void set_continue_rank(int continue_rank);
+  int continue_rank() const { return continue_rank_; }
+
+  void set_best_match_rank(int best_match_rank);
+  int best_match_rank() const { return best_match_rank_; }
+
+  void set_burnin_iteration(int burnin_iteration);
+  int burnin_iteration() const { return burnin_iteration_; }
+
+  void override_filter_for_test(bool override);
+
+ private:
+  // = Members used to compute the display score of a result ===================
+  bool filtered_ = false;
+  double normalized_relevance_ = 0.0;
+  double mrfu_result_score_ = 0.0;
+  double ftrl_result_score_ = 0.0;
+  // TODO(b/259607603) remove 'override_filter_for_test'. This field is used
+  // to temporarily disable filtering for a specific result. This is needed
+  // due to a race condition with the test beginning before the
+  // RemovedResultsRanker is initialized.
+  bool override_filter_for_test_ = false;
+
+  // Used only for results in the Continue section. Continue results are first
+  // ordered by |continue_rank|, and then by their display score. -1 indicates
+  // this is unset.
+  int continue_rank_ = -1;
+
+  // = Members used for sorting in SearchController ============================
+  // The rank (0, 1, 2, ...) of this result within
+  // the Best Match collection of results, or -1 if this result is not a Best
+  // Match.
+  int best_match_rank_ = -1;
+  // A counter for the burn-in iteration number, where 0 signifies the
+  // pre-burn-in state, and 1 and above signify the post-burn-in state.
+  // Incremented during the post-burn-in period each time a provider
+  // returns. Not applicable to zero-state search.
+  int burnin_iteration_ = 0;
 };
 
 ::std::ostream& operator<<(::std::ostream& os, const Scoring& result);
diff --git a/chrome/browser/ash/app_list/search/search_controller_impl.cc b/chrome/browser/ash/app_list/search/search_controller_impl.cc
index bcc9fa85..940f50d 100644
--- a/chrome/browser/ash/app_list/search/search_controller_impl.cc
+++ b/chrome/browser/ash/app_list/search/search_controller_impl.cc
@@ -189,7 +189,7 @@
     ranker_manager_->Remove(result);
     // We need to update the currently published results to not include the
     // just-removed result. Manually set the result as filtered and re-publish.
-    result->scoring().filter = true;
+    result->scoring().set_filtered(true);
     Publish();
   }
 }
diff --git a/chrome/browser/ash/app_list/search/test/test_result.cc b/chrome/browser/ash/app_list/search/test/test_result.cc
index 7faecd9..ab18a5a 100644
--- a/chrome/browser/ash/app_list/search/test/test_result.cc
+++ b/chrome/browser/ash/app_list/search/test/test_result.cc
@@ -16,7 +16,7 @@
   SetResultType(result_type);
   SetCategory(category);
   SetDisplayScore(display_score);
-  scoring().normalized_relevance = normalized_relevance;
+  scoring().set_normalized_relevance(normalized_relevance);
 }
 
 TestResult::TestResult(const std::string& id,
@@ -27,7 +27,7 @@
   set_id(id);
   SetTitle(base::UTF8ToUTF16(id));
   set_relevance(relevance);
-  scoring().normalized_relevance = normalized_relevance;
+  scoring().set_normalized_relevance(normalized_relevance);
   SetDisplayType(display_type);
   SetBestMatch(best_match);
 }
@@ -42,9 +42,9 @@
   SetTitle(base::UTF8ToUTF16(id));
   SetDisplayType(display_type);
   SetCategory(category);
-  scoring().best_match_rank = best_match_rank;
+  scoring().set_best_match_rank(best_match_rank);
   set_relevance(relevance);
-  scoring().ftrl_result_score = ftrl_result_score;
+  scoring().set_ftrl_result_score(ftrl_result_score);
 }
 
 TestResult::TestResult(const std::string& id,
@@ -65,7 +65,7 @@
   set_id(id);
   SetTitle(base::UTF8ToUTF16(id));
   set_relevance(relevance);
-  scoring().normalized_relevance = normalized_relevance;
+  scoring().set_normalized_relevance(normalized_relevance);
   SetMetricsType(metrics_type);
 }
 
diff --git a/chrome/browser/ash/child_accounts/time_limit_test_utils.cc b/chrome/browser/ash/child_accounts/time_limit_test_utils.cc
index c29be44..dbd00f0 100644
--- a/chrome/browser/ash/child_accounts/time_limit_test_utils.cc
+++ b/chrome/browser/ash/child_accounts/time_limit_test_utils.cc
@@ -128,20 +128,9 @@
                         base::TimeDelta start_time,
                         base::TimeDelta end_time,
                         base::Time last_updated) {
-  base::Value::Dict* time_window_limit = policy->FindDict(kTimeWindowLimit);
-  if (!time_window_limit) {
-    time_window_limit =
-        &policy->Set(kTimeWindowLimit, base::Value::Dict())->GetDict();
-  }
-
+  base::Value::Dict* time_window_limit = policy->EnsureDict(kTimeWindowLimit);
   base::Value::List* window_limit_entries =
-      time_window_limit->FindList(kWindowLimitEntries);
-  if (!window_limit_entries) {
-    window_limit_entries =
-        &time_window_limit->Set(kWindowLimitEntries, base::Value::List())
-             ->GetList();
-  }
-
+      time_window_limit->EnsureList(kWindowLimitEntries);
   window_limit_entries->Append(
       CreateTimeWindow(day, start_time, end_time, last_updated));
 }
@@ -149,34 +138,22 @@
 void AddOverride(base::Value::Dict* policy,
                  usage_time_limit::TimeLimitOverride::Action action,
                  base::Time created_at) {
-  base::Value* overrides =
-      policy->Find(usage_time_limit::TimeLimitOverride::kOverridesDictKey);
-  if (!overrides) {
-    overrides =
-        policy->Set(usage_time_limit::TimeLimitOverride::kOverridesDictKey,
-                    base::Value::List());
-  }
-
+  base::Value::List* overrides = policy->EnsureList(
+      usage_time_limit::TimeLimitOverride::kOverridesDictKey);
   usage_time_limit::TimeLimitOverride new_override(action, created_at,
                                                    absl::nullopt);
-  overrides->Append(base::Value(new_override.ToDictionary()));
+  overrides->Append(new_override.ToDictionary());
 }
 
 void AddOverrideWithDuration(base::Value::Dict* policy,
                              usage_time_limit::TimeLimitOverride::Action action,
                              base::Time created_at,
                              base::TimeDelta duration) {
-  base::Value* overrides =
-      policy->Find(usage_time_limit::TimeLimitOverride::kOverridesDictKey);
-  if (!overrides) {
-    overrides =
-        policy->Set(usage_time_limit::TimeLimitOverride::kOverridesDictKey,
-                    base::Value::List());
-  }
-
+  base::Value::List* overrides = policy->EnsureList(
+      usage_time_limit::TimeLimitOverride::kOverridesDictKey);
   usage_time_limit::TimeLimitOverride new_override(action, created_at,
                                                    duration);
-  overrides->Append(base::Value(new_override.ToDictionary()));
+  overrides->Append(new_override.ToDictionary());
 }
 
 std::string PolicyToString(const base::Value::Dict& policy) {
diff --git a/chrome/browser/ash/child_accounts/usage_time_limit_processor_unittest.cc b/chrome/browser/ash/child_accounts/usage_time_limit_processor_unittest.cc
index 1c0681db..87caed82 100644
--- a/chrome/browser/ash/child_accounts/usage_time_limit_processor_unittest.cc
+++ b/chrome/browser/ash/child_accounts/usage_time_limit_processor_unittest.cc
@@ -166,13 +166,12 @@
   base::Value::Dict override_one;
   override_one.Set("action",
                    ValueFromAction(TimeLimitOverride::Action::kUnlock));
-  override_one.Set("created_at_millis", base::Value(created_at_millis));
+  override_one.Set("created_at_millis", created_at_millis);
 
   base::Value::Dict override_two;
   override_two.Set("action", ValueFromAction(TimeLimitOverride::Action::kLock));
-  override_two.Set(
-      "created_at_millis",
-      base::Value(utils::CreatePolicyTimestamp("1 Jan 2018 9:00:00")));
+  override_two.Set("created_at_millis",
+                   utils::CreatePolicyTimestamp("1 Jan 2018 9:00:00"));
 
   base::Value::List overrides;
   overrides.Append(std::move(override_one));
@@ -198,26 +197,24 @@
   std::string created_at_millis =
       utils::CreatePolicyTimestamp("1 Jan 2018 10:00:00");
   base::Value::Dict action_specific_data;
-  action_specific_data.Set("duration_mins", base::Value(30));
+  action_specific_data.Set("duration_mins", 30);
 
   base::Value::Dict override_one;
   override_one.Set("action",
                    ValueFromAction(TimeLimitOverride::Action::kUnlock));
-  override_one.Set("created_at_millis", base::Value(created_at_millis));
+  override_one.Set("created_at_millis", created_at_millis);
   override_one.Set("action_specific_data", std::move(action_specific_data));
 
   base::Value::Dict override_two;
   override_two.Set("action", ValueFromAction(TimeLimitOverride::Action::kLock));
-  override_two.Set(
-      "created_at_millis",
-      base::Value(utils::CreatePolicyTimestamp("1 Jan 2018 9:00:00")));
+  override_two.Set("created_at_millis",
+                   utils::CreatePolicyTimestamp("1 Jan 2018 9:00:00"));
 
   base::Value::Dict override_three;
   override_three.Set("action",
                      ValueFromAction(TimeLimitOverride::Action::kLock));
-  override_three.Set(
-      "created_at_millis",
-      base::Value(utils::CreatePolicyTimestamp("1 Jan 2018 8:00:00")));
+  override_three.Set("created_at_millis",
+                     utils::CreatePolicyTimestamp("1 Jan 2018 8:00:00"));
 
   base::Value::List overrides;
   overrides.Append(std::move(override_one));
@@ -245,20 +242,20 @@
   base::Value::Dict override_one;
   override_one.Set("action",
                    ValueFromAction(TimeLimitOverride::Action::kUnlock));
-  override_one.Set("created_at_millis", base::Value("1000000"));
+  override_one.Set("created_at_millis", "1000000");
 
   base::Value::Dict override_two;
   override_two.Set("action", ValueFromAction(TimeLimitOverride::Action::kLock));
-  override_two.Set("created_at_millis", base::Value("999999"));
+  override_two.Set("created_at_millis", "999999");
 
   base::Value::Dict override_three;
   override_two.Set("action", ValueFromAction(TimeLimitOverride::Action::kLock));
-  override_two.Set("created_at_millis", base::Value("900000"));
+  override_two.Set("created_at_millis", "900000");
 
   base::Value::Dict override_four;
   override_two.Set("action",
                    ValueFromAction(TimeLimitOverride::Action::kUnlock));
-  override_two.Set("created_at_millis", base::Value("1200000"));
+  override_two.Set("created_at_millis", "1200000");
 
   base::Value::List overrides;
   overrides.Append(std::move(override_one));
diff --git a/chrome/browser/ash/extensions/autotest_private/autotest_private_apitest.cc b/chrome/browser/ash/extensions/autotest_private/autotest_private_apitest.cc
index 309967a..ee73e29 100644
--- a/chrome/browser/ash/extensions/autotest_private/autotest_private_apitest.cc
+++ b/chrome/browser/ash/extensions/autotest_private/autotest_private_apitest.cc
@@ -562,7 +562,7 @@
           std::make_unique<app_list::TestResult>(
               ids[i], display_types[i], categories[i], best_match_ranks[i],
               /*relevance=*/scores[i], /*ftrl_result_score=*/scores[i]);
-      test_result->scoring().override_filter_for_test = true;
+      test_result->scoring().override_filter_for_test(true);
       results.emplace_back(std::move(test_result));
     }
     return results;
diff --git a/chrome/browser/ash/file_manager/file_manager_jstest_base.cc b/chrome/browser/ash/file_manager/file_manager_jstest_base.cc
index 8c14534..ca828432 100644
--- a/chrome/browser/ash/file_manager/file_manager_jstest_base.cc
+++ b/chrome/browser/ash/file_manager/file_manager_jstest_base.cc
@@ -44,8 +44,8 @@
     // Loader.
     auto* profile = Profile::FromWebUI(web_ui);
     content::WebUIDataSource* files_swa_source =
-        content::WebUIDataSource::Create(
-            ash::file_manager::kChromeUIFileManagerHost);
+        content::WebUIDataSource::CreateAndAdd(
+            profile, ash::file_manager::kChromeUIFileManagerHost);
 
     files_swa_source->AddResourcePaths(base::make_span(
         kFileManagerSwaResources, kFileManagerSwaResourcesSize));
@@ -61,8 +61,6 @@
     files_swa_source->AddLocalizedStrings(dict_);
     files_swa_source->UseStringsJs();
 
-    content::WebUIDataSource::Add(profile, files_swa_source);
-
     return std::make_unique<content::WebUIController>(web_ui);
   }
 
diff --git a/chrome/browser/ash/system_web_apps/test_support/test_system_web_app_url_data_source.cc b/chrome/browser/ash/system_web_apps/test_support/test_system_web_app_url_data_source.cc
index 6a217f7..a37b103 100644
--- a/chrome/browser/ash/system_web_apps/test_support/test_system_web_app_url_data_source.cc
+++ b/chrome/browser/ash/system_web_apps/test_support/test_system_web_app_url_data_source.cc
@@ -65,7 +65,7 @@
 void AddTestURLDataSource(const std::string& source_name,
                           content::BrowserContext* browser_context) {
   content::WebUIDataSource* data_source =
-      content::WebUIDataSource::Create(source_name);
+      content::WebUIDataSource::CreateAndAdd(browser_context, source_name);
   data_source->DisableTrustedTypesCSP();
   data_source->AddResourcePath("icon-256.png", IDR_PRODUCT_LOGO_256);
   data_source->SetRequestFilter(
@@ -91,7 +91,6 @@
 
             std::move(callback).Run(ref_contents);
           }));
-  content::WebUIDataSource::Add(browser_context, data_source);
 }
 
 }  // namespace ash
diff --git a/chrome/browser/ash/telemetry_extension/probe_service_converters.cc b/chrome/browser/ash/telemetry_extension/probe_service_converters.cc
index 9c75dc7..a7749bd4e 100644
--- a/chrome/browser/ash/telemetry_extension/probe_service_converters.cc
+++ b/chrome/browser/ash/telemetry_extension/probe_service_converters.cc
@@ -6,12 +6,14 @@
 
 #include <unistd.h>
 #include <utility>
+#include <vector>
 
 #include "base/notreached.h"
 #include "base/strings/string_number_conversions.h"
 #include "chromeos/ash/services/cros_healthd/public/mojom/cros_healthd_probe.mojom.h"
 #include "chromeos/crosapi/mojom/nullable_primitives.mojom.h"
 #include "chromeos/crosapi/mojom/probe_service.mojom.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace ash {
 namespace converters {
@@ -49,6 +51,8 @@
       return cros_healthd::mojom::ProbeCategoryEnum::kSystem;
     case crosapi::mojom::ProbeCategoryEnum::kTpm:
       return cros_healthd::mojom::ProbeCategoryEnum::kTpm;
+    case crosapi::mojom::ProbeCategoryEnum::kAudio:
+      return cros_healthd::mojom::ProbeCategoryEnum::kAudio;
   }
   NOTREACHED();
 }
@@ -68,6 +72,45 @@
   return crosapi::mojom::UInt64Value::New(input->value);
 }
 
+crosapi::mojom::ProbeAudioInfoPtr UncheckedConvertPtr(
+    cros_healthd::mojom::AudioInfoPtr input) {
+  absl::optional<std::vector<crosapi::mojom::ProbeAudioOutputNodeInfoPtr>>
+      output_nodes;
+  absl::optional<std::vector<crosapi::mojom::ProbeAudioInputNodeInfoPtr>>
+      input_nodes;
+
+  if (input->output_nodes) {
+    output_nodes = std::vector<crosapi::mojom::ProbeAudioOutputNodeInfoPtr>();
+    for (auto& elem : input->output_nodes.value()) {
+      auto converted_value = ConvertAudioOutputNodePtr(std::move(elem));
+      output_nodes->push_back(std::move(converted_value));
+    }
+  }
+  if (input->input_nodes) {
+    input_nodes = std::vector<crosapi::mojom::ProbeAudioInputNodeInfoPtr>();
+    for (auto& elem : input->input_nodes.value()) {
+      auto converted_value = ConvertAudioInputNodePtr(std::move(elem));
+      input_nodes->push_back(std::move(converted_value));
+    }
+  }
+
+  return crosapi::mojom::ProbeAudioInfo::New(
+      input->output_mute, input->input_mute, input->underruns,
+      input->severe_underruns, std::move(output_nodes), std::move(input_nodes));
+}
+
+crosapi::mojom::ProbeAudioResultPtr UncheckedConvertPtr(
+    cros_healthd::mojom::AudioResultPtr input) {
+  switch (input->which()) {
+    case cros_healthd::mojom::AudioResult::Tag::kAudioInfo:
+      return crosapi::mojom::ProbeAudioResult::NewAudioInfo(
+          ConvertProbePtr(std::move(input->get_audio_info())));
+    case cros_healthd::mojom::AudioResult::Tag::kError:
+      return crosapi::mojom::ProbeAudioResult::NewError(
+          ConvertProbePtr(std::move(input->get_error())));
+  }
+}
+
 crosapi::mojom::ProbeBatteryInfoPtr UncheckedConvertPtr(
     cros_healthd::mojom::BatteryInfoPtr input) {
   return crosapi::mojom::ProbeBatteryInfo::New(
@@ -426,7 +469,8 @@
       ConvertProbePtr(std::move(input->bluetooth_result)),
       std::move(system_result_output.second),
       ConvertProbePtr(std::move(input->network_result)),
-      ConvertProbePtr(std::move(input->tpm_result)));
+      ConvertProbePtr(std::move(input->tpm_result)),
+      ConvertProbePtr(std::move(input->audio_result)));
 }
 
 }  // namespace unchecked
@@ -495,6 +539,40 @@
   return crosapi::mojom::UInt64Value::New(input);
 }
 
+crosapi::mojom::ProbeAudioInputNodeInfoPtr ConvertAudioInputNodePtr(
+    cros_healthd::mojom::AudioNodeInfoPtr input) {
+  if (!input) {
+    return crosapi::mojom::ProbeAudioInputNodeInfoPtr();
+  }
+
+  auto result = crosapi::mojom::ProbeAudioInputNodeInfo::New();
+
+  result->id = input->id;
+  result->name = input->name;
+  result->device_name = input->device_name;
+  result->active = input->active;
+  result->node_gain = input->input_node_gain;
+
+  return result;
+}
+
+crosapi::mojom::ProbeAudioOutputNodeInfoPtr ConvertAudioOutputNodePtr(
+    cros_healthd::mojom::AudioNodeInfoPtr input) {
+  if (!input) {
+    return crosapi::mojom::ProbeAudioOutputNodeInfoPtr();
+  }
+
+  auto result = crosapi::mojom::ProbeAudioOutputNodeInfo::New();
+
+  result->id = input->id;
+  result->name = input->name;
+  result->device_name = input->device_name;
+  result->active = input->active;
+  result->node_volume = input->node_volume;
+
+  return result;
+}
+
 std::vector<cros_healthd::mojom::ProbeCategoryEnum> ConvertCategoryVector(
     const std::vector<crosapi::mojom::ProbeCategoryEnum>& input) {
   std::vector<cros_healthd::mojom::ProbeCategoryEnum> output;
diff --git a/chrome/browser/ash/telemetry_extension/probe_service_converters.h b/chrome/browser/ash/telemetry_extension/probe_service_converters.h
index f21f049a..d3d5792e 100644
--- a/chrome/browser/ash/telemetry_extension/probe_service_converters.h
+++ b/chrome/browser/ash/telemetry_extension/probe_service_converters.h
@@ -31,6 +31,12 @@
 crosapi::mojom::UInt64ValuePtr UncheckedConvertPtr(
     cros_healthd::mojom::NullableUint64Ptr input);
 
+crosapi::mojom::ProbeAudioInfoPtr UncheckedConvertPtr(
+    cros_healthd::mojom::AudioInfoPtr input);
+
+crosapi::mojom::ProbeAudioResultPtr UncheckedConvertPtr(
+    cros_healthd::mojom::AudioResultPtr input);
+
 crosapi::mojom::ProbeBatteryInfoPtr UncheckedConvertPtr(
     cros_healthd::mojom::BatteryInfoPtr input);
 
@@ -156,6 +162,12 @@
 
 crosapi::mojom::UInt64ValuePtr Convert(uint64_t input);
 
+crosapi::mojom::ProbeAudioInputNodeInfoPtr ConvertAudioInputNodePtr(
+    cros_healthd::mojom::AudioNodeInfoPtr input);
+
+crosapi::mojom::ProbeAudioOutputNodeInfoPtr ConvertAudioOutputNodePtr(
+    cros_healthd::mojom::AudioNodeInfoPtr input);
+
 template <class OutputT, class InputT>
 std::vector<OutputT> ConvertPtrVector(std::vector<InputT> input) {
   std::vector<OutputT> output;
diff --git a/chrome/browser/ash/telemetry_extension/probe_service_converters_unittest.cc b/chrome/browser/ash/telemetry_extension/probe_service_converters_unittest.cc
index d853de0..2031182 100644
--- a/chrome/browser/ash/telemetry_extension/probe_service_converters_unittest.cc
+++ b/chrome/browser/ash/telemetry_extension/probe_service_converters_unittest.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/ash/telemetry_extension/probe_service_converters.h"
 
 #include <cstdint>
+#include <utility>
 #include <vector>
 
 #include "chromeos/ash/services/cros_healthd/public/mojom/cros_healthd_probe.mojom.h"
@@ -46,7 +47,8 @@
       crosapi::mojom::ProbeCategoryEnum::kBluetooth,
       crosapi::mojom::ProbeCategoryEnum::kSystem,
       crosapi::mojom::ProbeCategoryEnum::kNetwork,
-      crosapi::mojom::ProbeCategoryEnum::kTpm};
+      crosapi::mojom::ProbeCategoryEnum::kTpm,
+      crosapi::mojom::ProbeCategoryEnum::kAudio};
   EXPECT_THAT(
       ConvertCategoryVector(kInput),
       ElementsAre(
@@ -63,7 +65,8 @@
           cros_healthd::mojom::ProbeCategoryEnum::kBluetooth,
           cros_healthd::mojom::ProbeCategoryEnum::kSystem,
           cros_healthd::mojom::ProbeCategoryEnum::kNetwork,
-          cros_healthd::mojom::ProbeCategoryEnum::kTpm));
+          cros_healthd::mojom::ProbeCategoryEnum::kTpm,
+          cros_healthd::mojom::ProbeCategoryEnum::kAudio));
 }
 
 TEST(ProbeServiceConverters, ErrorType) {
@@ -117,6 +120,134 @@
             crosapi::mojom::UInt64Value::New(kValue));
 }
 
+TEST(ProbeServiceConverters, AudioNodeInputInfoPtr) {
+  constexpr uint64_t kId = 42;
+  constexpr char kName[] = "Internal Mic";
+  constexpr char kDeviceName[] = "HDA Intel PCH: CA0132 Analog:0,0";
+  constexpr bool kActive = true;
+  constexpr uint8_t kInputNodeGain = 1;
+
+  auto input_node = cros_healthd::mojom::AudioNodeInfo::New();
+  input_node->id = kId;
+  input_node->name = kName;
+  input_node->device_name = kDeviceName;
+  input_node->active = kActive;
+  input_node->input_node_gain = kInputNodeGain;
+
+  EXPECT_EQ(ConvertAudioInputNodePtr(std::move(input_node)),
+            crosapi::mojom::ProbeAudioInputNodeInfo::New(
+                kId, kName, kDeviceName, kActive, kInputNodeGain));
+
+  EXPECT_EQ(ConvertAudioInputNodePtr(nullptr),
+            crosapi::mojom::ProbeAudioInputNodeInfoPtr());
+}
+
+TEST(ProbeServiceConverters, AudioNodeOutputInfoPtr) {
+  constexpr uint64_t kId = 42;
+  constexpr char kName[] = "Internal Speaker";
+  constexpr char kDeviceName[] = "HDA Intel PCH: CA0132 Analog:0,0";
+  constexpr bool kActive = true;
+  constexpr uint8_t kNodeVolume = 242;
+
+  auto output_node = cros_healthd::mojom::AudioNodeInfo::New();
+  output_node->id = kId;
+  output_node->name = kName;
+  output_node->device_name = kDeviceName;
+  output_node->active = kActive;
+  output_node->node_volume = kNodeVolume;
+  EXPECT_EQ(ConvertAudioOutputNodePtr(std::move(output_node)),
+            crosapi::mojom::ProbeAudioOutputNodeInfo::New(
+                kId, kName, kDeviceName, kActive, kNodeVolume));
+
+  EXPECT_EQ(ConvertAudioOutputNodePtr(nullptr),
+            crosapi::mojom::ProbeAudioOutputNodeInfoPtr());
+}
+
+TEST(ProbeServiceConverters, AudioInfoPtr) {
+  constexpr bool kOutputMute = true;
+  constexpr bool kInputMute = false;
+  constexpr uint32_t kUnderruns = 56;
+  constexpr uint32_t kSevereUnderruns = 3;
+
+  constexpr uint64_t kIdInput = 42;
+  constexpr char kNameInput[] = "Internal Speaker";
+  constexpr char kDeviceNameInput[] = "HDA Intel PCH: CA0132 Analog:0,0";
+  constexpr bool kActiveInput = true;
+  constexpr uint8_t kInputNodeGainInput = 1;
+
+  constexpr uint64_t kIdOutput = 43;
+  constexpr char kNameOutput[] = "Extenal Speaker";
+  constexpr char kDeviceNameOutput[] = "HDA Intel PCH: CA0132 Analog:1,0";
+  constexpr bool kActiveOutput = false;
+  constexpr uint8_t kNodeVolumeOutput = 212;
+
+  std::vector<cros_healthd::mojom::AudioNodeInfoPtr> input_node_info;
+  auto input_node = cros_healthd::mojom::AudioNodeInfo::New();
+  input_node->id = kIdInput;
+  input_node->name = kNameInput;
+  input_node->device_name = kDeviceNameInput;
+  input_node->active = kActiveInput;
+  input_node->input_node_gain = kInputNodeGainInput;
+  input_node_info.push_back(std::move(input_node));
+
+  std::vector<cros_healthd::mojom::AudioNodeInfoPtr> output_node_info;
+  auto output_node = cros_healthd::mojom::AudioNodeInfo::New();
+  output_node->id = kIdOutput;
+  output_node->name = kNameOutput;
+  output_node->device_name = kDeviceNameOutput;
+  output_node->active = kActiveOutput;
+  output_node->node_volume = kNodeVolumeOutput;
+  output_node_info.push_back(std::move(output_node));
+
+  auto input = cros_healthd::mojom::AudioInfo::New();
+  input->output_mute = kOutputMute;
+  input->input_mute = kInputMute;
+  input->underruns = kUnderruns;
+  input->severe_underruns = kSevereUnderruns;
+  input->output_nodes = std::move(output_node_info);
+  input->input_nodes = std::move(input_node_info);
+
+  std::vector<crosapi::mojom::ProbeAudioInputNodeInfoPtr>
+      expected_input_node_info;
+  auto expected_input = crosapi::mojom::ProbeAudioInputNodeInfo::New();
+  expected_input->id = kIdInput;
+  expected_input->name = kNameInput;
+  expected_input->device_name = kDeviceNameInput;
+  expected_input->active = kActiveInput;
+  expected_input->node_gain = kInputNodeGainInput;
+  expected_input_node_info.push_back(std::move(expected_input));
+
+  std::vector<crosapi::mojom::ProbeAudioOutputNodeInfoPtr>
+      expected_output_node_info;
+  auto expected_output = crosapi::mojom::ProbeAudioOutputNodeInfo::New();
+  expected_output->id = kIdOutput;
+  expected_output->name = kNameOutput;
+  expected_output->device_name = kDeviceNameOutput;
+  expected_output->active = kActiveOutput;
+  expected_output->node_volume = kNodeVolumeOutput;
+  expected_output_node_info.push_back(std::move(expected_output));
+
+  EXPECT_EQ(ConvertProbePtr(std::move(input)),
+            crosapi::mojom::ProbeAudioInfo::New(
+                kOutputMute, kInputMute, kUnderruns, kSevereUnderruns,
+                std::move(expected_output_node_info),
+                std::move(expected_input_node_info)));
+}
+
+TEST(ProbeServiceConverters, AudioResultPtrInfo) {
+  const auto output =
+      ConvertProbePtr(cros_healthd::mojom::AudioResult::NewAudioInfo(nullptr));
+  ASSERT_TRUE(output);
+  EXPECT_TRUE(output->is_audio_info());
+}
+
+TEST(ProbeServiceConverters, AudioResultPtrError) {
+  const auto output =
+      ConvertProbePtr(cros_healthd::mojom::AudioResult::NewError(nullptr));
+  ASSERT_TRUE(output);
+  EXPECT_TRUE(output->is_error());
+}
+
 TEST(ProbeServiceConverters, BatteryInfoPtr) {
   constexpr int64_t kCycleCount = (1LL << 62) + 45;
   constexpr double kVoltageNow = 1000000000000.2;
@@ -1037,6 +1168,8 @@
             chromeos::network_health::mojom::NetworkHealthState::New());
     input->tpm_result = cros_healthd::mojom::TpmResult::NewTpmInfo(
         cros_healthd::mojom::TpmInfo::New());
+    input->audio_result = cros_healthd::mojom::AudioResult::NewAudioInfo(
+        cros_healthd::mojom::AudioInfo::New());
   }
 
   EXPECT_EQ(
@@ -1082,7 +1215,9 @@
           crosapi::mojom::ProbeNetworkResult::NewNetworkHealth(
               chromeos::network_health::mojom::NetworkHealthState::New()),
           crosapi::mojom::ProbeTpmResult::NewTpmInfo(
-              crosapi::mojom::ProbeTpmInfo::New())));
+              crosapi::mojom::ProbeTpmInfo::New()),
+          crosapi::mojom::ProbeAudioResult::NewAudioInfo(
+              crosapi::mojom::ProbeAudioInfo::New())));
 }
 
 TEST(ProbeServiceConverters, TelemetryInfoPtrWithNullFields) {
@@ -1100,7 +1235,8 @@
                 crosapi::mojom::ProbeBluetoothResultPtr(nullptr),
                 crosapi::mojom::ProbeSystemResultPtr(nullptr),
                 crosapi::mojom::ProbeNetworkResultPtr(nullptr),
-                crosapi::mojom::ProbeTpmResultPtr(nullptr)));
+                crosapi::mojom::ProbeTpmResultPtr(nullptr),
+                crosapi::mojom::ProbeAudioResultPtr(nullptr)));
 }
 
 }  // namespace ash::converters
diff --git a/chrome/browser/autofill/automated_tests/cache_replayer.cc b/chrome/browser/autofill/automated_tests/cache_replayer.cc
index 2447dfe..f112615 100644
--- a/chrome/browser/autofill/automated_tests/cache_replayer.cc
+++ b/chrome/browser/autofill/automated_tests/cache_replayer.cc
@@ -41,10 +41,6 @@
 namespace {
 
 constexpr char kHTTPBodySep[] = "\r\n\r\n";
-constexpr char kLegacyServerDomain[] = "clients1.google.com";
-constexpr char kLegacyServerQueryPath[] = "/tbproxy/af/query";
-constexpr char kLegacyServerUrlPrefix[] =
-    "https://clients1.google.com/tbproxy/af/query";
 constexpr char kApiServerDomain[] = "content-autofill.googleapis.com";
 constexpr char kApiServerUrlGetPrefix[] =
     "https://content-autofill.googleapis.com/v1/pages";
@@ -125,25 +121,7 @@
 }
 
 // Gets base64 encoded query parameter from the URL.
-template <typename Env>
-ErrorOr<std::string> GetQueryParameter(const GURL& url);
-
-template <>
-ErrorOr<std::string> GetQueryParameter<LegacyEnv>(const GURL& url) {
-  std::string q_value;
-  if (!net::GetValueForKeyInQuery(url, "q", &q_value)) {
-    // This situation will never happen if check for the presence of "q=" is
-    // done before calling this function.
-    return base::unexpected(
-        base::StrCat({"could not get any value from \"q\" query parameter in "
-                      "Query GET URL: ",
-                      url.spec()}));
-  }
-  return base::ok(std::move(q_value));
-}
-
-template <>
-ErrorOr<std::string> GetQueryParameter<ApiEnv>(const GURL& url) {
+ErrorOr<std::string> GetQueryParameter(const GURL& url) {
   std::string value = url.path();
   if (value.find(kApiServerQueryPath) != 0) {
     // This situation will never happen if check for the query path is
@@ -165,24 +143,7 @@
 }
 
 // Returns whether the |url| points to a GET or POST query, or neither.
-template <typename Env>
-RequestType GetRequestTypeFromURL(const GURL& url);
-
-template <>
-RequestType GetRequestTypeFromURL<LegacyEnv>(const GURL& url) {
-  if (url.host() != kLegacyServerDomain ||
-      url.path().find(kLegacyServerQueryPath) != 0) {
-    return RequestType::kNone;
-  }
-
-  if (url.spec().find("q=") != std::string::npos) {
-    return RequestType::kQueryProtoGET;
-  }
-  return RequestType::kQueryProtoPOST;
-}
-
-template <>
-RequestType GetRequestTypeFromURL<ApiEnv>(const GURL& url) {
+RequestType GetRequestTypeFromURL(const GURL& url) {
   if (url.host() != kApiServerDomain ||
       url.path().find(kApiServerQueryPath) != 0) {
     return RequestType::kNone;
@@ -194,9 +155,9 @@
 }
 
 // Gets query request protos from GET URL.
-template <typename Env>
-ErrorOr<typename Env::Query> GetAutofillQueryFromGETQueryURL(const GURL& url) {
-  ErrorOr<std::string> query_parameter = GetQueryParameter<Env>(url);
+ErrorOr<AutofillPageQueryRequest> GetAutofillQueryFromGETQueryURL(
+    const GURL& url) {
+  ErrorOr<std::string> query_parameter = GetQueryParameter(url);
   if (!query_parameter.has_value())
     return base::unexpected(query_parameter.error());
 
@@ -209,7 +170,7 @@
         {"could not base64-decode value of path in Query GET URL: \"",
          query_parameter->c_str(), "\""}));
   }
-  return ParseProtoContents<typename Env::Query>(decoded_query);
+  return ParseProtoContents<AutofillPageQueryRequest>(decoded_query);
 }
 
 // Puts all data elements within the request or response body together in a
@@ -259,11 +220,7 @@
   return ParseProtoContents<AutofillPageQueryRequest>(query.value());
 }
 
-bool IsSingleFormRequest(const LegacyEnv::Query& query) {
-  return query.form_size() == 1;
-}
-
-bool IsSingleFormRequest(const ApiEnv::Query& query) {
+bool IsSingleFormRequest(const AutofillPageQueryRequest& query) {
   return query.forms_size() == 1;
 }
 
@@ -294,8 +251,7 @@
 }
 
 // Gets AutofillQueryContents from WPR recorded HTTP request body for POST.
-template <typename Env>
-ErrorOr<typename Env::Query> GetAutofillQueryFromRequestNode(
+ErrorOr<AutofillPageQueryRequest> GetAutofillQueryFromRequestNode(
     const base::Value& request_node) {
   std::string decoded_request_text;
   if (!RetrieveValueFromRequestNode(request_node, "SerializedRequest",
@@ -304,20 +260,18 @@
         "Unable to retrieve serialized request from WPR request_node");
   }
   std::string http_text = SplitHTTP(decoded_request_text).second;
-  if (std::is_same<Env, ApiEnv>::value) {
-    ErrorOr<std::string> query =
-        PeelAutofillPageResourceQueryRequestWrapper(http_text);
-    if (!query.has_value())
-      return base::unexpected(query.error());
-    http_text = query.value();
+  ErrorOr<std::string> query =
+      PeelAutofillPageResourceQueryRequestWrapper(http_text);
+  if (!query.has_value()) {
+    return base::unexpected(query.error());
   }
-  return ParseProtoContents<typename Env::Query>(http_text);
+  http_text = query.value();
+  return ParseProtoContents<AutofillPageQueryRequest>(http_text);
 }
 
 // Gets AutofillQueryResponseContents from WPR recorded HTTP response body.
 // Also populates and returns the split |response_header_text|.
-template <typename Env>
-ErrorOr<typename Env::Response> GetAutofillResponseFromRequestNode(
+ErrorOr<AutofillQueryResponse> GetAutofillResponseFromRequestNode(
     const base::Value& request_node,
     std::string* response_header_text) {
   std::string compressed_response_text;
@@ -337,15 +291,14 @@
   // Eventual response needs header information, so lift that as well.
   *response_header_text = http_pair.first;
 
-  if (std::is_same<Env, ApiEnv>::value) {
-    // The Api Environment expects the response to be base64 encoded.
-    std::string tmp;
-    if (!base::Base64Decode(decompressed_body, &tmp))
-      return base::unexpected("Unable to base64 decode the body");
-    decompressed_body = tmp;
+  // The Api Environment expects the response to be base64 encoded.
+  std::string tmp;
+  if (!base::Base64Decode(decompressed_body, &tmp)) {
+    return base::unexpected("Unable to base64 decode the body");
   }
+  decompressed_body = tmp;
 
-  return ParseProtoContents<typename Env::Response>(decompressed_body);
+  return ParseProtoContents<AutofillQueryResponse>(decompressed_body);
 }
 
 // Fills |cache_to_fill| with the keys from a single |query_request| and
@@ -357,7 +310,7 @@
                         const std::string& response_header_text,
                         const AutofillQueryResponse& query_response,
                         ServerCache* cache_to_fill) {
-  VLOG(2) << "Full Request Key is:" << GetKeyFromQuery<ApiEnv>(query_request);
+  VLOG(2) << "Full Request Key is:" << GetKeyFromQuery(query_request);
   VLOG(2) << "Matching keys from Query request proto:\n" << query_request;
   VLOG(2) << "To field types from Query response proto:\n" << query_response;
   if (query_request.forms_size() != query_response.form_suggestions_size()) {
@@ -405,72 +358,6 @@
   return true;
 }
 
-template <typename ReadEnv>
-ErrorOr<std::string> ReencodeResponseMessage(
-    const std::string& http_response,
-    const typename ReadEnv::Query& query) {
-  auto response_pair = SplitHTTP(http_response);
-  // Decompress the body.
-  std::string decompressed_body;
-  if (!compression::GzipUncompress(response_pair.second, &decompressed_body)) {
-    return base::unexpected(
-        base::StrCat({"Could not gzip decompress HTTP response: ",
-                      GetHexString(response_pair.second)}));
-  }
-
-  if (std::is_same<ReadEnv, ApiEnv>::value) {
-    // The Api Environment expects the response body to be base64 encoded.
-    std::string tmp;
-    if (!base::Base64Decode(decompressed_body, &tmp)) {
-      return base::unexpected(
-          base::StrCat({"Could not base64 decode HTTP response body: ",
-                        GetHexString(response_pair.second)}));
-    }
-    decompressed_body = tmp;
-  }
-
-  // Parse the body.
-  ErrorOr<typename ReadEnv::Response> response =
-      ParseProtoContents<typename ReadEnv::Response>(decompressed_body);
-  if (!response.has_value()) {
-    VLOG(1) << "Failed to parse response body";
-    return base::unexpected(response.error());
-  }
-
-  // Convert the response protobuf
-  AutofillQueryResponse out_response =
-      ConvertResponse<ReadEnv>(response.value(), query);
-
-  // Compress that response to a string and gzip it.
-  std::string serialized_response;
-  if (!out_response.SerializeToString(&serialized_response)) {
-    VLOG(1) << "Unable to serialize the new response!";
-    return base::unexpected("Unable to serialize the new response.");
-  }
-
-  // The Api Environment expects the response body to be base64 encoded.
-  std::string tmp;
-  base::Base64Encode(serialized_response, &tmp);
-  serialized_response = tmp;
-
-  std::string out_compressed_response_body;
-  if (!compression::GzipCompress(serialized_response,
-                                 &out_compressed_response_body)) {
-    VLOG(1) << "Unable to compress the new response!";
-    return base::unexpected("Unable to compress the new response.");
-  }
-
-  return base::ok(
-      MakeHTTPTextFromSplit(response_pair.first, out_compressed_response_body));
-}
-
-template <>
-ErrorOr<std::string> ReencodeResponseMessage<ApiEnv>(
-    const std::string& compressed_response_text,
-    const typename ApiEnv::Query& query) {
-  return base::ok(compressed_response_text);
-}
-
 // Populates |cache_to_fill| with content from |query_node| that contains a
 // list of single request node that share the same URL field (e.g.,
 // https://clients1.google.com/tbproxy/af/query) in the WPR capture json cache.
@@ -483,7 +370,6 @@
 // is false, fill cache with individual form keys (and expect
 // ServerCacheReplayer to be able to split incoming request by key and stitch
 // results together).
-template <typename ReadEnv>
 ServerCacheReplayer::Status PopulateCacheFromQueryNode(
     const QueryNode& query_node,
     int options,
@@ -492,19 +378,17 @@
   bool split_requests_by_form = SplitRequestsByForm(options);
   for (const base::Value& request : query_node.node->GetList()) {
     // Get AutofillQueryContents from request.
-    bool is_post_request = GetRequestTypeFromURL<ReadEnv>(query_node.url) ==
-                           RequestType::kQueryProtoPOST;
-    ErrorOr<typename ReadEnv::Query> query_request_statusor =
-        is_post_request
-            ? GetAutofillQueryFromRequestNode<ReadEnv>(request)
-            : GetAutofillQueryFromGETQueryURL<ReadEnv>(GURL(query_node.url));
+    bool is_post_request =
+        GetRequestTypeFromURL(query_node.url) == RequestType::kQueryProtoPOST;
+    ErrorOr<AutofillPageQueryRequest> query_request_statusor =
+        is_post_request ? GetAutofillQueryFromRequestNode(request)
+                        : GetAutofillQueryFromGETQueryURL(GURL(query_node.url));
     // Only proceed if successfully parse the query request proto, else drop to
     // failure space.
     if (query_request_statusor.has_value()) {
       VLOG(2) << "Getting key from Query request proto:\n "
               << query_request_statusor.value();
-      std::string key =
-          GetKeyFromQuery<ReadEnv>(query_request_statusor.value());
+      std::string key = GetKeyFromQuery(query_request_statusor.value());
       bool is_single_form_request =
           IsSingleFormRequest(query_request_statusor.value());
       // Switch to store forms as individuals or only in the groupings that they
@@ -514,39 +398,25 @@
         std::string compressed_response_text;
         if (RetrieveValueFromRequestNode(request, "SerializedResponse",
                                          &compressed_response_text)) {
-          ErrorOr<std::string> reencoded_compressed_response_text =
-              ReencodeResponseMessage<ReadEnv>(compressed_response_text,
-                                               query_request_statusor.value());
-          if (!reencoded_compressed_response_text.has_value()) {
-            VLOG(1) << "Unable to reencode response for key: " << key << ": "
-                    << reencoded_compressed_response_text.error();
-            continue;
-          }
-
-          (*cache_to_fill)[key] = reencoded_compressed_response_text.value();
+          (*cache_to_fill)[key] = compressed_response_text;
           VLOG(1) << "Cached response content for key: " << key;
           continue;
         }
       } else {
         // Get AutofillQueryResponseContents and response header text.
         std::string response_header_text;
-        ErrorOr<typename ReadEnv::Response> query_response_statusor =
-            GetAutofillResponseFromRequestNode<ReadEnv>(request,
-                                                        &response_header_text);
+        ErrorOr<AutofillQueryResponse> query_response_statusor =
+            GetAutofillResponseFromRequestNode(request, &response_header_text);
         if (!query_response_statusor.has_value()) {
-          VLOG(1) << "Unable to get AutofillQueryResponseContents from WPR node"
+          VLOG(1) << "Unable to get AutofillQueryResponse from WPR node"
                   << "SerializedResponse for request:" << key;
           continue;
         }
-        ErrorOr<AutofillPageQueryRequest> write_query =
-            ConvertQuery<ReadEnv>(query_request_statusor.value());
-        ErrorOr<AutofillQueryResponse> write_response =
-            ConvertResponse<ReadEnv>(query_response_statusor.value(),
-                                     query_request_statusor.value());
         // We have a proper request and a proper response, we can populate for
         // each form in the AutofillQueryContents.
-        if (FillFormSplitCache(write_query.value(), response_header_text,
-                               write_response.value(), cache_to_fill)) {
+        if (FillFormSplitCache(
+                query_request_statusor.value(), response_header_text,
+                query_response_statusor.value(), cache_to_fill)) {
           continue;
         }
       }
@@ -624,41 +494,6 @@
 
   {
     const base::Value* domain_node =
-        root_node.FindPath({"Requests", kLegacyServerDomain});
-    std::vector<QueryNode> query_nodes =
-        domain_node
-            ? FindQueryNodesInDomainDict(*domain_node, kLegacyServerUrlPrefix)
-            : std::vector<QueryNode>();
-
-    // Fill cache with the content of each Query node. There are 3 possible
-    // situations: (1) there is a single Query node that contains POST requests
-    // that share the same URL, (2) there is one Query node per GET request
-    // where each Query node only contains one request, and (3) a mix of (1) and
-    // (2). Exit early with false whenever there is an error parsing a node.
-    for (auto query_node : query_nodes) {
-      if (!CheckNodeType(query_node.node,
-                         "Requests->clients1.google.com->clients1.google."
-                         "com/tbproxy/af/query*",
-                         base::Value::Type::LIST)) {
-        return ServerCacheReplayer::Status{
-            ServerCacheReplayer::StatusCode::kBadNode,
-            base::StrCat({"could not read node content for node with URL ",
-                          query_node.url.spec()})};
-      }
-
-      // Populate cache from Query node content.
-      // The template parameters specify the reading and writing format.
-      auto status = PopulateCacheFromQueryNode<LegacyEnv>(query_node, options,
-                                                          cache_to_fill);
-      if (!status.Ok())
-        return status;
-      VLOG(1) << "Filled cache with " << cache_to_fill->size()
-              << " requests for Query node with URL: " << query_node.url;
-    }
-  }
-
-  {
-    const base::Value* domain_node =
         root_node.FindPath({"Requests", kApiServerDomain});
     std::vector<QueryNode> query_nodes =
         domain_node
@@ -683,8 +518,8 @@
 
       // Populate cache from Query node content.
       // The template parameters specify the reading and writing format.
-      auto status = PopulateCacheFromQueryNode<ApiEnv>(query_node, options,
-                                                       cache_to_fill);
+      auto status =
+          PopulateCacheFromQueryNode(query_node, options, cache_to_fill);
       if (!status.Ok())
         return status;
       VLOG(1) << "Filled cache with " << cache_to_fill->size()
@@ -706,103 +541,6 @@
 
 }  // namespace
 
-// Convert query protobuf from one environment to ApiEnv.
-template <typename ReadEnv>
-AutofillPageQueryRequest ConvertQuery(const typename ReadEnv::Query& in);
-
-template <>
-AutofillPageQueryRequest ConvertQuery<ApiEnv>(
-    const typename ApiEnv::Query& in) {
-  VLOG(1) << "ConvertQuery: identity";
-  // No conversion necessary.
-  return in;
-}
-
-template <>
-AutofillPageQueryRequest ConvertQuery<LegacyEnv>(
-    const typename LegacyEnv::Query& in) {
-  VLOG(1) << "ConvertQuery: legacy->api";
-  AutofillPageQueryRequest out;
-  for (const auto& in_form : in.form()) {
-    auto* out_form = out.add_forms();
-    out_form->set_signature(in_form.signature());
-    if (in_form.has_form_metadata())
-      out_form->mutable_metadata()->CopyFrom(in_form.form_metadata());
-    for (const auto& in_field : in_form.field()) {
-      auto* out_field = out_form->add_fields();
-      out_field->set_signature(in_field.signature());
-      if (in_field.has_name())
-        out_field->set_name(in_field.name());
-      if (in_field.has_type())
-        out_field->set_control_type(in_field.type());
-      if (in_field.has_field_metadata())
-        out_field->mutable_metadata()->CopyFrom(in_field.field_metadata());
-    }
-  }
-  if (in.experiments_size() > 0)
-    out.mutable_experiments()->CopyFrom(in.experiments());
-  return out;
-}
-
-// Convert response protobuf from one environment to ApiEnv.
-// The |query| is passed as a helper because the legacy response does not
-// contain enough information to create an api response.
-template <typename ReadEnv>
-AutofillQueryResponse ConvertResponse(const typename ReadEnv::Response& in,
-                                      const typename ReadEnv::Query& query);
-
-template <>
-AutofillQueryResponse ConvertResponse<ApiEnv>(
-    const typename ApiEnv::Response& in,
-    const typename ApiEnv::Query& query) {
-  // No conversion necessary.
-  VLOG(1) << "ConvertResponse: identity";
-  return in;
-}
-
-template <>
-AutofillQueryResponse ConvertResponse<LegacyEnv>(
-    const typename LegacyEnv::Response& in,
-    const typename LegacyEnv::Query& query) {
-  VLOG(1) << "ConvertResponse: legacy->api";
-  AutofillQueryResponse out;
-  // The Legacy response does not carry enough information to create an api
-  // server response. Therefore, we wal the legacy query.
-  int in_field_index = 0;
-  for (const auto& query_form : query.form()) {
-    auto* out_form = out.add_form_suggestions();
-    for (const auto& query_field : query_form.field()) {
-      const auto& in_field = in.field(in_field_index);
-      auto* out_field = out_form->add_field_suggestions();
-      out_field->set_field_signature(query_field.signature());
-      int starting_index = 0;
-      // LegacyEnv Response is inconsistent on the overall type being in the
-      // predictions list, so first add the overall_type, and then address any
-      // additional predictions.
-      if (in_field.has_overall_type_prediction()) {
-        out_field->add_predictions()->set_type(
-            in_field.overall_type_prediction());
-        starting_index = 1;
-      }
-      for (int i = starting_index; i < in_field.predictions_size(); i++) {
-        out_field->add_predictions()->set_type(
-            in_field.predictions()[i].type());
-      }
-      if (in_field.predictions().size() > 0 &&
-          in_field.predictions(0).has_may_use_prefilled_placeholder()) {
-        out_field->set_may_use_prefilled_placeholder(
-            in_field.predictions(0).may_use_prefilled_placeholder());
-      }
-      if (in_field.has_password_requirements()) {
-        out_field->mutable_password_requirements()->CopyFrom(
-            in_field.password_requirements());
-      }
-      ++in_field_index;
-    }
-  }
-  return out;
-}
-
 // Decompressed HTTP response read from WPR capture file. Will set
 // |decompressed_http| to "" and return false if there is an error.
 bool RetrieveAndDecompressStoredHTTP(const ServerCache& cache,
@@ -880,22 +618,6 @@
 // Streams in text format. For consistency, taken from anonymous namespace in
 // components/autofill/core/browser/autofill_download_manager.cc
 std::ostream& operator<<(std::ostream& out,
-                         const autofill::AutofillQueryContents& query) {
-  out << "client_version: " << query.client_version();
-  for (const auto& form : query.form()) {
-    out << "\nForm\n signature: " << form.signature();
-    for (const auto& field : form.field()) {
-      out << "\n Field\n  signature: " << field.signature();
-      if (!field.name().empty())
-        out << "\n  name: " << field.name();
-      if (!field.type().empty())
-        out << "\n  type: " << field.type();
-    }
-  }
-  return out;
-}
-
-std::ostream& operator<<(std::ostream& out,
                          const autofill::AutofillPageQueryRequest& query) {
   for (const auto& form : query.forms()) {
     out << "\nForm\n signature: " << form.signature();
@@ -912,16 +634,6 @@
 
 // Streams in text format. For consistency, taken from anonymous namespace in
 // components/autofill/core/browser/form_structure.cc
-std::ostream& operator<<(
-    std::ostream& out,
-    const autofill::AutofillQueryResponseContents& response) {
-  for (const auto& field : response.field()) {
-    out << "\nautofill_type: " << field.overall_type_prediction();
-    for (const auto& prediction : field.predictions())
-      out << "\n  prediction: " << prediction.type();
-  }
-  return out;
-}
 std::ostream& operator<<(std::ostream& out,
                          const autofill::AutofillQueryResponse& response) {
   for (const auto& form : response.form_suggestions()) {
@@ -936,22 +648,7 @@
 }
 
 // Gets a key for cache lookup from a query request.
-template <typename Env>
-std::string GetKeyFromQuery(const typename Env::Query& query_request);
-
-template <>
-std::string GetKeyFromQuery<LegacyEnv>(const LegacyEnv::Query& query_request) {
-  std::vector<std::string> form_ids;
-  for (const auto& form : query_request.form()) {
-    form_ids.push_back(base::NumberToString(form.signature()));
-  }
-  std::sort(form_ids.begin(), form_ids.end());
-  return base::JoinString(form_ids, "_");
-}
-
-// Gets a key for cache lookup from a query request.
-template <>
-std::string GetKeyFromQuery<ApiEnv>(const ApiEnv::Query& query_request) {
+std::string GetKeyFromQuery(const AutofillPageQueryRequest& query_request) {
   std::vector<std::string> form_ids;
   for (const auto& form : query_request.forms()) {
     form_ids.push_back(base::NumberToString(form.signature()));
@@ -1013,7 +710,7 @@
   }
   const ServerCache& cache = cache_replayer.cache();
   bool split_requests_by_form = cache_replayer.split_requests_by_form();
-  std::string combined_key = GetKeyFromQuery<ApiEnv>(query);
+  std::string combined_key = GetKeyFromQuery(query);
 
   if (base::Contains(cache, combined_key)) {
     VLOG(1) << "Retrieving response for " << combined_key;
@@ -1142,8 +839,7 @@
     const ServerCacheReplayer& cache_replayer,
     content::URLLoaderInterceptor::RequestParams* params) {
   const network::ResourceRequest& resource_request = params->url_request;
-  RequestType request_type =
-      GetRequestTypeFromURL<ApiEnv>(resource_request.url);
+  RequestType request_type = GetRequestTypeFromURL(resource_request.url);
   CHECK_NE(request_type, RequestType::kNone);
 
   // Intercept autofill query and serve back response from cache.
@@ -1159,9 +855,8 @@
   }
 
   ErrorOr<AutofillPageQueryRequest> query_request_statusor =
-      is_post_request
-          ? GetAutofillQueryFromPOSTQuery(resource_request)
-          : GetAutofillQueryFromGETQueryURL<ApiEnv>(resource_request.url);
+      is_post_request ? GetAutofillQueryFromPOSTQuery(resource_request)
+                      : GetAutofillQueryFromGETQueryURL(resource_request.url);
   // Using CHECK is fine here since ServerCacheReplayer will only be used for
   // testing and we prefer the test to crash rather than missing the cache
   // because the request content could not be parsed back to a Query request
diff --git a/chrome/browser/autofill/automated_tests/cache_replayer.h b/chrome/browser/autofill/automated_tests/cache_replayer.h
index d9d0cca..5ed80d0 100644
--- a/chrome/browser/autofill/automated_tests/cache_replayer.h
+++ b/chrome/browser/autofill/automated_tests/cache_replayer.h
@@ -11,7 +11,6 @@
 
 #include "base/files/file_path.h"
 #include "components/autofill/core/browser/proto/api_v1.pb.h"
-#include "components/autofill/core/browser/proto/server.pb.h"
 #include "content/public/test/url_loader_interceptor.h"
 
 namespace autofill {
@@ -44,52 +43,13 @@
   kNone,
 };
 
-class LegacyEnv {
- public:
-  using Query = AutofillQueryContents;
-  using Response = AutofillQueryResponseContents;
-};
-
-class ApiEnv {
- public:
-  using Query = AutofillPageQueryRequest;
-  using Response = AutofillQueryResponse;
-};
-
 // Gets a key from a given query request.
-template <typename Env>
-std::string GetKeyFromQuery(const typename Env::Query& query_request);
-template <>
-std::string GetKeyFromQuery<LegacyEnv>(const LegacyEnv::Query& query_request);
-template <>
-std::string GetKeyFromQuery<ApiEnv>(const ApiEnv::Query& query_request);
+std::string GetKeyFromQuery(const AutofillPageQueryRequest& query_request);
 
 bool GetResponseForQuery(const ServerCache& cache,
                          const AutofillPageQueryRequest& query,
                          std::string* http_text);
 
-// Conversion between different versions of queries.
-template <typename ReadEnv>
-AutofillPageQueryRequest ConvertQuery(const typename ReadEnv::Query& in);
-template <>
-AutofillPageQueryRequest ConvertQuery<LegacyEnv>(
-    const typename LegacyEnv::Query& in);
-template <>
-AutofillPageQueryRequest ConvertQuery<ApiEnv>(const typename ApiEnv::Query& in);
-
-// Conversion between different versions of responses.
-template <typename ReadEnv>
-AutofillQueryResponse ConvertResponse(const typename ReadEnv::Response& in,
-                                      const typename ReadEnv::Query& query);
-template <>
-AutofillQueryResponse ConvertResponse<LegacyEnv>(
-    const typename LegacyEnv::Response& in,
-    const typename LegacyEnv::Query& query);
-template <>
-AutofillQueryResponse ConvertResponse<ApiEnv>(
-    const typename ApiEnv::Response& in,
-    const typename ApiEnv::Query& query);
-
 // Switch `--autofill-server-type` is used to override the default behavior of
 // using the cached responses from the wpr archive. The valid values match the
 // enum AutofillServerBehaviorType below. Options are:
diff --git a/chrome/browser/autofill/automated_tests/cache_replayer_unittest.cc b/chrome/browser/autofill/automated_tests/cache_replayer_unittest.cc
index aab101c..f151fd6 100644
--- a/chrome/browser/autofill/automated_tests/cache_replayer_unittest.cc
+++ b/chrome/browser/autofill/automated_tests/cache_replayer_unittest.cc
@@ -19,7 +19,6 @@
 #include "base/values.h"
 #include "build/build_config.h"
 #include "components/autofill/core/browser/proto/api_v1.pb.h"
-#include "components/autofill/core/browser/proto/server.pb.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/zlib/google/compression_utils.h"
 
@@ -389,122 +388,6 @@
   return u_serialized == v_serialized;
 }
 
-TEST(AutofillCacheReplayerTest, ProtobufConversion) {
-  AutofillRandomizedFormMetadata form_metadata;
-  form_metadata.mutable_id()->set_encoded_bits("foobar");
-
-  AutofillRandomizedFieldMetadata field_metadata;
-  field_metadata.mutable_id()->set_encoded_bits("foobarbaz");
-
-  // Form 1 (fields 101, 102), Form 2 (fields 201).
-  LegacyEnv::Query legacy_query;
-  {
-    legacy_query.set_client_version("DummyClient");
-    auto* form1 = legacy_query.add_form();
-    form1->set_signature(1);
-    form1->mutable_form_metadata()->CopyFrom(form_metadata);
-    auto* field101 = form1->add_field();
-    field101->set_signature(101);
-    field101->set_name("field_101");
-    field101->set_type("text");
-    field101->mutable_field_metadata()->CopyFrom(field_metadata);
-    auto* field102 = form1->add_field();
-    field102->set_signature(102);
-    field102->set_name("field_102");
-    field102->set_type("text");
-
-    auto* form2 = legacy_query.add_form();
-    form2->set_signature(2);
-    auto* field201 = form2->add_field();
-    field201->set_signature(201);
-    field201->set_name("field_201");
-    field201->set_type("text");
-
-    legacy_query.add_experiments(50);
-    legacy_query.add_experiments(51);
-  }
-
-  ApiEnv::Query api_query;
-  {
-    auto* form1 = api_query.add_forms();
-    form1->set_signature(1);
-    form1->mutable_metadata()->CopyFrom(form_metadata);
-    auto* field101 = form1->add_fields();
-    field101->set_signature(101);
-    field101->set_name("field_101");
-    field101->set_control_type("text");
-    field101->mutable_metadata()->CopyFrom(field_metadata);
-    auto* field102 = form1->add_fields();
-    field102->set_signature(102);
-    field102->set_name("field_102");
-    field102->set_control_type("text");
-
-    auto* form2 = api_query.add_forms();
-    form2->set_signature(2);
-    auto* field201 = form2->add_fields();
-    field201->set_signature(201);
-    field201->set_name("field_201");
-    field201->set_control_type("text");
-
-    api_query.add_experiments(50);
-    api_query.add_experiments(51);
-  }
-
-  LegacyEnv::Response legacy_response;
-  {
-    auto* field101 = legacy_response.add_field();
-    field101->set_overall_type_prediction(101);
-    auto* field101_prediction = field101->add_predictions();
-    field101_prediction->set_type(101);
-    field101_prediction->set_may_use_prefilled_placeholder(true);
-    field101_prediction = field101->add_predictions();
-    field101_prediction->set_type(1010);
-    field101_prediction->set_may_use_prefilled_placeholder(true);
-    // Todo: Password requirements
-    auto* field102 = legacy_response.add_field();
-    field102->set_overall_type_prediction(102);
-    auto* field102_prediction = field102->add_predictions();
-    field102_prediction->set_type(102);
-    field102_prediction->set_may_use_prefilled_placeholder(false);
-
-    auto* field201 = legacy_response.add_field();
-    field201->set_overall_type_prediction(201);
-    field201->add_predictions()->set_type(201);
-  }
-
-  ApiEnv::Response api_response;
-  {
-    auto* form1 = api_response.add_form_suggestions();
-    auto* field101 = form1->add_field_suggestions();
-    field101->set_field_signature(101);
-    field101->add_predictions()->set_type(101);
-    field101->add_predictions()->set_type(1010);
-    field101->set_may_use_prefilled_placeholder(true);
-    // Todo: Password requirements
-    auto* field102 = form1->add_field_suggestions();
-    field102->set_field_signature(102);
-    field102->add_predictions()->set_type(102);
-    field102->set_may_use_prefilled_placeholder(false);
-
-    auto* form2 = api_response.add_form_suggestions();
-    auto* field201 = form2->add_field_suggestions();
-    field201->set_field_signature(201);
-    field201->add_predictions()->set_type(201);
-  }
-
-  // Verify equivalence of converted queries.
-  EXPECT_TRUE(ProtobufsEqual(api_query, api_query));
-  EXPECT_TRUE(ProtobufsEqual(api_query, ConvertQuery<ApiEnv>(api_query)));
-  EXPECT_TRUE(ProtobufsEqual(api_query, ConvertQuery<LegacyEnv>(legacy_query)));
-
-  // Verify equivalence of converted responses.
-  EXPECT_TRUE(ProtobufsEqual(api_response, api_response));
-  EXPECT_TRUE(ProtobufsEqual(api_response,
-                             ConvertResponse<ApiEnv>(api_response, api_query)));
-  EXPECT_TRUE(ProtobufsEqual(
-      api_response, ConvertResponse<LegacyEnv>(legacy_response, legacy_query)));
-}
-
 // Test suite for Query response retrieval test.
 class AutofillCacheReplayerGetResponseForQueryTest
     : public testing::TestWithParam<RequestType> {};
@@ -600,7 +483,7 @@
   form_to_add.fields = {LightField{1234, 1}};
   const AutofillPageQueryRequest query_request_for_key =
       MakeQueryRequestResponsePair({form_to_add}).first;
-  const std::string key = GetKeyFromQuery<ApiEnv>(query_request_for_key);
+  const std::string key = GetKeyFromQuery(query_request_for_key);
 
   const char invalid_http[] = "Dumb Nonsense That Doesn't Have a HTTP Header";
   ServerCacheReplayer cache_replayer(ServerCache{{key, invalid_http}});
@@ -619,7 +502,7 @@
   form_to_add.fields = {LightField{1234, 1}};
   const AutofillPageQueryRequest query_request_for_key =
       MakeQueryRequestResponsePair({form_to_add}).first;
-  const std::string key = GetKeyFromQuery<ApiEnv>(query_request_for_key);
+  const std::string key = GetKeyFromQuery(query_request_for_key);
 
   const char http_without_body[] = "Test HTTP Header\r\n\r\n";
   ServerCacheReplayer cache_replayer(ServerCache{{key, http_without_body}});
diff --git a/chrome/browser/browser_resources.grd b/chrome/browser/browser_resources.grd
index 8f9da8b7..e0d2ae14 100644
--- a/chrome/browser/browser_resources.grd
+++ b/chrome/browser/browser_resources.grd
@@ -263,7 +263,7 @@
             <include name="IDR_HANGUL_MANIFEST" file="resources\chromeos\input_method\hangul_manifest.json" type="BINDATA" />
           </else>
         </if>
-        <include name="IDR_BRAILLE_MANIFEST" file="resources\chromeos\accessibility\braille_ime\manifest.json" type="BINDATA" />
+        <include name="IDR_BRAILLE_MANIFEST" file="resources\chromeos\accessibility\braille_ime_manifest.json" type="BINDATA" />
       </if>
       <if expr="not is_android">
         <include name="IDR_MEDIA_ROUTER_INTERNALS_HTML" file="resources\media_router\internals\media_router_internals.html" type="BINDATA" />
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 8114b31..f685b07 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -601,6 +601,11 @@
     "expiry_milestone": 112
   },
   {
+    "name": "autofill-suggest-server-card-instead-of-local-card",
+    "owners": [ "koulvipul@google.com", "payments-autofill-team@google.com"],
+    "expiry_milestone": 120
+  },
+  {
     "name": "autofill-upstream-allow-additional-email-domains",
     "owners": [ "jsaul@google.com", "payments-autofill-team@google.com" ],
     "expiry_milestone": 112
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 660bf52..6d3e37a 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -546,6 +546,13 @@
     "When enabled, users would get address/credit cards/passwords autofilling "
     "options in the context menu if the context menu is opened on a text field";
 
+const char kAutofillSuggestServerCardInsteadOfLocalCardName[] =
+    "Suggest Server card instead of Local card for deduped cards";
+const char kAutofillSuggestServerCardInsteadOfLocalCardDescription[] =
+    "When enabled, Autofill suggestions that consist of a local and server "
+    "version of the same card will attempt to fill the server card upon "
+    "selection instead of the local card.";
+
 const char kAutofillTouchToFillForCreditCardsAndroidName[] =
     "Enable Touch To Fill bottomsheet for Autofill credit card suggestions";
 const char kAutofillTouchToFillForCreditCardsAndroidDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index e1c00f3..c9527c5 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -294,6 +294,9 @@
 extern const char kAutofillShowManualFallbackInContextMenuName[];
 extern const char kAutofillShowManualFallbackInContextMenuDescription[];
 
+extern const char kAutofillSuggestServerCardInsteadOfLocalCardName[];
+extern const char kAutofillSuggestServerCardInsteadOfLocalCardDescription[];
+
 extern const char kAutofillTouchToFillForCreditCardsAndroidName[];
 extern const char kAutofillTouchToFillForCreditCardsAndroidDescription[];
 
diff --git a/chrome/browser/history_clusters/history_clusters_internals_browsertest.cc b/chrome/browser/history_clusters/history_clusters_internals_browsertest.cc
index 020849f9..e9b202bb7 100644
--- a/chrome/browser/history_clusters/history_clusters_internals_browsertest.cc
+++ b/chrome/browser/history_clusters/history_clusters_internals_browsertest.cc
@@ -33,12 +33,11 @@
                           const char* web_ui_host,
                           base::span<const webui::ResourcePath> resources,
                           int default_resource) {
-  auto source = base::WrapUnique(content::WebUIDataSource::Create(web_ui_host));
-  webui::SetupWebUIDataSource(source.get(), resources, default_resource);
+  content::WebUIDataSource* source = content::WebUIDataSource::CreateAndAdd(
+      web_ui->GetWebContents()->GetBrowserContext(), web_ui_host);
+  webui::SetupWebUIDataSource(source, resources, default_resource);
   // Disable CSP for tests so that EvalJS can be invoked without CSP violations.
   source->DisableContentSecurityPolicy();
-  content::WebUIDataSource::Add(web_ui->GetWebContents()->GetBrowserContext(),
-                                source.release());
 }
 
 class TestWebUIControllerFactory : public content::WebUIControllerFactory {
diff --git a/chrome/browser/media/router/discovery/access_code/README.md b/chrome/browser/media/router/discovery/access_code/README.md
new file mode 100644
index 0000000..506807d
--- /dev/null
+++ b/chrome/browser/media/router/discovery/access_code/README.md
@@ -0,0 +1,34 @@
+# Overview
+Access Code Casting is an extension of the [Media Router](http://www.chromium.org/developers/design-documents/media-router) that allows for casting via an access code.
+
+# External Uses
+The external product that currently only uses this feature is [Cast Moderator](g.co/castmoderator/setup)
+
+# User Flow
+The code within this directory handles the back end of an access code within
+Chrome.
+1) An access code is submitted
+2) Check with the server if this is a valid access code
+3) Construct a device with returned info from server
+4) Attempt to add this device to the media router
+5) Attempt to start a casting session to this device
+6) (Optional) Store this device in prefs
+
+# Important Classes
+*access_code_cast_sink_service*
+The communication from the frontend to backend is handled by this class. This
+class also handles the lifetimes of other objects that are constructed within
+this directory.
+
+This class also handles stored device logic on startup/whenever a route is
+removed.
+
+*access_code_cast_discovery_interface*
+Handles communication between the server and Chrome
+
+*access_code_cast_pref_updater*
+Handles storage of prefs within Chrome.
+
+*access_code_cast_service_factory*
+Handles the construction of the AccessCodeCastSinkService and ensures lifetime
+is valid within the constrains of the Media Router lifetime.
diff --git a/chrome/browser/media/router/discovery/access_code/access_code_cast_sink_service_browsertest.cc b/chrome/browser/media/router/discovery/access_code/access_code_cast_sink_service_browsertest.cc
index 6ca7258..0131a1a 100644
--- a/chrome/browser/media/router/discovery/access_code/access_code_cast_sink_service_browsertest.cc
+++ b/chrome/browser/media/router/discovery/access_code/access_code_cast_sink_service_browsertest.cc
@@ -5,7 +5,6 @@
 #include "chrome/test/media_router/access_code_cast/access_code_cast_integration_browsertest.h"
 
 #include "base/test/metrics/histogram_tester.h"
-#include "chrome/browser/buildflags.h"
 #include "chrome/browser/media/router/discovery/access_code/access_code_cast_constants.h"
 #include "chrome/browser/media/router/discovery/access_code/access_code_media_sink_util.h"
 #include "chrome/browser/media/router/discovery/access_code/access_code_test_util.h"
@@ -76,14 +75,9 @@
   UpdateRoutes({media_route_cast});
   base::RunLoop().RunUntilIdle();
 
-// Recorded once from the route created when pressing submit, and then again
-// when we manually call `UpdateRoutes`.
-// TODO(b/262287112): AccessCodeCast.Discovery.DeviceDurationOnRoute is
-// recorded twice for saved devices browser tests on ChromeOS.
-#if !BUILDFLAG(IS_CHROMEOS)
+  // Recorded once from the route created when pressing submit.
   histogram_tester.ExpectTotalCount(
       "AccessCodeCast.Discovery.DeviceDurationOnRoute", 1);
-#endif
 
   EXPECT_CALL(*mock_cast_media_sink_service_impl(), DisconnectAndRemoveSink(_));
   UpdateRoutes({});
diff --git a/chrome/browser/optimization_guide/optimization_guide_internals_page_browsertest.cc b/chrome/browser/optimization_guide/optimization_guide_internals_page_browsertest.cc
index 62d98cc..b0f62931 100644
--- a/chrome/browser/optimization_guide/optimization_guide_internals_page_browsertest.cc
+++ b/chrome/browser/optimization_guide/optimization_guide_internals_page_browsertest.cc
@@ -42,12 +42,11 @@
                           const char* web_ui_host,
                           base::span<const webui::ResourcePath> resources,
                           int default_resource) {
-  auto source = base::WrapUnique(content::WebUIDataSource::Create(web_ui_host));
-  webui::SetupWebUIDataSource(source.get(), resources, default_resource);
+  content::WebUIDataSource* source = content::WebUIDataSource::CreateAndAdd(
+      web_ui->GetWebContents()->GetBrowserContext(), web_ui_host);
+  webui::SetupWebUIDataSource(source, resources, default_resource);
   // Disable CSP for tests so that EvalJS can be invoked without CSP violations.
   source->DisableContentSecurityPolicy();
-  content::WebUIDataSource::Add(web_ui->GetWebContents()->GetBrowserContext(),
-                                source.release());
 }
 
 class TestWebUIControllerFactory : public content::WebUIControllerFactory {
diff --git a/chrome/browser/pdf/pdf_extension_test.cc b/chrome/browser/pdf/pdf_extension_test.cc
index f7f6a65..34659569 100644
--- a/chrome/browser/pdf/pdf_extension_test.cc
+++ b/chrome/browser/pdf/pdf_extension_test.cc
@@ -1008,8 +1008,7 @@
         pak_path, ui::kScaleFactorNone);
 
     // Register the chrome://webui-test data source.
-    content::WebUIDataSource::Add(browser()->profile(),
-                                  webui::CreateWebUITestDataSource());
+    webui::CreateAndAddWebUITestDataSource(browser()->profile());
   }
 
   void RunTestsInJsModule(const std::string& filename,
diff --git a/chrome/browser/resources/app_settings/BUILD.gn b/chrome/browser/resources/app_settings/BUILD.gn
index 353b81d..5600a51 100644
--- a/chrome/browser/resources/app_settings/BUILD.gn
+++ b/chrome/browser/resources/app_settings/BUILD.gn
@@ -21,5 +21,6 @@
     "//third_party/polymer/v3_0:library",
     "//ui/webui/resources:library",
     "//ui/webui/resources/cr_components/app_management:build_ts",
+    "//ui/webui/resources/cr_components/localized_link:build_ts",
   ]
 }
diff --git a/chrome/browser/resources/bookmarks/api_listener.ts b/chrome/browser/resources/bookmarks/api_listener.ts
index acbc9c3..05033d0 100644
--- a/chrome/browser/resources/bookmarks/api_listener.ts
+++ b/chrome/browser/resources/bookmarks/api_listener.ts
@@ -122,7 +122,7 @@
 }
 
 function onImportEnded() {
-  chrome.bookmarks.getTree(function(results) {
+  chrome.bookmarks.getTree().then((results) => {
     dispatch(refreshNodes(normalizeNodes(results[0]!)));
   });
   chrome.bookmarks.onCreated.addListener(onBookmarkCreated);
@@ -177,3 +177,7 @@
         canEditBookmarksListener));
   }
 }
+
+export function setDebouncerForTesting() {
+  debouncer = new Debouncer(() => {});
+}
diff --git a/chrome/browser/resources/bookmarks/bookmarks.ts b/chrome/browser/resources/bookmarks/bookmarks.ts
index d745b959..1e85e1d 100644
--- a/chrome/browser/resources/bookmarks/bookmarks.ts
+++ b/chrome/browser/resources/bookmarks/bookmarks.ts
@@ -5,6 +5,7 @@
 import './app.js';
 
 export {changeFolderOpen, clearSearch, createBookmark, deselectItems, editBookmark, moveBookmark, removeBookmark, reorderChildren, selectFolder, SelectFolderAction, selectItem, SelectItemsAction, setSearchResults, setSearchTerm, StartSearchAction, updateAnchor} from './actions.js';
+export {setDebouncerForTesting} from './api_listener.js';
 export {BookmarksAppElement} from './app.js';
 export {BookmarksApiProxy, BookmarksApiProxyImpl, Query} from './bookmarks_api_proxy.js';
 export {BrowserProxy, BrowserProxyImpl} from './browser_proxy.js';
diff --git a/chrome/browser/resources/bookmarks/bookmarks_api_proxy.ts b/chrome/browser/resources/bookmarks/bookmarks_api_proxy.ts
index d6fa28f..b753139 100644
--- a/chrome/browser/resources/bookmarks/bookmarks_api_proxy.ts
+++ b/chrome/browser/resources/bookmarks/bookmarks_api_proxy.ts
@@ -19,27 +19,19 @@
 
 export class BookmarksApiProxyImpl implements BookmarksApiProxy {
   getTree() {
-    return new Promise<chrome.bookmarks.BookmarkTreeNode[]>(resolve => {
-      chrome.bookmarks.getTree(resolve);
-    });
+    return chrome.bookmarks.getTree();
   }
 
   search(query: Query) {
-    return new Promise<chrome.bookmarks.BookmarkTreeNode[]>(resolve => {
-      chrome.bookmarks.search(query, resolve);
-    });
+    return chrome.bookmarks.search(query);
   }
 
   update(id: string, changes: {title?: string, url?: string}) {
-    return new Promise<chrome.bookmarks.BookmarkTreeNode>(resolve => {
-      chrome.bookmarks.update(id, changes, resolve);
-    });
+    return chrome.bookmarks.update(id, changes);
   }
 
   create(bookmark: chrome.bookmarks.CreateDetails) {
-    return new Promise<chrome.bookmarks.BookmarkTreeNode>(resolve => {
-      chrome.bookmarks.create(bookmark, resolve);
-    });
+    return chrome.bookmarks.create(bookmark);
   }
 
   static getInstance(): BookmarksApiProxy {
diff --git a/chrome/browser/resources/bookmarks/edit_dialog.ts b/chrome/browser/resources/bookmarks/edit_dialog.ts
index d57d831d..f67b0c99 100644
--- a/chrome/browser/resources/bookmarks/edit_dialog.ts
+++ b/chrome/browser/resources/bookmarks/edit_dialog.ts
@@ -15,6 +15,7 @@
 import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {highlightUpdatedItems, trackUpdatedItems} from './api_listener.js';
+import {BookmarksApiProxyImpl} from './bookmarks_api_proxy.js';
 import {DialogFocusManager} from './dialog_focus_manager.js';
 import {getTemplate} from './edit_dialog.html.js';
 import {BookmarkNode} from './types.js';
@@ -153,7 +154,8 @@
     } else {
       edit['parentId'] = this.parentId_;
       trackUpdatedItems();
-      chrome.bookmarks.create(edit, highlightUpdatedItems);
+      BookmarksApiProxyImpl.getInstance().create(edit).then(
+          highlightUpdatedItems);
     }
     this.$.dialog.close();
   }
diff --git a/chrome/browser/resources/chromeos/accessibility/BUILD.gn b/chrome/browser/resources/chromeos/accessibility/BUILD.gn
index 1484ece..db57768 100644
--- a/chrome/browser/resources/chromeos/accessibility/BUILD.gn
+++ b/chrome/browser/resources/chromeos/accessibility/BUILD.gn
@@ -15,6 +15,7 @@
 group("build") {
   deps = [
     ":accessibility_strings",
+    ":braille_ime_manifest",
     ":chromevox_guest_manifest",
     ":chromevox_manifest",
     "chromevox:build",
@@ -38,6 +39,14 @@
   }
 }
 
+# The Braille IME manifest is not compiled
+copy("braille_ime_manifest") {
+  sources = [ "braille_ime_manifest.json" ]
+  outputs = [
+    "$root_out_dir/resources/chromeos/accessibility/braille_ime_manifest.json",
+  ]
+}
+
 template("manifest") {
   version_file = "//chrome/VERSION"
   version_script = "//build/util/version.py"
diff --git a/chrome/browser/resources/chromeos/accessibility/braille_ime/BUILD.gn b/chrome/browser/resources/chromeos/accessibility/braille_ime/BUILD.gn
index 0df742a..dcd61748 100644
--- a/chrome/browser/resources/chromeos/accessibility/braille_ime/BUILD.gn
+++ b/chrome/browser/resources/chromeos/accessibility/braille_ime/BUILD.gn
@@ -7,13 +7,6 @@
 
 assert(is_chromeos_ash)
 
-copy("braille_ime_manifest") {
-  sources = [ "manifest.json" ]
-  outputs = [
-    "$root_out_dir/resources/chromeos/accessibility/braille_ime/manifest.json",
-  ]
-}
-
 js_type_check("closure_compile") {
   deps = [
     ":braille_ime",
diff --git a/chrome/browser/resources/chromeos/accessibility/braille_ime/manifest.json b/chrome/browser/resources/chromeos/accessibility/braille_ime_manifest.json
similarity index 100%
rename from chrome/browser/resources/chromeos/accessibility/braille_ime/manifest.json
rename to chrome/browser/resources/chromeos/accessibility/braille_ime_manifest.json
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn b/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn
index aa9a574b..1b3b88c 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn
@@ -154,6 +154,7 @@
   "options/bluetooth_braille_display_ui.js",
   "options/options.js",
   "panel/i_search_ui.js",
+  "panel/menu_manager.js",
   "panel/panel.js",
   "panel/panel_interface.js",
   "panel/panel_menu.js",
@@ -193,7 +194,6 @@
     ":chromevox_options_script",
     ":chromevox_panel_script",
     ":chromevox_phonetic_dictionaries_js",
-    "//chrome/browser/resources/chromeos/accessibility/braille_ime:braille_ime_manifest",
     "//third_party/chromevox:chromevox_third_party_resources",
     "//third_party/liblouis",
   ]
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/background.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/background.js
index b152a36d..bdafc6d6 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/background.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/background.js
@@ -30,6 +30,7 @@
 import {DesktopAutomationInterface} from './desktop_automation_interface.js';
 import {DownloadHandler} from './download_handler.js';
 import {Earcons} from './earcons.js';
+import {EventSourceState} from './event_source.js';
 import {FindHandler} from './find_handler.js';
 import {FocusAutomationHandler} from './focus_automation_handler.js';
 import {FocusBounds} from './focus_bounds.js';
@@ -125,6 +126,7 @@
     ClipboardHandler.init();
     CommandHandler.init();
     DownloadHandler.init();
+    EventSourceState.init();
     FindHandler.init();
     FocusAutomationHandler.init();
     GestureCommandHandler.init();
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/event_source.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/event_source.js
index 0e8e2d38..800428e4 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/event_source.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/event_source.js
@@ -9,30 +9,30 @@
 import {BridgeHelper} from '../common/bridge_helper.js';
 import {EventSourceType} from '../common/event_source_type.js';
 
-export const EventSourceState = {};
+export class EventSourceState {
+  constructor() {
+    /** @private {!EventSourceType} */
+    this.state_ = chrome.accessibilityPrivate.IS_DEFAULT_EVENT_SOURCE_TOUCH ?
+        EventSourceType.TOUCH_GESTURE :
+        EventSourceType.NONE;
+  }
 
-/**
- * Sets the current event source.
- * @param {EventSourceType} source
- */
-EventSourceState.set = function(source) {
-  EventSourceState.current_ = source;
-};
+  static init() {
+    EventSourceState.instance = new EventSourceState();
 
-/**
- * Gets the current event source.
- * @return {EventSourceType}
- */
-EventSourceState.get = function() {
-  return EventSourceState.current_;
-};
+    BridgeHelper.registerHandler(
+        BridgeConstants.EventSourceState.TARGET,
+        BridgeConstants.EventSourceState.Action.GET,
+        () => EventSourceState.get());
+  }
 
-/** @private {EventSourceType} */
-EventSourceState.current_ =
-    chrome.accessibilityPrivate.IS_DEFAULT_EVENT_SOURCE_TOUCH ?
-    EventSourceType.TOUCH_GESTURE :
-    EventSourceType.NONE;
+  /** @param {!EventSourceType} source */
+  static set(source) {
+    EventSourceState.instance.state_ = source;
+  }
 
-BridgeHelper.registerHandler(
-    BridgeConstants.EventSourceState.TARGET,
-    BridgeConstants.EventSourceState.Action.GET, () => EventSourceState.get());
+  /** @return {!EventSourceType} */
+  static get() {
+    return EventSourceState.instance.state_;
+  }
+}
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/output/output.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/output/output.js
index 504fc667..4a25f57 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/output/output.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/output/output.js
@@ -670,22 +670,6 @@
   }
 
   /** @override */
-  formatListNestedLevel_(data) {
-    const buff = data.outputBuffer;
-    const node = data.node;
-
-    let level = 0;
-    let current = node;
-    while (current) {
-      if (current.role === RoleType.LIST) {
-        level += 1;
-      }
-      current = current.parent;
-    }
-    this.append_(buff, level.toString());
-  }
-
-  /** @override */
   formatPrecedingBullet_(data) {
     const buff = data.outputBuffer;
     const node = data.node;
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/output/output_formatter.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/output/output_formatter.js
index 18dfc86..ad6e0b05 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/output/output_formatter.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/output/output_formatter.js
@@ -111,7 +111,7 @@
     } else if (token === 'phoneticReading') {
       this.formatPhoneticReading_(this.params_);
     } else if (token === 'listNestedLevel') {
-      this.output_.formatListNestedLevel_(this.params_);
+      this.formatListNestedLevel_(this.params_);
     } else if (token === 'precedingBullet') {
       this.output_.formatPrecedingBullet_(this.params_);
     } else if (tree.firstChild) {
@@ -478,6 +478,25 @@
 
   /**
    * @param {!outputTypes.OutputFormattingData} data
+   * @private
+   */
+  formatListNestedLevel_(data) {
+    const buff = data.outputBuffer;
+    const node = data.node;
+
+    let level = 0;
+    let current = node;
+    while (current) {
+      if (current.role === RoleType.LIST) {
+        level += 1;
+      }
+      current = current.parent;
+    }
+    this.output_.append_(buff, level.toString());
+  }
+
+  /**
+   * @param {!outputTypes.OutputFormattingData} data
    * @param {string} token
    * @param {!{annotation: Array<*>, isUnique: (boolean|undefined)}} options
    */
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/output/output_interface.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/output/output_interface.js
index b8100f6..548dbeca 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/output/output_interface.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/output/output_interface.js
@@ -58,11 +58,6 @@
 
   /**
    * @param {!outputTypes.OutputFormattingData} data
-   */
-  formatListNestedLevel_(data) {}
-
-  /**
-   * @param {!outputTypes.OutputFormattingData} data
    * @param {string} token
    * @param {!OutputFormatTree} tree
    * @param {!{annotation: Array<*>, isUnique: (boolean|undefined)}} options
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/tts_background.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/tts_background.js
index ba70bc1..1694500 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/tts_background.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/tts_background.js
@@ -42,10 +42,6 @@
     BridgeHelper.registerHandler(
         TARGET, Action.GET_CURRENT_VOICE,
         () => TtsBackground.primary.currentVoice);
-    BridgeHelper.registerHandler(
-        TARGET, Action.SPEAK,
-        (textString, queue, properties) =>
-            TtsBackground.composite.speak(textString, queue, properties));
   }
 
   /** @return {!CompositeTts} */
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/panel/menu_manager.js b/chrome/browser/resources/chromeos/accessibility/chromevox/panel/menu_manager.js
new file mode 100644
index 0000000..47c0304
--- /dev/null
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/panel/menu_manager.js
@@ -0,0 +1,26 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview Class to manage the ChromeVox menus.
+ */
+import {PanelMenu} from './panel_menu.js';
+
+export class MenuManager {
+  constructor() {
+    /**
+     * The array of top-level menus.
+     * @private {!Array<PanelMenu>}
+     */
+    this.menus_ = [];
+  }
+
+  /**
+   * Temporary method during migration from panel.js.
+   * @return {!Array<PanelMenu>}
+   */
+  get menus() {
+    return this.menus_;
+  }
+}
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel.js b/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel.js
index 773d282e..931cd95 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel.js
@@ -25,6 +25,7 @@
 import {QueueMode} from '../common/tts_types.js';
 
 import {ISearchUI} from './i_search_ui.js';
+import {MenuManager} from './menu_manager.js';
 import {PanelInterface} from './panel_interface.js';
 import {PanelMenu, PanelNodeMenu, PanelSearchMenu} from './panel_menu.js';
 import {PanelMode, PanelModeInfo} from './panel_mode.js';
@@ -46,11 +47,8 @@
     /** @private {!PanelMode} */
     this.mode_ = PanelMode.COLLAPSED;
 
-    /**
-     * The array of top-level menus.
-     * @private {!Array<PanelMenu>}
-     */
-    this.menus_ = [];
+    /** @private {!MenuManager} */
+    this.menuManager_ = new MenuManager();
 
     /** @private {!Object<!PanelNodeMenuId, !PanelNodeMenu>} */
     this.nodeMenuDictionary_ = {};
@@ -480,8 +478,8 @@
       if (this.sessionState_ !== 'IN_SESSION') {
         tabsMenu.disable();
         // Disable commands that contain the property 'denyOOBE'.
-        for (let i = 0; i < this.menus_.length; ++i) {
-          const menu = this.menus_[i];
+        for (let i = 0; i < this.menuManager_.menus.length; ++i) {
+          const menu = this.menuManager_.menus[i];
           for (let j = 0; j < menu.items.length; ++j) {
             const item = menu.items[j];
             if (CommandStore.denySignedOut(
@@ -531,10 +529,10 @@
 
       // Activate either the specified menu or the search menu.
       // Search menu can be null, since it is hidden behind a flag.
-      let selectedMenu = this.searchMenu_ || this.menus_[0];
-      for (let i = 0; i < this.menus_.length; i++) {
-        if (this.menus_[i].menuMsg === opt_activateMenuTitle) {
-          selectedMenu = this.menus_[i];
+      let selectedMenu = this.searchMenu_ || this.menuManager_.menus[0];
+      for (let i = 0; i < this.menuManager_.menus.length; i++) {
+        if (this.menuManager_.menus[i].menuMsg === opt_activateMenuTitle) {
+          selectedMenu = this.menuManager_.menus[i];
         }
       }
 
@@ -570,8 +568,8 @@
    * @private
    */
   clearMenus_() {
-    while (this.menus_.length) {
-      const menu = this.menus_.pop();
+    while (this.menuManager_.menus.length) {
+      const menu = this.menuManager_.menus.pop();
       $('menu-bar').removeChild(menu.menuBarItemElement);
       $('menus_background').removeChild(menu.menuContainerElement);
     }
@@ -596,7 +594,7 @@
     menu.menuBarItemElement.addEventListener(
         'mouseup', event => this.onMouseUpOnMenuTitle_(menu, event), false);
     $('menus_background').appendChild(menu.menuContainerElement);
-    this.menus_.push(menu);
+    this.menuManager_.menus.push(menu);
     return menu;
   }
 
@@ -755,7 +753,7 @@
     menu.menuBarItemElement.addEventListener(
         'mouseup', event => this.onMouseUpOnMenuTitle_(menu, event));
     $('menus_background').appendChild(menu.menuContainerElement);
-    this.menus_.push(menu);
+    this.menuManager_.menus.push(menu);
     this.nodeMenuDictionary_[menuData.menuId] = menu;
   }
 
@@ -796,7 +794,7 @@
         'mouseup', event => this.onMouseUpOnMenuTitle_(this.searchMenu_, event),
         false);
     $('menus_background').appendChild(this.searchMenu_.menuContainerElement);
-    this.menus_.push(this.searchMenu_);
+    this.menuManager_.menus.push(this.searchMenu_);
     return this.searchMenu_;
   }
 
@@ -848,8 +846,8 @@
    */
   advanceActiveMenuBy_(delta) {
     let activeIndex = -1;
-    for (let i = 0; i < this.menus_.length; i++) {
-      if (this.activeMenu_ === this.menus_[i]) {
+    for (let i = 0; i < this.menuManager_.menus.length; i++) {
+      if (this.activeMenu_ === this.menuManager_.menus[i]) {
         activeIndex = i;
         break;
       }
@@ -857,12 +855,13 @@
 
     if (activeIndex >= 0) {
       activeIndex += delta;
-      activeIndex = (activeIndex + this.menus_.length) % this.menus_.length;
+      activeIndex = (activeIndex + this.menuManager_.menus.length) %
+          this.menuManager_.menus.length;
     } else {
       if (delta >= 0) {
         activeIndex = 0;
       } else {
-        activeIndex = this.menus_.length - 1;
+        activeIndex = this.menuManager_.menus.length - 1;
       }
     }
 
@@ -871,7 +870,8 @@
       return;
     }
 
-    this.activateMenu_(this.menus_[activeIndex], true /* activateFirstItem */);
+    this.activateMenu_(
+        this.menuManager_.menus[activeIndex], true /* activateFirstItem */);
   }
 
   /**
@@ -882,9 +882,9 @@
    * @private
    */
   findEnabledMenuIndex_(startIndex, delta) {
-    const endIndex = (delta > 0) ? this.menus_.length : -1;
+    const endIndex = (delta > 0) ? this.menuManager_.menus.length : -1;
     while (startIndex !== endIndex) {
-      if (this.menus_[startIndex].enabled) {
+      if (this.menuManager_.menus[startIndex].enabled) {
         return startIndex;
       }
       startIndex += delta;
@@ -1235,8 +1235,8 @@
     this.activateMenu_(this.searchMenu_, false /* activateFirstItem */);
     // Populate.
     if (query) {
-      for (let i = 0; i < this.menus_.length; ++i) {
-        const menu = this.menus_[i];
+      for (let i = 0; i < this.menuManager_.menus.length; ++i) {
+        const menu = this.menuManager_.menus[i];
         if (menu === this.searchMenu_ || menu instanceof PanelNodeMenu) {
           continue;
         }
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/action_manager.js b/chrome/browser/resources/chromeos/accessibility/switch_access/action_manager.js
index 7d67196..055254e 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/action_manager.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/action_manager.js
@@ -8,10 +8,10 @@
 import {Navigator} from './navigator.js';
 import {SAChildNode, SARootNode} from './nodes/switch_access_node.js';
 import {SwitchAccess} from './switch_access.js';
-import {SAConstants, SwitchAccessMenuAction} from './switch_access_constants.js';
+import {SAConstants} from './switch_access_constants.js';
 
 const ActionResponse = SAConstants.ActionResponse;
-const MenuAction = SwitchAccessMenuAction;
+const MenuAction = chrome.accessibilityPrivate.SwitchAccessMenuAction;
 const MenuType = SAConstants.MenuType;
 const Mode = SAConstants.Mode;
 
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/item_scan_manager_test.js b/chrome/browser/resources/chromeos/accessibility/switch_access/item_scan_manager_test.js
index 73dfc39..812dfbc4 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/item_scan_manager_test.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/item_scan_manager_test.js
@@ -22,12 +22,12 @@
     await importModule('Navigator', '/switch_access/navigator.js');
     await importModule('SACache', '/switch_access/cache.js');
     await importModule(
-        'SwitchAccessMenuAction', '/switch_access/switch_access_constants.js');
-    await importModule(
         'SwitchAccessPredicate', '/switch_access/switch_access_predicate.js');
     await importModule('KeyCode', '/common/key_code.js');
     await importModule('AutomationTreeWalker', '/common/tree_walker.js');
 
+    globalThis.MenuAction = chrome.accessibilityPrivate.SwitchAccessMenuAction;
+
     BackButtonNode
         .locationForTesting = {top: 10, left: 10, width: 20, height: 20};
   }
@@ -360,7 +360,7 @@
       assertEquals(
           'testinput', input.automationNode.htmlAttributes.id,
           'Current node is not input');
-      input.performAction(SwitchAccessMenuAction.KEYBOARD);
+      input.performAction(MenuAction.KEYBOARD);
 
       const keyboard =
           await this.untilFocusIs({role: chrome.automation.RoleType.KEYBOARD});
@@ -396,7 +396,7 @@
       assertEquals(
           'testinput', input.automationNode.htmlAttributes.id,
           'Current node is not input');
-      input.performAction(SwitchAccessMenuAction.KEYBOARD);
+      input.performAction(MenuAction.KEYBOARD);
 
       const keyboard =
           await this.untilFocusIs({role: chrome.automation.RoleType.KEYBOARD});
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/menu_manager.js b/chrome/browser/resources/chromeos/accessibility/switch_access/menu_manager.js
index 82e648b..b75d0b7 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/menu_manager.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/menu_manager.js
@@ -8,9 +8,9 @@
 import {ActionManager} from './action_manager.js';
 import {Navigator} from './navigator.js';
 import {SwitchAccess} from './switch_access.js';
-import {SwitchAccessMenuAction} from './switch_access_constants.js';
 
 const AutomationNode = chrome.automation.AutomationNode;
+const MenuAction = chrome.accessibilityPrivate.SwitchAccessMenuAction;
 
 /**
  * Class to handle interactions with the Switch Access action menu, including
@@ -20,7 +20,7 @@
 export class MenuManager {
   /** @private */
   constructor() {
-    /** @private {?Array<!SwitchAccessMenuAction>} */
+    /** @private {?Array<!MenuAction>} */
     this.displayedActions_ = null;
 
     /** @private {chrome.accessibilityPrivate.ScreenRect} */
@@ -50,7 +50,7 @@
   /**
    * If multiple actions are available for the currently highlighted node,
    * opens the menu. Otherwise performs the node's default action.
-   * @param {!Array<!SwitchAccessMenuAction>} actions
+   * @param {!Array<!MenuAction>} actions
    * @param {chrome.accessibilityPrivate.ScreenRect|undefined} location
    */
   static open(actions, location) {
@@ -95,12 +95,12 @@
 
   /**
    * @param {string=} actionString
-   * @return {?SwitchAccessMenuAction}
+   * @return {?MenuAction}
    * @private
    */
   asAction_(actionString) {
-    if (Object.values(SwitchAccessMenuAction).includes(actionString)) {
-      return /** @type {!SwitchAccessMenuAction} */ (actionString);
+    if (Object.values(MenuAction).includes(actionString)) {
+      return /** @type {!MenuAction} */ (actionString);
     }
     return null;
   }
@@ -108,7 +108,7 @@
   /**
    * Opens or reloads the menu for the current action node with the specified
    * actions.
-   * @param {!Array<SwitchAccessMenuAction>} actions
+   * @param {!Array<!MenuAction>} actions
    * @private
    */
   displayMenuWithActions_(actions) {
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/navigator_interface.js b/chrome/browser/resources/chromeos/accessibility/switch_access/navigator_interface.js
index 24f31438..23e9ad1 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/navigator_interface.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/navigator_interface.js
@@ -7,6 +7,7 @@
 import {SAChildNode, SARootNode} from './nodes/switch_access_node.js';
 
 const AutomationNode = chrome.automation.AutomationNode;
+const MenuAction = chrome.accessibilityPrivate.SwitchAccessMenuAction;
 
 /** @abstract */
 export class ItemNavigatorInterface {
@@ -152,7 +153,7 @@
 
   /**
    * Performs a mouse action at the currentPoint().
-   * @param {chrome.accessibilityPrivate.SwitchAccessMenuAction} action
+   * @param {MenuAction} action
    */
   performMouseAction(action) {}
 }
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/back_button_node.js b/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/back_button_node.js
index 885b8f63..94f8c28 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/back_button_node.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/back_button_node.js
@@ -9,11 +9,12 @@
 import {MenuManager} from '../menu_manager.js';
 import {Navigator} from '../navigator.js';
 import {SwitchAccess} from '../switch_access.js';
-import {SAConstants, SwitchAccessMenuAction} from '../switch_access_constants.js';
+import {SAConstants} from '../switch_access_constants.js';
 
 import {SAChildNode, SARootNode} from './switch_access_node.js';
 
 const AutomationNode = chrome.automation.AutomationNode;
+const MenuAction = chrome.accessibilityPrivate.SwitchAccessMenuAction;
 
 /**
  * This class handles the behavior of the back button.
@@ -38,7 +39,7 @@
 
   /** @override */
   get actions() {
-    return [SwitchAccessMenuAction.SELECT];
+    return [MenuAction.SELECT];
   }
 
   /** @override */
@@ -122,7 +123,7 @@
 
   /** @override */
   performAction(action) {
-    if (action === SwitchAccessMenuAction.SELECT && this.automationNode) {
+    if (action === MenuAction.SELECT && this.automationNode) {
       BackButtonNode.onClick_();
       return SAConstants.ActionResponse.CLOSE_MENU;
     }
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/basic_node.js b/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/basic_node.js
index 43b4ffc..4d8159a 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/basic_node.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/basic_node.js
@@ -9,13 +9,14 @@
 import {FocusRingManager} from '../focus_ring_manager.js';
 import {Navigator} from '../navigator.js';
 import {SwitchAccess} from '../switch_access.js';
-import {SAConstants, SwitchAccessMenuAction} from '../switch_access_constants.js';
+import {SAConstants} from '../switch_access_constants.js';
 import {SwitchAccessPredicate} from '../switch_access_predicate.js';
 
 import {BackButtonNode} from './back_button_node.js';
 import {SAChildNode, SARootNode} from './switch_access_node.js';
 
 const AutomationNode = chrome.automation.AutomationNode;
+const MenuAction = chrome.accessibilityPrivate.SwitchAccessMenuAction;
 
 /**
  * This class handles interactions with an onscreen element based on a single
@@ -44,26 +45,26 @@
   /** @override */
   get actions() {
     const actions = [];
-    actions.push(SwitchAccessMenuAction.SELECT);
+    actions.push(MenuAction.SELECT);
 
     const ancestor = this.getScrollableAncestor_();
     if (ancestor.scrollable) {
       if (ancestor.scrollX > ancestor.scrollXMin) {
-        actions.push(SwitchAccessMenuAction.SCROLL_LEFT);
+        actions.push(MenuAction.SCROLL_LEFT);
       }
       if (ancestor.scrollX < ancestor.scrollXMax) {
-        actions.push(SwitchAccessMenuAction.SCROLL_RIGHT);
+        actions.push(MenuAction.SCROLL_RIGHT);
       }
       if (ancestor.scrollY > ancestor.scrollYMin) {
-        actions.push(SwitchAccessMenuAction.SCROLL_UP);
+        actions.push(MenuAction.SCROLL_UP);
       }
       if (ancestor.scrollY < ancestor.scrollYMax) {
-        actions.push(SwitchAccessMenuAction.SCROLL_DOWN);
+        actions.push(MenuAction.SCROLL_DOWN);
       }
     }
-    const standardActions = /** @type {!Array<!SwitchAccessMenuAction>} */ (
+    const standardActions = /** @type {!Array<!MenuAction>} */ (
         this.baseNode_.standardActions.filter(
-            action => Object.values(SwitchAccessMenuAction).includes(action)));
+            action => Object.values(MenuAction).includes(action)));
 
     return actions.concat(standardActions);
   }
@@ -156,32 +157,32 @@
   performAction(action) {
     let ancestor;
     switch (action) {
-      case SwitchAccessMenuAction.SELECT:
+      case MenuAction.SELECT:
         if (this.isGroup()) {
           Navigator.byItem.enterGroup();
         } else {
           this.baseNode_.doDefault();
         }
         return SAConstants.ActionResponse.CLOSE_MENU;
-      case SwitchAccessMenuAction.SCROLL_DOWN:
+      case MenuAction.SCROLL_DOWN:
         ancestor = this.getScrollableAncestor_();
         if (ancestor.scrollable) {
           ancestor.scrollDown();
         }
         return SAConstants.ActionResponse.RELOAD_MENU;
-      case SwitchAccessMenuAction.SCROLL_UP:
+      case MenuAction.SCROLL_UP:
         ancestor = this.getScrollableAncestor_();
         if (ancestor.scrollable) {
           ancestor.scrollUp();
         }
         return SAConstants.ActionResponse.RELOAD_MENU;
-      case SwitchAccessMenuAction.SCROLL_RIGHT:
+      case MenuAction.SCROLL_RIGHT:
         ancestor = this.getScrollableAncestor_();
         if (ancestor.scrollable) {
           ancestor.scrollRight();
         }
         return SAConstants.ActionResponse.RELOAD_MENU;
-      case SwitchAccessMenuAction.SCROLL_LEFT:
+      case MenuAction.SCROLL_LEFT:
         ancestor = this.getScrollableAncestor_();
         if (ancestor.scrollable) {
           ancestor.scrollLeft();
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/basic_node_test.js b/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/basic_node_test.js
index b573b733..54c6e5f6 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/basic_node_test.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/basic_node_test.js
@@ -16,8 +16,8 @@
     await importModule('DesktopNode', '/switch_access/nodes/desktop_node.js');
     await importModule(
         'SARootNode', '/switch_access/nodes/switch_access_node.js');
-    await importModule(
-        'SwitchAccessMenuAction', '/switch_access/switch_access_constants.js');
+
+    globalThis.MenuAction = chrome.accessibilityPrivate.SwitchAccessMenuAction;
   }
 };
 
@@ -137,14 +137,13 @@
       chrome.automation.RoleType.TEXT_FIELD, textField.role,
       'Text field node is not a text field');
   assertTrue(
-      textField.hasAction(SwitchAccessMenuAction.KEYBOARD),
+      textField.hasAction(MenuAction.KEYBOARD),
       'Text field does not have action KEYBOARD');
   assertTrue(
-      textField.hasAction(SwitchAccessMenuAction.DICTATION),
+      textField.hasAction(MenuAction.DICTATION),
       'Text field does not have action DICTATION');
   assertFalse(
-      textField.hasAction(SwitchAccessMenuAction.SELECT),
-      'Text field has action SELECT');
+      textField.hasAction(MenuAction.SELECT), 'Text field has action SELECT');
 
   const button = BasicNode.create(
       rootWebArea.find({role: chrome.automation.RoleType.BUTTON}),
@@ -154,14 +153,12 @@
       chrome.automation.RoleType.BUTTON, button.role,
       'Button node is not a button');
   assertTrue(
-      button.hasAction(SwitchAccessMenuAction.SELECT),
+      button.hasAction(MenuAction.SELECT),
       'Button does not have action SELECT');
   assertFalse(
-      button.hasAction(SwitchAccessMenuAction.KEYBOARD),
-      'Button has action KEYBOARD');
+      button.hasAction(MenuAction.KEYBOARD), 'Button has action KEYBOARD');
   assertFalse(
-      button.hasAction(SwitchAccessMenuAction.DICTATION),
-      'Button has action DICTATION');
+      button.hasAction(MenuAction.DICTATION), 'Button has action DICTATION');
 
   const slider = BasicNode.create(
       rootWebArea.find({role: chrome.automation.RoleType.SLIDER}),
@@ -171,9 +168,9 @@
       chrome.automation.RoleType.SLIDER, slider.role,
       'Slider node is not a slider');
   assertTrue(
-      slider.hasAction(SwitchAccessMenuAction.INCREMENT),
+      slider.hasAction(MenuAction.INCREMENT),
       'Slider does not have action INCREMENT');
   assertTrue(
-      slider.hasAction(SwitchAccessMenuAction.DECREMENT),
+      slider.hasAction(MenuAction.DECREMENT),
       'Slider does not have action DECREMENT');
 });
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/combo_box_node.js b/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/combo_box_node.js
index c0f0463..3810e3e 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/combo_box_node.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/combo_box_node.js
@@ -7,12 +7,13 @@
 import {KeyCode} from '../../common/key_code.js';
 import {RepeatedEventHandler} from '../../common/repeated_event_handler.js';
 import {Navigator} from '../navigator.js';
-import {SAConstants, SwitchAccessMenuAction} from '../switch_access_constants.js';
+import {SAConstants} from '../switch_access_constants.js';
 
 import {BasicNode} from './basic_node.js';
 import {SAChildNode, SARootNode} from './switch_access_node.js';
 
 const AutomationNode = chrome.automation.AutomationNode;
+const MenuAction = chrome.accessibilityPrivate.SwitchAccessMenuAction;
 
 /**
  * This class handles interactions with combo boxes.
@@ -33,10 +34,9 @@
   /** @override */
   get actions() {
     const actions = super.actions;
-    if (!actions.includes(SwitchAccessMenuAction.INCREMENT) &&
-        !actions.includes(SwitchAccessMenuAction.DECREMENT)) {
-      actions.push(
-          SwitchAccessMenuAction.INCREMENT, SwitchAccessMenuAction.DECREMENT);
+    if (!actions.includes(MenuAction.INCREMENT) &&
+        !actions.includes(MenuAction.DECREMENT)) {
+      actions.push(MenuAction.INCREMENT, MenuAction.DECREMENT);
     }
     return actions;
   }
@@ -67,10 +67,10 @@
     // by selecting a value without opening the pop-up, using the up and down
     // arrows.
     switch (action) {
-      case SwitchAccessMenuAction.DECREMENT:
+      case MenuAction.DECREMENT:
         EventGenerator.sendKeyPress(KeyCode.UP);
         return SAConstants.ActionResponse.REMAIN_OPEN;
-      case SwitchAccessMenuAction.INCREMENT:
+      case MenuAction.INCREMENT:
         EventGenerator.sendKeyPress(KeyCode.DOWN);
         return SAConstants.ActionResponse.REMAIN_OPEN;
     }
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/desktop_node_test.js b/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/desktop_node_test.js
index 16d0e26..87c6946 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/desktop_node_test.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/desktop_node_test.js
@@ -11,8 +11,6 @@
     await importModule('DesktopNode', '/switch_access/nodes/desktop_node.js');
     await importModule(
         'BackButtonNode', '/switch_access/nodes/back_button_node.js');
-    await importModule(
-        'SwitchAccessMenuAction', '/switch_access/switch_access_constants.js');
   }
 };
 
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/editable_text_node.js b/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/editable_text_node.js
index 43b3179..4238ebce 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/editable_text_node.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/editable_text_node.js
@@ -7,7 +7,7 @@
 import {KeyCode} from '../../common/key_code.js';
 import {Navigator} from '../navigator.js';
 import {SwitchAccess} from '../switch_access.js';
-import {SAConstants, SwitchAccessMenuAction} from '../switch_access_constants.js';
+import {SAConstants} from '../switch_access_constants.js';
 import {SwitchAccessPredicate} from '../switch_access_predicate.js';
 import {TextNavigationManager} from '../text_navigation_manager.js';
 
@@ -15,6 +15,7 @@
 import {SAChildNode, SARootNode} from './switch_access_node.js';
 
 const AutomationNode = chrome.automation.AutomationNode;
+const MenuAction = chrome.accessibilityPrivate.SwitchAccessMenuAction;
 
 /**
  * This class handles interactions with editable text fields.
@@ -36,36 +37,34 @@
     // The SELECT action is used to press buttons, etc. For text inputs, the
     // equivalent action is KEYBOARD, which focuses the input and opens the
     // keyboard.
-    const selectIndex = actions.indexOf(SwitchAccessMenuAction.SELECT);
+    const selectIndex = actions.indexOf(MenuAction.SELECT);
     if (selectIndex >= 0) {
       actions.splice(selectIndex, 1);
     }
 
-    actions.unshift(
-        SwitchAccessMenuAction.KEYBOARD, SwitchAccessMenuAction.DICTATION);
+    actions.unshift(MenuAction.KEYBOARD, MenuAction.DICTATION);
 
     if (SwitchAccess.improvedTextInputEnabled()) {
       actions.push(
-          SwitchAccessMenuAction.MOVE_CURSOR,
-          SwitchAccessMenuAction.JUMP_TO_BEGINNING_OF_TEXT,
-          SwitchAccessMenuAction.JUMP_TO_END_OF_TEXT,
-          SwitchAccessMenuAction.MOVE_BACKWARD_ONE_CHAR_OF_TEXT,
-          SwitchAccessMenuAction.MOVE_FORWARD_ONE_CHAR_OF_TEXT,
-          SwitchAccessMenuAction.MOVE_BACKWARD_ONE_WORD_OF_TEXT,
-          SwitchAccessMenuAction.MOVE_FORWARD_ONE_WORD_OF_TEXT,
-          SwitchAccessMenuAction.MOVE_DOWN_ONE_LINE_OF_TEXT,
-          SwitchAccessMenuAction.MOVE_UP_ONE_LINE_OF_TEXT);
+          MenuAction.MOVE_CURSOR, MenuAction.JUMP_TO_BEGINNING_OF_TEXT,
+          MenuAction.JUMP_TO_END_OF_TEXT,
+          MenuAction.MOVE_BACKWARD_ONE_CHAR_OF_TEXT,
+          MenuAction.MOVE_FORWARD_ONE_CHAR_OF_TEXT,
+          MenuAction.MOVE_BACKWARD_ONE_WORD_OF_TEXT,
+          MenuAction.MOVE_FORWARD_ONE_WORD_OF_TEXT,
+          MenuAction.MOVE_DOWN_ONE_LINE_OF_TEXT,
+          MenuAction.MOVE_UP_ONE_LINE_OF_TEXT);
 
-      actions.push(SwitchAccessMenuAction.START_TEXT_SELECTION);
+      actions.push(MenuAction.START_TEXT_SELECTION);
       if (TextNavigationManager.currentlySelecting()) {
-        actions.push(SwitchAccessMenuAction.END_TEXT_SELECTION);
+        actions.push(MenuAction.END_TEXT_SELECTION);
       }
 
       if (TextNavigationManager.selectionExists) {
-        actions.push(SwitchAccessMenuAction.CUT, SwitchAccessMenuAction.COPY);
+        actions.push(MenuAction.CUT, MenuAction.COPY);
       }
       if (TextNavigationManager.clipboardHasData) {
-        actions.push(SwitchAccessMenuAction.PASTE);
+        actions.push(MenuAction.PASTE);
       }
     }
     return actions;
@@ -75,16 +74,16 @@
 
   /** @override */
   doDefaultAction() {
-    this.performAction(SwitchAccessMenuAction.KEYBOARD);
+    this.performAction(MenuAction.KEYBOARD);
   }
 
   /** @override */
   performAction(action) {
     switch (action) {
-      case SwitchAccessMenuAction.KEYBOARD:
+      case MenuAction.KEYBOARD:
         Navigator.byItem.enterKeyboard();
         return SAConstants.ActionResponse.CLOSE_MENU;
-      case SwitchAccessMenuAction.DICTATION:
+      case MenuAction.DICTATION:
         if (this.automationNode.state[chrome.automation.StateType.FOCUSED]) {
           chrome.accessibilityPrivate.toggleDictation();
         } else {
@@ -96,48 +95,48 @@
           this.automationNode.focus();
         }
         return SAConstants.ActionResponse.CLOSE_MENU;
-      case SwitchAccessMenuAction.MOVE_CURSOR:
+      case MenuAction.MOVE_CURSOR:
         return SAConstants.ActionResponse.OPEN_TEXT_NAVIGATION_MENU;
 
-      case SwitchAccessMenuAction.CUT:
+      case MenuAction.CUT:
         EventGenerator.sendKeyPress(KeyCode.X, {ctrl: true});
         return SAConstants.ActionResponse.REMAIN_OPEN;
-      case SwitchAccessMenuAction.COPY:
+      case MenuAction.COPY:
         EventGenerator.sendKeyPress(KeyCode.C, {ctrl: true});
         return SAConstants.ActionResponse.REMAIN_OPEN;
-      case SwitchAccessMenuAction.PASTE:
+      case MenuAction.PASTE:
         EventGenerator.sendKeyPress(KeyCode.V, {ctrl: true});
         return SAConstants.ActionResponse.REMAIN_OPEN;
 
-      case SwitchAccessMenuAction.START_TEXT_SELECTION:
+      case MenuAction.START_TEXT_SELECTION:
         TextNavigationManager.saveSelectStart();
         return SAConstants.ActionResponse.OPEN_TEXT_NAVIGATION_MENU;
-      case SwitchAccessMenuAction.END_TEXT_SELECTION:
+      case MenuAction.END_TEXT_SELECTION:
         TextNavigationManager.saveSelectEnd();
         return SAConstants.ActionResponse.EXIT_SUBMENU;
 
-      case SwitchAccessMenuAction.JUMP_TO_BEGINNING_OF_TEXT:
+      case MenuAction.JUMP_TO_BEGINNING_OF_TEXT:
         TextNavigationManager.jumpToBeginning();
         return SAConstants.ActionResponse.REMAIN_OPEN;
-      case SwitchAccessMenuAction.JUMP_TO_END_OF_TEXT:
+      case MenuAction.JUMP_TO_END_OF_TEXT:
         TextNavigationManager.jumpToEnd();
         return SAConstants.ActionResponse.REMAIN_OPEN;
-      case SwitchAccessMenuAction.MOVE_BACKWARD_ONE_CHAR_OF_TEXT:
+      case MenuAction.MOVE_BACKWARD_ONE_CHAR_OF_TEXT:
         TextNavigationManager.moveBackwardOneChar();
         return SAConstants.ActionResponse.REMAIN_OPEN;
-      case SwitchAccessMenuAction.MOVE_BACKWARD_ONE_WORD_OF_TEXT:
+      case MenuAction.MOVE_BACKWARD_ONE_WORD_OF_TEXT:
         TextNavigationManager.moveBackwardOneWord();
         return SAConstants.ActionResponse.REMAIN_OPEN;
-      case SwitchAccessMenuAction.MOVE_DOWN_ONE_LINE_OF_TEXT:
+      case MenuAction.MOVE_DOWN_ONE_LINE_OF_TEXT:
         TextNavigationManager.moveDownOneLine();
         return SAConstants.ActionResponse.REMAIN_OPEN;
-      case SwitchAccessMenuAction.MOVE_FORWARD_ONE_CHAR_OF_TEXT:
+      case MenuAction.MOVE_FORWARD_ONE_CHAR_OF_TEXT:
         TextNavigationManager.moveForwardOneChar();
         return SAConstants.ActionResponse.REMAIN_OPEN;
-      case SwitchAccessMenuAction.MOVE_UP_ONE_LINE_OF_TEXT:
+      case MenuAction.MOVE_UP_ONE_LINE_OF_TEXT:
         TextNavigationManager.moveUpOneLine();
         return SAConstants.ActionResponse.REMAIN_OPEN;
-      case SwitchAccessMenuAction.MOVE_FORWARD_ONE_WORD_OF_TEXT:
+      case MenuAction.MOVE_FORWARD_ONE_WORD_OF_TEXT:
         TextNavigationManager.moveForwardOneWord();
         return SAConstants.ActionResponse.REMAIN_OPEN;
     }
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/group_node.js b/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/group_node.js
index 2bc168a..0bb6238a 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/group_node.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/group_node.js
@@ -4,12 +4,13 @@
 
 import {RectUtil} from '../../common/rect_util.js';
 import {Navigator} from '../navigator.js';
-import {SAConstants, SwitchAccessMenuAction} from '../switch_access_constants.js';
+import {SAConstants} from '../switch_access_constants.js';
 
 import {BackButtonNode} from './back_button_node.js';
 import {SAChildNode, SARootNode} from './switch_access_node.js';
 
 const AutomationNode = chrome.automation.AutomationNode;
+const MenuAction = chrome.accessibilityPrivate.SwitchAccessMenuAction;
 
 /**
  * This class handles the grouping of nodes that are not grouped in the
@@ -39,7 +40,7 @@
 
   /** @override */
   get actions() {
-    return [SwitchAccessMenuAction.SELECT];
+    return [MenuAction.SELECT];
   }
 
   /** @override */
@@ -124,7 +125,7 @@
 
   /** @override */
   performAction(action) {
-    if (action === SwitchAccessMenuAction.SELECT) {
+    if (action === MenuAction.SELECT) {
       Navigator.byItem.enterGroup();
       return SAConstants.ActionResponse.CLOSE_MENU;
     }
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/group_node_test.js b/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/group_node_test.js
index b04c534e..47759980 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/group_node_test.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/group_node_test.js
@@ -11,8 +11,6 @@
     await importModule(
         ['BasicNode', 'BasicRootNode'], '/switch_access/nodes/basic_node.js');
     await importModule('GroupNode', '/switch_access/nodes/group_node.js');
-    await importModule(
-        'SwitchAccessMenuAction', '/switch_access/switch_access_constants.js');
   }
 };
 
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/keyboard_node.js b/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/keyboard_node.js
index 2b81b764c..eccd5d84 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/keyboard_node.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/keyboard_node.js
@@ -8,7 +8,7 @@
 import {AutoScanManager} from '../auto_scan_manager.js';
 import {Navigator} from '../navigator.js';
 import {SwitchAccess} from '../switch_access.js';
-import {SAConstants, SwitchAccessMenuAction} from '../switch_access_constants.js';
+import {SAConstants} from '../switch_access_constants.js';
 import {SwitchAccessPredicate} from '../switch_access_predicate.js';
 
 import {BackButtonNode} from './back_button_node.js';
@@ -17,6 +17,7 @@
 import {SAChildNode, SARootNode} from './switch_access_node.js';
 
 const AutomationNode = chrome.automation.AutomationNode;
+const MenuAction = chrome.accessibilityPrivate.SwitchAccessMenuAction;
 
 /**
  * This class handles the behavior of keyboard nodes directly associated with a
@@ -35,7 +36,7 @@
 
   /** @override */
   get actions() {
-    return [SwitchAccessMenuAction.SELECT];
+    return [MenuAction.SELECT];
   }
 
   // ================= General methods =================
@@ -69,7 +70,7 @@
 
   /** @override */
   performAction(action) {
-    if (action !== SwitchAccessMenuAction.SELECT) {
+    if (action !== MenuAction.SELECT) {
       return SAConstants.ActionResponse.NO_ACTION_TAKEN;
     }
 
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/slider_node.js b/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/slider_node.js
index 19c023a8..6b417f5 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/slider_node.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/slider_node.js
@@ -4,12 +4,13 @@
 
 import {EventGenerator} from '../../common/event_generator.js';
 import {KeyCode} from '../../common/key_code.js';
-import {SAConstants, SwitchAccessMenuAction} from '../switch_access_constants.js';
+import {SAConstants} from '../switch_access_constants.js';
 
 import {BasicNode} from './basic_node.js';
 import {SAChildNode, SARootNode} from './switch_access_node.js';
 
 const AutomationNode = chrome.automation.AutomationNode;
+const MenuAction = chrome.accessibilityPrivate.SwitchAccessMenuAction;
 
 /** This class handles interactions with sliders. */
 export class SliderNode extends BasicNode {
@@ -34,10 +35,10 @@
     // the automation API. We handle this case by simulating left/right arrow
     // presses.
     if (this.isCustomSlider_) {
-      if (action === SwitchAccessMenuAction.INCREMENT) {
+      if (action === MenuAction.INCREMENT) {
         EventGenerator.sendKeyPress(KeyCode.RIGHT);
         return SAConstants.ActionResponse.REMAIN_OPEN;
-      } else if (action === SwitchAccessMenuAction.DECREMENT) {
+      } else if (action === MenuAction.DECREMENT) {
         EventGenerator.sendKeyPress(KeyCode.LEFT);
         return SAConstants.ActionResponse.REMAIN_OPEN;
       }
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/switch_access_node.js b/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/switch_access_node.js
index 39656b9..c96ebe0 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/switch_access_node.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/switch_access_node.js
@@ -5,9 +5,10 @@
 import {RectUtil} from '../../common/rect_util.js';
 import {FocusRingManager} from '../focus_ring_manager.js';
 import {SwitchAccess} from '../switch_access.js';
-import {SAConstants, SwitchAccessMenuAction} from '../switch_access_constants.js';
+import {SAConstants} from '../switch_access_constants.js';
 
 const AutomationNode = chrome.automation.AutomationNode;
+const MenuAction = chrome.accessibilityPrivate.SwitchAccessMenuAction;
 
 /**
  * This interface represents some object or group of objects on screen
@@ -38,7 +39,7 @@
 
   /**
    * Returns a list of all the actions available for this node.
-   * @return {!Array<SwitchAccessMenuAction>}
+   * @return {!Array<MenuAction>}
    * @abstract
    */
   get actions() {}
@@ -132,7 +133,7 @@
     if (!this.isFocused_) {
       return;
     }
-    this.performAction(SwitchAccessMenuAction.SELECT);
+    this.performAction(MenuAction.SELECT);
   }
 
   /**
@@ -144,7 +145,7 @@
 
   /**
    * Given a menu action, returns whether it can be performed on this node.
-   * @param {SwitchAccessMenuAction} action
+   * @param {MenuAction} action
    * @return {boolean}
    */
   hasAction(action) {
@@ -200,7 +201,7 @@
 
   /**
    * Performs the specified action on the node, if it is available.
-   * @param {SwitchAccessMenuAction} action
+   * @param {MenuAction} action
    * @return {SAConstants.ActionResponse} What action the menu should perform in
    *      response.
    * @abstract
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/tab_node.js b/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/tab_node.js
index fcc7f0b..2ba1c53 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/tab_node.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/tab_node.js
@@ -4,13 +4,14 @@
 
 import {RectUtil} from '../../common/rect_util.js';
 import {Navigator} from '../navigator.js';
-import {SAConstants, SwitchAccessMenuAction} from '../switch_access_constants.js';
+import {SAConstants} from '../switch_access_constants.js';
 
 import {BackButtonNode} from './back_button_node.js';
 import {BasicNode, BasicRootNode} from './basic_node.js';
 import {SAChildNode, SARootNode} from './switch_access_node.js';
 
 const AutomationNode = chrome.automation.AutomationNode;
+const MenuAction = chrome.accessibilityPrivate.SwitchAccessMenuAction;
 
 /**
  * This class handles the behavior of tab nodes at the top level (i.e. as
@@ -35,7 +36,7 @@
 
   /** @override */
   get actions() {
-    return [SwitchAccessMenuAction.SELECT];
+    return [MenuAction.SELECT];
   }
 
   // ================= General methods =================
@@ -52,7 +53,7 @@
 
   /** @override */
   performAction(action) {
-    if (action !== SwitchAccessMenuAction.SELECT) {
+    if (action !== MenuAction.SELECT) {
       return SAConstants.ActionResponse.NO_ACTION_TAKEN;
     }
     Navigator.byItem.enterGroup();
@@ -104,7 +105,7 @@
 
   /** @override */
   get actions() {
-    return [SwitchAccessMenuAction.SELECT];
+    return [MenuAction.SELECT];
   }
 
   /** @override */
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/tab_node_test.js b/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/tab_node_test.js
index 61b24cd..a30d694 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/tab_node_test.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/tab_node_test.js
@@ -11,9 +11,9 @@
     await importModule(
         'BackButtonNode', '/switch_access/nodes/back_button_node.js');
     await importModule('Navigator', '/switch_access/navigator.js');
-    await importModule(
-        'SwitchAccessMenuAction', '/switch_access/switch_access_constants.js');
     await importModule('RectUtil', '/common/rect_util.js');
+
+    globalThis.MenuAction = chrome.accessibilityPrivate.SwitchAccessMenuAction;
   }
 };
 
@@ -45,8 +45,8 @@
     assertEquals(
         1, tab.actions.length, 'Tab as a group should have 1 action (select)');
     assertEquals(
-        chrome.accessibilityPrivate.SwitchAccessMenuAction.SELECT,
-        tab.actions[0], 'Tab as a group should have the action SELECT');
+        MenuAction.SELECT, tab.actions[0],
+        'Tab as a group should have the action SELECT');
 
     Navigator.byItem.node_.doDefaultAction();
 
@@ -64,7 +64,7 @@
     assertFalse(
         tabToSelect.isGroup(), 'Tab node to select should not be a group');
     assertTrue(
-        tabToSelect.hasAction(SwitchAccessMenuAction.SELECT),
+        tabToSelect.hasAction(MenuAction.SELECT),
         'Tab as a group should have a SELECT action');
     assertFalse(
         RectUtil.equal(tabAsRoot.location, tabToSelect.location),
@@ -81,7 +81,7 @@
         'Close button is not a button');
     assertFalse(close.isGroup(), 'Close button should not be a group');
     assertTrue(
-        close.hasAction(SwitchAccessMenuAction.SELECT),
+        close.hasAction(MenuAction.SELECT),
         'Close button should have a SELECT action');
     assertFalse(
         RectUtil.equal(tabAsRoot.location, close.location),
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/point_scan_manager.js b/chrome/browser/resources/chromeos/accessibility/switch_access/point_scan_manager.js
index dd93c3f2..0139221 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/point_scan_manager.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/point_scan_manager.js
@@ -9,8 +9,9 @@
 import {FocusRingManager} from './focus_ring_manager.js';
 import {PointNavigatorInterface} from './navigator_interface.js';
 import {SwitchAccess} from './switch_access.js';
-import {SAConstants, SwitchAccessMenuAction} from './switch_access_constants.js';
+import {SAConstants} from './switch_access_constants.js';
 
+const MenuAction = chrome.accessibilityPrivate.SwitchAccessMenuAction;
 const PointScanState = chrome.accessibilityPrivate.PointScanState;
 
 export class PointScanManager extends PointNavigatorInterface {
@@ -48,13 +49,12 @@
     if (SwitchAccess.mode !== SAConstants.Mode.POINT_SCAN) {
       return;
     }
-    if (action !== SwitchAccessMenuAction.LEFT_CLICK &&
-        action !== SwitchAccessMenuAction.RIGHT_CLICK) {
+    if (action !== MenuAction.LEFT_CLICK && action !== MenuAction.RIGHT_CLICK) {
       return;
     }
 
     const params = {};
-    if (action === SwitchAccessMenuAction.RIGHT_CLICK) {
+    if (action === MenuAction.RIGHT_CLICK) {
       params.mouseButton =
           chrome.accessibilityPrivate.SyntheticMouseEventButton.RIGHT;
     }
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/point_scan_manager_test.js b/chrome/browser/resources/chromeos/accessibility/switch_access/point_scan_manager_test.js
index 3a9f2c6..712c790e 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/point_scan_manager_test.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/point_scan_manager_test.js
@@ -19,8 +19,8 @@
     await importModule('Navigator', '/switch_access/navigator.js');
     await importModule('SwitchAccess', '/switch_access/switch_access.js');
     await importModule(
-        ['SwitchAccessMenuAction', 'SAConstants'],
-        '/switch_access/switch_access_constants.js');
+        'SAConstants', '/switch_access/switch_access_constants.js');
+    globalThis.MenuAction = chrome.accessibilityPrivate.SwitchAccessMenuAction;
   }
 };
 
@@ -47,7 +47,7 @@
 
       SwitchAccess.mode = SAConstants.Mode.POINT_SCAN;
       Navigator.byPoint.point_ = {x: 600, y: 600};
-      Navigator.byPoint.performMouseAction(SwitchAccessMenuAction.LEFT_CLICK);
+      Navigator.byPoint.performMouseAction(MenuAction.LEFT_CLICK);
       await new Promise(verifyChecked(false));
     });
 
@@ -76,7 +76,7 @@
 
       SwitchAccess.mode = SAConstants.Mode.POINT_SCAN;
       Navigator.byPoint.point_ = {x: 400, y: 400};
-      Navigator.byPoint.performMouseAction(SwitchAccessMenuAction.RIGHT_CLICK);
+      Navigator.byPoint.performMouseAction(MenuAction.RIGHT_CLICK);
       await new Promise(menuItemLoaded());
     });
 
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/switch_access_constants.js b/chrome/browser/resources/chromeos/accessibility/switch_access/switch_access_constants.js
index a72241e..e6e9b9e 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/switch_access_constants.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/switch_access_constants.js
@@ -3,8 +3,6 @@
 // found in the LICENSE file.
 
 const AutomationNode = chrome.automation.AutomationNode;
-export const SwitchAccessMenuAction =
-    chrome.accessibilityPrivate.SwitchAccessMenuAction;
 
 /** Constants used in Switch Access */
 export const SAConstants = {
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/test_support.js b/chrome/browser/resources/chromeos/accessibility/switch_access/test_support.js
index e1f15784..4d90d9c8 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/test_support.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/test_support.js
@@ -18,7 +18,6 @@
 
   module = await import('./switch_access_constants.js');
   globalThis.SAConstants = module.SAConstants;
-  globalThis.SwitchAccessMenuAction = module.SwitchAccessMenuAction;
 
   module = await import('./switch_access.js');
   globalThis.SwitchAccess = module.SwitchAccess;
@@ -62,7 +61,8 @@
     transcript.push(`Clicking with point scanning at location x=${x} y=${y}`);
     SwitchAccess.mode = SAConstants.Mode.POINT_SCAN;
     Navigator.byPoint.point_ = {x, y};
-    Navigator.byPoint.performMouseAction(SwitchAccessMenuAction.LEFT_CLICK);
+    Navigator.byPoint.performMouseAction(
+        chrome.accessibilityPrivate.SwitchAccessMenuAction.LEFT_CLICK);
     callback();
   };
 
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/text_navigation_manager.js b/chrome/browser/resources/chromeos/accessibility/switch_access/text_navigation_manager.js
index b06125e..2e9baea0 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/text_navigation_manager.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/text_navigation_manager.js
@@ -9,9 +9,10 @@
 import {ActionManager} from './action_manager.js';
 import {Navigator} from './navigator.js';
 import {SwitchAccess} from './switch_access.js';
-import {SAConstants, SwitchAccessMenuAction} from './switch_access_constants.js';
+import {SAConstants} from './switch_access_constants.js';
 
 const AutomationNode = chrome.automation.AutomationNode;
+const MenuAction = chrome.accessibilityPrivate.SwitchAccessMenuAction;
 
 /**
  * Class to handle navigating text. Currently, only
@@ -398,7 +399,7 @@
   updateClipboardHasData_() {
     this.clipboardHasData_ = true;
     const node = Navigator.byItem.currentNode;
-    if (node.hasAction(SwitchAccessMenuAction.PASTE)) {
+    if (node.hasAction(MenuAction.PASTE)) {
       ActionManager.refreshMenuForNode(node);
     }
   }
diff --git a/chrome/browser/resources/settings/BUILD.gn b/chrome/browser/resources/settings/BUILD.gn
index b66a7e3..7f9064c8 100644
--- a/chrome/browser/resources/settings/BUILD.gn
+++ b/chrome/browser/resources/settings/BUILD.gn
@@ -419,6 +419,7 @@
     "//third_party/polymer/v3_0:library",
     "//ui/webui/resources:library",
     "//ui/webui/resources/cr_components/help_bubble:build_ts",
+    "//ui/webui/resources/cr_components/localized_link:build_ts",
     "//ui/webui/resources/cr_components/managed_dialog:build_ts",
     "//ui/webui/resources/cr_components/managed_footnote:build_ts",
   ]
diff --git a/chrome/browser/resources/settings/chromeos/BUILD.gn b/chrome/browser/resources/settings/chromeos/BUILD.gn
index 7f0ef65c..0857c9d 100644
--- a/chrome/browser/resources/settings/chromeos/BUILD.gn
+++ b/chrome/browser/resources/settings/chromeos/BUILD.gn
@@ -90,6 +90,7 @@
     "//ui/webui/resources:library",
     "//ui/webui/resources/cr_components/app_management:build_ts",
     "//ui/webui/resources/cr_components/color_change_listener:build_ts",
+    "//ui/webui/resources/cr_components/localized_link:build_ts",
     "//ui/webui/resources/cr_components/managed_footnote:build_ts",
     "//ui/webui/resources/mojo:library",
   ]
diff --git a/chrome/browser/resources/settings/chromeos/device_page/fake_cros_audio_config.ts b/chrome/browser/resources/settings/chromeos/device_page/fake_cros_audio_config.ts
index 8ee227d..cdcdd73 100644
--- a/chrome/browser/resources/settings/chromeos/device_page/fake_cros_audio_config.ts
+++ b/chrome/browser/resources/settings/chromeos/device_page/fake_cros_audio_config.ts
@@ -106,7 +106,6 @@
 }
 
 export interface FakeCrosAudioConfigInterface extends CrosAudioConfigInterface {
-  setOutputMuted(muted: boolean): void;
   setInputMuted(muted: boolean): void;
   setInputVolumePercent(percent: number): void;
   setNoiseCancellationEnabled(enabled: boolean): void;
diff --git a/chrome/browser/resources/settings/chromeos/ensure_lazy_loaded.ts b/chrome/browser/resources/settings/chromeos/ensure_lazy_loaded.ts
index 2f05590..01c397f 100644
--- a/chrome/browser/resources/settings/chromeos/ensure_lazy_loaded.ts
+++ b/chrome/browser/resources/settings/chromeos/ensure_lazy_loaded.ts
@@ -2,6 +2,18 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import {getTrustedScriptURL} from 'chrome://resources/js/static_types.js';
+
+// List of pages (not subpages) that exist in the Advanced section.
+const LAZY_LOAD_PAGES = [
+  'settings-crostini-page',
+  'settings-date-time-page',
+  'os-settings-files-page',
+  'os-settings-languages-section',
+  'os-settings-printing-page',
+  'os-settings-reset-page',
+];
+
 let lazyLoadPromise: Promise<CustomElementConstructor[]>|null = null;
 
 /** @return Resolves when the lazy load module is imported. */
@@ -9,20 +21,11 @@
   if (!lazyLoadPromise) {
     const script = document.createElement('script');
     script.type = 'module';
-    script.src = './chromeos/lazy_load.js';
+    script.src = getTrustedScriptURL`./chromeos/lazy_load.js`;
     document.body.appendChild(script);
-    const lazyLoadPages = [
-      'settings-crostini-page',
-      'settings-date-time-page',
-      'os-settings-a11y-page',
-      'os-settings-files-page',
-      'os-settings-languages-section',
-      'os-settings-printing-page',
-      'os-settings-reset-page',
-    ];
 
     lazyLoadPromise = Promise.all(
-        lazyLoadPages.map((name) => customElements.whenDefined(name)));
+        LAZY_LOAD_PAGES.map((name) => customElements.whenDefined(name)));
   }
   return lazyLoadPromise;
 }
diff --git a/chrome/browser/resources/settings/chromeos/lazy_load.js b/chrome/browser/resources/settings/chromeos/lazy_load.js
index 81941c5..193aa70 100644
--- a/chrome/browser/resources/settings/chromeos/lazy_load.js
+++ b/chrome/browser/resources/settings/chromeos/lazy_load.js
@@ -2,6 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+/**
+ * @fileoverview This file is the entry point for custom elements and other
+ * modules that should be lazily loaded in the ChromeOS Settings frontend app.
+ * This should include:
+ *  - Top-level pages that exist in the "Advanced" section.
+ *  - All subpages
+ */
+
 import '../strings.m.js';
 import './crostini_page/bruschetta_subpage.js';
 import './crostini_page/crostini_arc_adb.js';
@@ -24,7 +32,6 @@
 import './guest_os/guest_os_shared_usb_devices.js';
 import './guest_os/guest_os_shared_usb_devices_add_dialog.js';
 import './guest_os/guest_os_shared_paths.js';
-import './os_a11y_page/os_a11y_page.js';
 import './os_a11y_page/manage_a11y_page.js';
 import './os_a11y_page/text_to_speech_page.js';
 import './os_a11y_page/display_and_magnification_page.js';
diff --git a/chrome/browser/resources/settings/chromeos/os_settings.js b/chrome/browser/resources/settings/chromeos/os_settings.js
index 0a0c4f9d..1771321 100644
--- a/chrome/browser/resources/settings/chromeos/os_settings.js
+++ b/chrome/browser/resources/settings/chromeos/os_settings.js
@@ -2,6 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+/**
+ * @fileoverview The main entry point for the ChromeOS Settings SWA. This
+ * imports all of the necessary modules and custom elements to load the page.
+ */
+
 import '../strings.m.js';
 import '../prefs/prefs.js';
 import './device_page/audio.js';
@@ -46,6 +51,7 @@
 import './nearby_share_page/nearby_share_subpage.js';
 import './personalization_page/personalization_page.js';
 import './os_a11y_page/change_dictation_locale_dialog.js';
+import './os_a11y_page/os_a11y_page.js';
 import './os_about_page/channel_switcher_dialog.js';
 import './os_about_page/detailed_build_info.js';
 import './os_about_page/os_about_page.js';
@@ -120,6 +126,11 @@
 
 import * as fakeCrosAudioConfig from './device_page/fake_cros_audio_config.js';
 
+/**
+ * With the optimize_webui() build step, the generated JS files are bundled
+ * into a single JS file. The exports below are necessary so they can be
+ * imported into browser tests.
+ */
 export {PermissionType, TriState} from 'chrome://resources/cr_components/app_management/app_management.mojom-webui.js';
 export {BrowserProxy as AppManagementComponentBrowserProxy} from 'chrome://resources/cr_components/app_management/browser_proxy.js';
 export {PageType, WindowMode} from 'chrome://resources/cr_components/app_management/constants.js';
diff --git a/chrome/browser/resources/settings/chromeos/os_settings_icons.html b/chrome/browser/resources/settings/chromeos/os_settings_icons.html
index 1ff5447a..bd5ffbfe 100644
--- a/chrome/browser/resources/settings/chromeos/os_settings_icons.html
+++ b/chrome/browser/resources/settings/chromeos/os_settings_icons.html
@@ -124,6 +124,7 @@
       <g id="google-drive"><path fill-rule="evenodd" clip-rule="evenodd" d="M18.7333 12L13.0167 2H7.31665V2.00833L13.025 12H18.7333ZM8.27502 12.8334L5.41669 17.8334H16.35L19.2084 12.8334H8.27502ZM6.59167 3.26672L1.125 12.8334L3.98333 17.8251L9.45 8.26672C9.45 8.27506 6.59167 3.26672 6.59167 3.26672Z"></path></g>
       <g id="google-play"><path fill-rule="evenodd" clip-rule="evenodd" d="M16.8167 9.06658L14.2667 7.61658L11.8834 9.99991L14.2667 12.3832L16.8167 10.9332C17.275 10.6749 17.5 10.3416 17.5 9.99991C17.5 9.65824 17.275 9.32491 16.8167 9.06658ZM3.92498 2.04163C4.93332 3.04996 10.9417 9.05829 10.9417 9.05829L13.0666 6.93329L4.14998 1.88329C4.09165 1.84996 4.03332 1.82496 3.97498 1.79996C3.83332 1.74163 3.72498 1.84163 3.84998 1.97496C3.87498 1.99163 3.89998 2.01663 3.92498 2.04163ZM3.92501 17.9583C3.90001 17.9833 3.87501 18.0083 3.85834 18.025C3.73334 18.15 3.84168 18.2583 3.98334 18.2C4.04168 18.175 4.10001 18.15 4.15834 18.1166L13.0667 13.0667L10.9417 10.9417C10.9417 10.9417 4.94168 16.95 3.92501 17.9583ZM10 9.99995C10 9.99995 2.975 2.97495 2.81667 2.81662C2.65833 2.65828 2.5 2.75828 2.5 2.97495V17.025C2.5 17.2416 2.65833 17.3416 2.81667 17.1833C2.975 17.025 10 9.99995 10 9.99995Z"></path></g>
       <g id="hard-drive"><path d="M14 14C14 14.5523 13.5523 15 13 15C12.4477 15 12 14.5523 12 14C12 13.4477 12.4477 13 13 13C13.5523 13 14 13.4477 14 14Z"></path><path fill-rule="evenodd" clip-rule="evenodd" d="M5 2C3.89543 2 3 2.89543 3 4V16C3 17.1046 3.89543 18 5 18H15C16.1046 18 17 17.1046 17 16V4C17 2.89543 16.1046 2 15 2H5ZM5 16H15V12H5V16ZM5 10H15V4H5V10Z"></path></g>
+      <g id="hotspot"><path d="M2 10.5C2 5.84343 5.808 2 10.5 2C15.192 2 19 5.84343 19 10.5C19 13.7534 17.2915 16.5158 15 18L14 16.5C15.9315 15.3233 17.3 13.1185 17.5 10.5C17.3 6.78713 14.257 3.71582 10.5 3.5C6.743 3.71582 3.7 6.78713 3.5 10.5C3.7 13.1185 5.0685 15.3233 7 16.5L6 18C3.7085 16.5158 2 13.7534 2 10.5Z"></path><path d="M15.5 10.5C15.5 7.78374 13.2583 5.5 10.5 5.5C7.74167 5.5 5.5 7.78374 5.5 10.5C5.5 12.4786 6.50833 14.1171 8 15L9 13.5C7.84167 12.9285 7.16667 11.8503 7 10.5C7.16667 8.71761 8.65833 7.19794 10.5 7C12.3417 7.19794 13.8333 8.71761 14 10.5C13.8333 11.8503 13.1583 12.9285 12 13.5L13 15C14.4917 14.1171 15.5 12.4786 15.5 10.5Z"></path><path d="M10.5 12C11.3284 12 12 11.3284 12 10.5C12 9.67157 11.3284 9 10.5 9C9.67157 9 9 9.67157 9 10.5C9 11.3284 9.67157 12 10.5 12Z"></path></g>
       <g id="ic-checked-filled"><circle cx="10" cy="10" r="8" fill="#1967D2"></circle><path fill-rule="evenodd" clip-rule="evenodd" d="M8.33333 11.833L6.16667 9.66634L5 10.833L8.33333 14.1663L15 7.49967L13.8333 6.33301L8.33333 11.833Z" fill="white"></path></g>
       <g id="ic-checked-filled-dark"><circle cx="10" cy="10" r="8" fill="#AECBFA"></circle><path fill-rule="evenodd" clip-rule="evenodd" d="M8.33333 11.833L6.16667 9.66634L5 10.833L8.33333 14.1663L15 7.49967L13.8333 6.33301L8.33333 11.833Z" fill="#2A2A2D"></path></g>
       <g id="lock"><path d="M11.75 12.5C11.75 13.4665 10.9665 14.25 10 14.25C9.0335 14.25 8.25 13.4665 8.25 12.5C8.25 11.5335 9.0335 10.75 10 10.75C10.9665 10.75 11.75 11.5335 11.75 12.5Z"></path><path fill-rule="evenodd" clip-rule="evenodd" d="M14 7H13.5V5C13.5 3.34315 11.6569 2 10 2C8.34315 2 6.5 3.34315 6.5 5V7H6C4.89543 7 4 7.89543 4 9V16C4 17.1046 4.89543 18 6 18H14C15.1046 18 16 17.1046 16 16V9C16 7.89543 15.1046 7 14 7ZM12 5.5V7H8V5.5C8 5 8.5 3.5 10 3.5C11.5 3.5 12 5 12 5.5ZM6 9V16H14V9H6Z"></path></g>
diff --git a/chrome/browser/resources/settings/chromeos/os_settings_search_box/os_search_result_row.ts b/chrome/browser/resources/settings/chromeos/os_settings_search_box/os_search_result_row.ts
index 97ec2a3..cb6410f 100644
--- a/chrome/browser/resources/settings/chromeos/os_settings_search_box/os_search_result_row.ts
+++ b/chrome/browser/resources/settings/chromeos/os_settings_search_box/os_search_result_row.ts
@@ -716,6 +716,8 @@
         return 'os-settings:google-play';
       case SearchResultIcon.kHardDrive:
         return 'os-settings:hard-drive';
+      case SearchResultIcon.kHotspot:
+        return 'os-settings:hotspot';
       case SearchResultIcon.kInstantTethering:
         return 'os-settings:magic-tethering';
       case SearchResultIcon.kKeyboard:
diff --git a/chrome/browser/resources/settings/languages_page/add_languages_dialog.html b/chrome/browser/resources/settings/languages_page/add_languages_dialog.html
index 3d877bf..27c8bdd8 100644
--- a/chrome/browser/resources/settings/languages_page/add_languages_dialog.html
+++ b/chrome/browser/resources/settings/languages_page/add_languages_dialog.html
@@ -37,11 +37,13 @@
       </div>
       <div id="dialog-body" slot="body" scrollable>
         <iron-list class="ripple-padding" scroll-target="dialog-body"
-            role="list" items="[[getLanguages_(filterValue_)]]">
+            role="grid" items="[[getLanguages_(filterValue_)]]"
+            aria-rowcount$="[[getLanguagesCount_(filterValue_)]]">
           <template>
-            <cr-checkbox class="list-item no-outline"
+            <cr-checkbox class="list-item no-outline" role="row"
                 checked="[[willAdd_(item.code)]]" tab-index="[[tabIndex]]"
-                title$="[[item.nativeDisplayName]]" role="listitem"
+                aria-rowindex$="[[getAriaRowindex_(index)]]"
+                aria-label="[[i18n('addLanguageAriaLabel', item.displayName)]]"
                 on-change="onLanguageCheckboxChange_">
               [[getDisplayText_(item)]]
             </cr-checkbox>
diff --git a/chrome/browser/resources/settings/languages_page/add_languages_dialog.ts b/chrome/browser/resources/settings/languages_page/add_languages_dialog.ts
index 8842d79..009f380 100644
--- a/chrome/browser/resources/settings/languages_page/add_languages_dialog.ts
+++ b/chrome/browser/resources/settings/languages_page/add_languages_dialog.ts
@@ -19,6 +19,7 @@
 import {CrScrollableMixin} from 'chrome://resources/cr_elements/cr_scrollable_mixin.js';
 import {CrSearchFieldElement} from 'chrome://resources/cr_elements/cr_search_field/cr_search_field.js';
 import {FindShortcutMixin} from 'chrome://resources/cr_elements/find_shortcut_mixin.js';
+import {I18nMixin} from 'chrome://resources/cr_elements/i18n_mixin.js';
 import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {getTemplate} from './add_languages_dialog.html.js';
@@ -38,7 +39,7 @@
 }
 
 const SettingsAddLanguagesDialogElementBase =
-    CrScrollableMixin(FindShortcutMixin(PolymerElement));
+    CrScrollableMixin(FindShortcutMixin(I18nMixin(PolymerElement)));
 
 export class SettingsAddLanguagesDialogElement extends
     SettingsAddLanguagesDialogElementBase {
@@ -111,9 +112,7 @@
     this.filterValue_ = e.detail;
   }
 
-  /**
-   * @return A list of languages to be displayed.
-   */
+  /** @return A list of languages to be displayed. */
   private getLanguages_(): chrome.languageSettingsPrivate.Language[] {
     if (!this.filterValue_) {
       return this.languages;
@@ -127,6 +126,16 @@
     });
   }
 
+  /** @return The number of languages to be displayed. */
+  private getLanguagesCount_(): number {
+    return this.getLanguages_().length;
+  }
+
+  /** @return A 1-based index for aria-rowindex. */
+  private getAriaRowindex_(index: number): number {
+    return index + 1;
+  }
+
   private getDisplayText_(language: chrome.languageSettingsPrivate.Language):
       string {
     return this.languageHelper.getFullName(language);
@@ -140,9 +149,7 @@
     return this.languagesToAdd_.has(languageCode);
   }
 
-  /**
-   * Handler for checking or unchecking a language item.
-   */
+  /** Handler for checking or unchecking a language item. */
   private onLanguageCheckboxChange_(e: Repeaterevent) {
     // Add or remove the item to the Set. No need to worry about data binding:
     // willAdd_ is called to initialize the checkbox state (in case the
@@ -162,9 +169,7 @@
     this.$.dialog.close();
   }
 
-  /**
-   * Enables the checked languages.
-   */
+  /** Enables the checked languages. */
   private onActionButtonTap_() {
     this.dispatchEvent(new CustomEvent('languages-added', {
       bubbles: true,
diff --git a/chrome/browser/resources/side_panel/bookmarks/bookmarks_api_proxy.ts b/chrome/browser/resources/side_panel/bookmarks/bookmarks_api_proxy.ts
index c50b2f9..380620e 100644
--- a/chrome/browser/resources/side_panel/bookmarks/bookmarks_api_proxy.ts
+++ b/chrome/browser/resources/side_panel/bookmarks/bookmarks_api_proxy.ts
@@ -107,14 +107,12 @@
   }
 
   getFolders() {
-    return new Promise<chrome.bookmarks.BookmarkTreeNode[]>(
-        resolve => chrome.bookmarks.getTree(results => {
-          if (results[0] && results[0].children) {
-            resolve(results[0].children);
-            return;
-          }
-          resolve([]);
-        }));
+    return chrome.bookmarks.getTree().then(results => {
+      if (results[0] && results[0].children) {
+        return results[0].children;
+      }
+      return [];
+    });
   }
 
   openBookmark(
diff --git a/chrome/browser/resources/welcome/google_apps/nux_google_apps.ts b/chrome/browser/resources/welcome/google_apps/nux_google_apps.ts
index fe8768f..aafc99a 100644
--- a/chrome/browser/resources/welcome/google_apps/nux_google_apps.ts
+++ b/chrome/browser/resources/welcome/google_apps/nux_google_apps.ts
@@ -259,13 +259,13 @@
   private updateBookmark_(item: AppItem) {
     if (item.selected && !item.bookmarkId) {
       this.bookmarkBarManager_.setShown(true);
-      this.bookmarkProxy_.addBookmark(
-          {
+      this.bookmarkProxy_
+          .addBookmark({
             title: item.name,
             url: item.url,
             parentId: '1',
-          },
-          result => {
+          })
+          .then(result => {
             item.bookmarkId = result.id;
           });
       // Cache bookmark icon.
diff --git a/chrome/browser/resources/welcome/shared/bookmark_proxy.ts b/chrome/browser/resources/welcome/shared/bookmark_proxy.ts
index dcf99c9..2e23d01 100644
--- a/chrome/browser/resources/welcome/shared/bookmark_proxy.ts
+++ b/chrome/browser/resources/welcome/shared/bookmark_proxy.ts
@@ -14,7 +14,7 @@
     void;
 
 export interface BookmarkProxy {
-  addBookmark(data: BookmarkData, callback: AddBookmarkCallback): void;
+  addBookmark(data: BookmarkData): Promise<chrome.bookmarks.BookmarkTreeNode>;
 
   /** @param id ID provided by callback when bookmark was added. */
   removeBookmark(id: string): void;
@@ -24,8 +24,8 @@
 }
 
 export class BookmarkProxyImpl implements BookmarkProxy {
-  addBookmark(data: BookmarkData, callback: AddBookmarkCallback) {
-    chrome.bookmarks.create(data, callback);
+  addBookmark(data: BookmarkData) {
+    return chrome.bookmarks.create(data);
   }
 
   removeBookmark(id: string) {
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index acef408..ebf095e 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -3256,6 +3256,9 @@
       "//chromeos/ash/services/bluetooth_config/public/mojom",
       "//chromeos/ash/services/cellular_setup",
       "//chromeos/ash/services/cellular_setup/public/mojom",
+      "//chromeos/ash/services/hotspot_config",
+      "//chromeos/ash/services/hotspot_config/public/cpp",
+      "//chromeos/ash/services/hotspot_config/public/mojom",
       "//chromeos/ash/services/multidevice_setup",
       "//chromeos/ash/services/multidevice_setup/public/cpp",
       "//chromeos/ash/services/multidevice_setup/public/cpp:android_sms_app_helper_delegate",
diff --git a/chrome/browser/ui/chrome_pages.cc b/chrome/browser/ui/chrome_pages.cc
index e7803b4..df9bad6e 100644
--- a/chrome/browser/ui/chrome_pages.cc
+++ b/chrome/browser/ui/chrome_pages.cc
@@ -40,6 +40,7 @@
 #include "chrome/common/url_constants.h"
 #include "components/bookmarks/browser/bookmark_model.h"
 #include "components/bookmarks/browser/bookmark_node.h"
+#include "components/privacy_sandbox/privacy_sandbox_features.h"
 #include "components/signin/public/base/consent_level.h"
 #include "content/public/browser/web_contents.h"
 #include "extensions/browser/extension_prefs.h"
@@ -470,7 +471,11 @@
 
 void ShowPrivacySandboxSettings(Browser* browser) {
   base::RecordAction(UserMetricsAction("Options_ShowPrivacySandbox"));
-  ShowSettingsSubPage(browser, kPrivacySandboxSubPage);
+  if (base::FeatureList::IsEnabled(privacy_sandbox::kPrivacySandboxSettings4)) {
+    ShowSettingsSubPage(browser, kAdPrivacySubPage);
+  } else {
+    ShowSettingsSubPage(browser, kPrivacySandboxSubPage);
+  }
 }
 
 void ShowPrivacySandboxAdPersonalization(Browser* browser) {
diff --git a/chrome/browser/ui/page_info/chrome_page_info_ui_delegate.cc b/chrome/browser/ui/page_info/chrome_page_info_ui_delegate.cc
index 2e030639..0abff36 100644
--- a/chrome/browser/ui/page_info/chrome_page_info_ui_delegate.cc
+++ b/chrome/browser/ui/page_info/chrome_page_info_ui_delegate.cc
@@ -182,6 +182,11 @@
   chrome::ShowPrivacySandboxAdPersonalization(browser);
 }
 
+void ChromePageInfoUiDelegate::ShowPrivacySandboxSettings() {
+  Browser* browser = chrome::FindBrowserWithWebContents(web_contents_);
+  chrome::ShowPrivacySandboxSettings(browser);
+}
+
 std::u16string ChromePageInfoUiDelegate::GetPermissionDetail(
     ContentSettingsType type) {
   switch (type) {
diff --git a/chrome/browser/ui/page_info/chrome_page_info_ui_delegate.h b/chrome/browser/ui/page_info/chrome_page_info_ui_delegate.h
index 258f03d91..867fe18 100644
--- a/chrome/browser/ui/page_info/chrome_page_info_ui_delegate.h
+++ b/chrome/browser/ui/page_info/chrome_page_info_ui_delegate.h
@@ -62,8 +62,12 @@
   std::u16string GetPermissionDetail(ContentSettingsType type);
 
   // Opens Privacy Sandbox's "Ad Personalzation" settings page.
+  // TODO(crbug.com/1378703): Remove after the feature is launched.
   void ShowPrivacySandboxAdPersonalization();
 
+  // Opens Privacy Sandbox settings page.
+  void ShowPrivacySandboxSettings();
+
   // PageInfoUiDelegate implementation
   bool IsBlockAutoPlayEnabled() override;
   bool IsMultipleTabsOpen() override;
diff --git a/chrome/browser/ui/views/crostini/crostini_force_close_view_browsertest.cc b/chrome/browser/ui/views/crostini/crostini_force_close_view_browsertest.cc
index 6585f20..6180b15 100644
--- a/chrome/browser/ui/views/crostini/crostini_force_close_view_browsertest.cc
+++ b/chrome/browser/ui/views/crostini/crostini_force_close_view_browsertest.cc
@@ -11,7 +11,6 @@
 #include "base/run_loop.h"
 #include "chrome/browser/ui/views/crostini/crostini_dialogue_browser_test_util.h"
 #include "components/exo/shell_surface.h"
-#include "components/exo/test/exo_test_base_views.h"
 #include "components/exo/test/shell_surface_builder.h"
 #include "components/exo/wm_helper_chromeos.h"
 #include "content/public/test/browser_test.h"
diff --git a/chrome/browser/ui/views/extensions/extensions_menu_main_page_view.cc b/chrome/browser/ui/views/extensions/extensions_menu_main_page_view.cc
index bf7a171..21f6f60 100644
--- a/chrome/browser/ui/views/extensions/extensions_menu_main_page_view.cc
+++ b/chrome/browser/ui/views/extensions/extensions_menu_main_page_view.cc
@@ -8,8 +8,10 @@
 
 #include "base/functional/bind.h"
 #include "base/i18n/case_conversion.h"
+#include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/color/chrome_color_id.h"
+#include "chrome/browser/ui/toolbar/toolbar_actions_model.h"
 #include "chrome/browser/ui/views/chrome_layout_provider.h"
 #include "chrome/browser/ui/views/chrome_typography.h"
 #include "chrome/browser/ui/views/extensions/extensions_menu_navigation_handler.h"
@@ -21,6 +23,7 @@
 #include "ui/views/bubble/bubble_frame_view.h"
 #include "ui/views/controls/button/button.h"
 #include "ui/views/controls/button/label_button.h"
+#include "ui/views/controls/button/toggle_button.h"
 #include "ui/views/controls/label.h"
 #include "ui/views/layout/box_layout.h"
 #include "ui/views/layout/flex_layout_view.h"
@@ -31,6 +34,8 @@
 
 namespace {
 
+using PermissionsManager = extensions::PermissionsManager;
+
 // Returns the current site pointed by `web_contents`. This method should only
 // be called when web contents are present.
 std::u16string GetCurrentSite(content::WebContents* web_contents) {
@@ -40,6 +45,33 @@
       url);
 }
 
+// Updates the `toggle_button` text based on its state.
+void UpdateSiteSettingToggleText(views::ToggleButton* toggle_button) {
+  bool is_on = toggle_button->GetIsOn();
+  toggle_button->SetTooltipText(l10n_util::GetStringUTF16(
+      is_on ? IDS_EXTENSIONS_MENU_SITE_SETTINGS_TOGGLE_ON_TOOLTIP
+            : IDS_EXTENSIONS_MENU_SITE_SETTINGS_TOGGLE_OFF_TOOLTIP));
+  toggle_button->SetAccessibleName(l10n_util::GetStringUTF16(
+      is_on ? IDS_EXTENSIONS_MENU_SITE_SETTINGS_TOGGLE_ON_TOOLTIP
+            : IDS_EXTENSIONS_MENU_SITE_SETTINGS_TOGGLE_OFF_TOOLTIP));
+}
+
+// Returns whether `site_settings_toggle_` should be on or off.
+bool IsSiteSettingsToggleOn(Browser* browser,
+                            content::WebContents* web_contents) {
+  auto origin = web_contents->GetPrimaryMainFrame()->GetLastCommittedOrigin();
+  return PermissionsManager::Get(browser->profile())
+             ->GetUserSiteSetting(origin) ==
+         PermissionsManager::UserSiteSetting::kCustomizeByExtension;
+}
+
+// Returns whether `site_setting_toggle_` should be visible.
+bool IsSiteSettingsToggleVisible(
+    const raw_ptr<ToolbarActionsModel> toolbar_model,
+    content::WebContents* web_contents) {
+  return !toolbar_model->IsRestrictedUrl(web_contents->GetLastCommittedURL());
+}
+
 }  // namespace
 
 class RequestsAccessSection : public views::BoxLayoutView {
@@ -87,12 +119,15 @@
 ExtensionsMenuMainPageView::ExtensionsMenuMainPageView(
     Browser* browser,
     ExtensionsMenuNavigationHandler* navigation_handler)
-    : browser_(browser), navigation_handler_(navigation_handler) {
+    : browser_(browser),
+      navigation_handler_(navigation_handler),
+      toolbar_model_(ToolbarActionsModel::Get(browser_->profile())) {
   views::FlexSpecification stretch_specification =
       views::FlexSpecification(views::MinimumFlexSizeRule::kScaleToZero,
                                views::MaximumFlexSizeRule::kUnbounded,
                                /*adjust_height_for_width =*/true)
           .WithWeight(1);
+  content::WebContents* web_contents = GetActiveWebContents();
 
   views::Builder<ExtensionsMenuMainPageView>(this)
       .SetLayoutManager(std::make_unique<views::BoxLayout>(
@@ -121,7 +156,7 @@
                               .SetTextStyle(views::style::STYLE_SECONDARY),
                           views::Builder<views::Label>()
                               .CopyAddressTo(&subheader_subtitle_)
-                              .SetText(GetCurrentSite(GetActiveWebContents()))
+                              .SetText(GetCurrentSite(web_contents))
                               .SetHorizontalAlignment(gfx::ALIGN_LEFT)
                               .SetTextContext(views::style::CONTEXT_LABEL)
                               .SetTextStyle(views::style::STYLE_SECONDARY)
@@ -129,6 +164,18 @@
                               .SetMultiLine(true)
                               .SetProperty(views::kFlexBehaviorKey,
                                            stretch_specification)),
+                  // Toggle site settings button.
+                  // TODO(crbug.com/1390952): Move button under close button.
+                  // This will be done as part of adding margins to the menu.
+                  views::Builder<views::ToggleButton>()
+                      .CopyAddressTo(&site_settings_toggle_)
+                      .SetCallback(base::BindRepeating(
+                          &ExtensionsMenuMainPageView::OnToggleButtonPressed,
+                          base::Unretained(this)))
+                      .SetVisible(IsSiteSettingsToggleVisible(toolbar_model_,
+                                                              web_contents))
+                      .SetIsOn(IsSiteSettingsToggleOn(browser_, web_contents)),
+                  // Close button.
                   views::Builder<views::Button>(
                       views::BubbleFrameView::CreateCloseButton(
                           base::BindRepeating(
@@ -146,13 +193,28 @@
                   base::Unretained(navigation_handler_))))
       .BuildChildren();
 
+  // Update toggle button text after it's build, as it depends on its state.
+  UpdateSiteSettingToggleText(site_settings_toggle_);
+
   browser_->tab_strip_model()->AddObserver(this);
 }
 
 void ExtensionsMenuMainPageView::Update() {
   content::WebContents* web_contents = GetActiveWebContents();
-  if (web_contents)
+  if (web_contents) {
     subheader_subtitle_->SetText(GetCurrentSite(web_contents));
+
+    site_settings_toggle_->SetVisible(
+        IsSiteSettingsToggleVisible(toolbar_model_, web_contents));
+    site_settings_toggle_->SetIsOn(
+        IsSiteSettingsToggleOn(browser_, web_contents));
+    UpdateSiteSettingToggleText(site_settings_toggle_);
+  }
+}
+
+void ExtensionsMenuMainPageView::OnToggleButtonPressed() {
+  // TODO(crbug.com/1390952): Update user site setting and add test.
+  UpdateSiteSettingToggleText(site_settings_toggle_);
 }
 
 void ExtensionsMenuMainPageView::TabChangedAt(content::WebContents* contents,
diff --git a/chrome/browser/ui/views/extensions/extensions_menu_main_page_view.h b/chrome/browser/ui/views/extensions/extensions_menu_main_page_view.h
index 5e84205..3c4bd49 100644
--- a/chrome/browser/ui/views/extensions/extensions_menu_main_page_view.h
+++ b/chrome/browser/ui/views/extensions/extensions_menu_main_page_view.h
@@ -10,10 +10,12 @@
 
 namespace views {
 class Label;
+class ToggleButton;
 }
 
 class Browser;
 class ExtensionsMenuNavigationHandler;
+class ToolbarActionsModel;
 
 // The main view of the extensions menu.
 class ExtensionsMenuMainPageView : public views::View,
@@ -29,6 +31,8 @@
 
   void Update();
 
+  void OnToggleButtonPressed();
+
   // TabStripModelObserver:
   // Sometimes, menu can stay open when tab changes (e.g keyboard shortcuts) or
   // due to the extension (e.g extension switching the active tab). Thus, we
@@ -44,10 +48,13 @@
  private:
   content::WebContents* GetActiveWebContents() const;
 
-  raw_ptr<Browser> browser_;
-  raw_ptr<ExtensionsMenuNavigationHandler> navigation_handler_;
+  const raw_ptr<Browser> browser_;
+  const raw_ptr<ExtensionsMenuNavigationHandler> navigation_handler_;
+  const raw_ptr<ToolbarActionsModel> toolbar_model_;
 
+  // Subheader.
   raw_ptr<views::Label> subheader_subtitle_;
+  raw_ptr<views::ToggleButton> site_settings_toggle_;
 };
 
 BEGIN_VIEW_BUILDER(/* no export */, ExtensionsMenuMainPageView, views::View)
diff --git a/chrome/browser/ui/views/page_info/page_info_ad_personalization_content_view.cc b/chrome/browser/ui/views/page_info/page_info_ad_personalization_content_view.cc
index c5c55d2f..6d26a8a6 100644
--- a/chrome/browser/ui/views/page_info/page_info_ad_personalization_content_view.cc
+++ b/chrome/browser/ui/views/page_info/page_info_ad_personalization_content_view.cc
@@ -13,6 +13,7 @@
 #include "chrome/browser/ui/views/chrome_typography.h"
 #include "chrome/browser/ui/views/controls/rich_hover_button.h"
 #include "chrome/browser/ui/views/page_info/page_info_view_factory.h"
+#include "components/privacy_sandbox/privacy_sandbox_features.h"
 #include "components/strings/grit/components_strings.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/views/border.h"
@@ -32,14 +33,18 @@
       views::BoxLayout::Orientation::kVertical));
 
   AddChildView(PageInfoViewFactory::CreateSeparator());
-  // TODO(olesiamarukhno): Use correct strings.
   AddChildView(std::make_unique<RichHoverButton>(
       base::BindRepeating(
           [](PageInfoAdPersonalizationContentView* view) {
             view->presenter_->RecordPageInfoAction(
                 PageInfo::PageInfoAction::
                     PAGE_INFO_AD_PERSONALIZATION_SETTINGS_OPENED);
-            view->ui_delegate_->ShowPrivacySandboxAdPersonalization();
+            if (base::FeatureList::IsEnabled(
+                    privacy_sandbox::kPrivacySandboxSettings4)) {
+              view->ui_delegate_->ShowPrivacySandboxSettings();
+            } else {
+              view->ui_delegate_->ShowPrivacySandboxAdPersonalization();
+            }
           },
           this),
       PageInfoViewFactory::GetSiteSettingsIcon(),
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 59e4586..572ee98 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
@@ -792,7 +792,7 @@
 
 void WebAppIntegrationTestDriver::SetUpOnMainThread() {
   override_registration_ =
-      ShortcutOverrideForTesting::OverrideForTesting(base::GetHomeDir());
+      OsIntegrationTestOverride::OverrideForTesting(base::GetHomeDir());
 
   // Only support manifest updates on non-sync tests, as the current
   // infrastructure here only supports listening on one profile.
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 0506e77..1687640 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
@@ -450,7 +450,7 @@
   base::ScopedObservation<web_app::WebAppInstallManager,
                           web_app::WebAppInstallManagerObserver>
       observation_{this};
-  std::unique_ptr<ShortcutOverrideForTesting::BlockingRegistration>
+  std::unique_ptr<OsIntegrationTestOverride::BlockingRegistration>
       override_registration_;
 
   std::unique_ptr<base::RunLoop> window_controls_overlay_callback_for_testing_ =
diff --git a/chrome/browser/ui/web_applications/web_app_browsertest.cc b/chrome/browser/ui/web_applications/web_app_browsertest.cc
index 8fd7b58..ae0f9230 100644
--- a/chrome/browser/ui/web_applications/web_app_browsertest.cc
+++ b/chrome/browser/ui/web_applications/web_app_browsertest.cc
@@ -1301,8 +1301,8 @@
   os_hooks_suppress_.reset();
   base::ScopedAllowBlockingForTesting allow_blocking;
 
-  std::unique_ptr<ShortcutOverrideForTesting::BlockingRegistration>
-      registration = ShortcutOverrideForTesting::OverrideForTesting();
+  std::unique_ptr<OsIntegrationTestOverride::BlockingRegistration>
+      registration = OsIntegrationTestOverride::OverrideForTesting();
 
   NavigateToURLAndWait(
       browser(),
@@ -1369,8 +1369,8 @@
   os_hooks_suppress_.reset();
   base::ScopedAllowBlockingForTesting allow_blocking;
 
-  std::unique_ptr<ShortcutOverrideForTesting::BlockingRegistration>
-      registration = ShortcutOverrideForTesting::OverrideForTesting();
+  std::unique_ptr<OsIntegrationTestOverride::BlockingRegistration>
+      registration = OsIntegrationTestOverride::OverrideForTesting();
   NavigateToURLAndWait(
       browser(),
       https_server()->GetURL(
@@ -1450,8 +1450,8 @@
   os_hooks_suppress_.reset();
   base::ScopedAllowBlockingForTesting allow_blocking;
 
-  std::unique_ptr<ShortcutOverrideForTesting::BlockingRegistration>
-      registration = ShortcutOverrideForTesting::OverrideForTesting();
+  std::unique_ptr<OsIntegrationTestOverride::BlockingRegistration>
+      registration = OsIntegrationTestOverride::OverrideForTesting();
   NavigateToURLAndWait(
       browser(),
       https_server()->GetURL("/banners/"
@@ -1515,8 +1515,8 @@
 
   base::ScopedAllowBlockingForTesting allow_blocking;
 
-  std::unique_ptr<ShortcutOverrideForTesting::BlockingRegistration>
-      registration = ShortcutOverrideForTesting::OverrideForTesting();
+  std::unique_ptr<OsIntegrationTestOverride::BlockingRegistration>
+      registration = OsIntegrationTestOverride::OverrideForTesting();
 
   auto* provider = WebAppProvider::GetForTest(profile());
 
@@ -1624,9 +1624,9 @@
       &g_app_shims_allow_update_and_launch_in_tests, true);
 #endif
   base::ScopedAllowBlockingForTesting allow_blocking;
-  std::unique_ptr<ShortcutOverrideForTesting::BlockingRegistration>
+  std::unique_ptr<OsIntegrationTestOverride::BlockingRegistration>
       shortcut_override =
-          ShortcutOverrideForTesting::OverrideForTesting(base::GetHomeDir());
+          OsIntegrationTestOverride::OverrideForTesting(base::GetHomeDir());
 
   NavigateToURLAndWait(browser(), GetInstallableAppURL());
 
@@ -2187,9 +2187,9 @@
   base::ScopedAllowBlockingForTesting allow_blocking;
   base::HistogramTester tester;
 
-  std::unique_ptr<ShortcutOverrideForTesting::BlockingRegistration>
+  std::unique_ptr<OsIntegrationTestOverride::BlockingRegistration>
       registration =
-          ShortcutOverrideForTesting::OverrideForTesting(base::GetHomeDir());
+          OsIntegrationTestOverride::OverrideForTesting(base::GetHomeDir());
   std::vector<std::string> expected_extensions{"bar", "baz", "foo", "foobar"};
 
   ASSERT_TRUE(embedded_test_server()->Start());
@@ -2289,9 +2289,9 @@
   os_hooks_suppress_.reset();
   base::ScopedAllowBlockingForTesting allow_blocking;
 
-  std::unique_ptr<ShortcutOverrideForTesting::BlockingRegistration>
+  std::unique_ptr<OsIntegrationTestOverride::BlockingRegistration>
       registration =
-          ShortcutOverrideForTesting::OverrideForTesting(base::GetHomeDir());
+          OsIntegrationTestOverride::OverrideForTesting(base::GetHomeDir());
   std::vector<std::string> expected_extensions{"bar", "baz", "foo", "foobar"};
 
   ASSERT_TRUE(embedded_test_server()->Start());
diff --git a/chrome/browser/ui/webui/ash/parent_access/parent_access_ui_handler_impl.cc b/chrome/browser/ui/webui/ash/parent_access/parent_access_ui_handler_impl.cc
index 9082e466..8c4fe84d 100644
--- a/chrome/browser/ui/webui/ash/parent_access/parent_access_ui_handler_impl.cc
+++ b/chrome/browser/ui/webui/ash/parent_access/parent_access_ui_handler_impl.cc
@@ -77,12 +77,9 @@
 void ParentAccessUIHandlerImpl::RecordParentAccessWidgetError(
     ParentAccessUIHandlerImpl::ParentAccessWidgetError error) {
   if (delegate_) {
-    // TODO(b/260144025): Reduce the number of times params are cloned.
-    parent_access_ui::mojom::ParentAccessParamsPtr params =
-        delegate_->CloneParentAccessParams();
     base::UmaHistogramEnumeration(
         ParentAccessUIHandlerImpl::
-            GetParentAccessWidgetErrorHistogramForFlowType(params->flow_type),
+            GetParentAccessWidgetErrorHistogramForFlowType(params_->flow_type),
         error);
   }
 
@@ -100,15 +97,13 @@
     ParentAccessUIHandlerDelegate* delegate)
     : identity_manager_(identity_manager),
       delegate_(delegate),
-      receiver_(this, std::move(receiver)) {
+      receiver_(this, std::move(receiver)),
+      params_(delegate_ ? delegate_->CloneParentAccessParams() : nullptr) {
   // ParentAccess state is only tracked when a dialog is created. i.e. not when
   // chrome://parent-access is directly accessed.
   if (delegate_) {
-    // TODO(b/260144025): Reduce the number of times params are cloned.
-    parent_access_ui::mojom::ParentAccessParamsPtr params =
-        delegate_->CloneParentAccessParams();
     state_tracker_ =
-        std::make_unique<ParentAccessStateTracker>(params->flow_type);
+        std::make_unique<ParentAccessStateTracker>(params_->flow_type);
   }
 }
 
@@ -166,8 +161,7 @@
     std::move(callback).Run(parent_access_ui::mojom::ParentAccessParams::New());
     return;
   }
-  // TODO(b/260144025): Reduce the number of times params are cloned.
-  std::move(callback).Run(delegate_->CloneParentAccessParams());
+  std::move(callback).Run(params_->Clone());
   return;
 }
 
@@ -243,16 +237,12 @@
     DCHECK(GURL(url).DomainIs("google.com"));
   }
 
-  // TODO(b/260144025): Reduce the number of times params are cloned.
-  parent_access_ui::mojom::ParentAccessParamsPtr params =
-      delegate_->CloneParentAccessParams();
-
   const GURL base_url(url);
   GURL::Replacements replacements;
   std::string query_string = base::StringPrintf(
       "callerid=%s&hl=%s&platform_version=%s&cros-origin=chrome://"
       "parent-access",
-      GetCallerId(params->flow_type).c_str(), language_code.c_str(),
+      GetCallerId(params_->flow_type).c_str(), language_code.c_str(),
       platform_version.c_str());
   replacements.SetQueryStr(query_string);
   const GURL result = base_url.ReplaceComponents(replacements);
diff --git a/chrome/browser/ui/webui/ash/parent_access/parent_access_ui_handler_impl.h b/chrome/browser/ui/webui/ash/parent_access/parent_access_ui_handler_impl.h
index 4205b97..822a98e 100644
--- a/chrome/browser/ui/webui/ash/parent_access/parent_access_ui_handler_impl.h
+++ b/chrome/browser/ui/webui/ash/parent_access/parent_access_ui_handler_impl.h
@@ -104,6 +104,9 @@
       kids::platform::parentaccess::client::proto::ParentAccessToken>
       parent_access_token_;
 
+  // The params for this instance of the Parent Access UI.
+  const parent_access_ui::mojom::ParentAccessParamsPtr params_;
+
   // Tracks the current state of the webUI, which is used for logging purposes.
   std::unique_ptr<ParentAccessStateTracker> state_tracker_;
 
diff --git a/chrome/browser/ui/webui/settings/ash/internet_section.cc b/chrome/browser/ui/webui/settings/ash/internet_section.cc
index 1f51fe2..1934b00 100644
--- a/chrome/browser/ui/webui/settings/ash/internet_section.cc
+++ b/chrome/browser/ui/webui/settings/ash/internet_section.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/ui/webui/settings/ash/internet_section.h"
 
 #include "ash/constants/ash_features.h"
+#include "ash/public/cpp/hotspot_config_service.h"
 #include "ash/public/cpp/network_config_service.h"
 #include "ash/webui/network_ui/network_health_resource_provider.h"
 #include "ash/webui/network_ui/traffic_counters_resource_provider.h"
@@ -501,6 +502,57 @@
   return *tags;
 }
 
+const std::vector<SearchConcept>& GetHotspotSubpageSearchConcepts() {
+  static const base::NoDestructor<std::vector<SearchConcept>> tags({
+      {IDS_OS_SETTINGS_TAG_HOTSPOT,
+       mojom::kHotspotSubpagePath,
+       mojom::SearchResultIcon::kHotspot,
+       mojom::SearchResultDefaultRank::kMedium,
+       mojom::SearchResultType::kSubpage,
+       {.subpage = mojom::Subpage::kHotspotDetails}},
+  });
+  return *tags;
+}
+
+const std::vector<SearchConcept>& GetHotspotOnSearchConcepts() {
+  static const base::NoDestructor<std::vector<SearchConcept>> tags({
+      {IDS_OS_SETTINGS_TAG_HOTSPOT_TURN_OFF,
+       mojom::kHotspotSubpagePath,
+       mojom::SearchResultIcon::kHotspot,
+       mojom::SearchResultDefaultRank::kMedium,
+       mojom::SearchResultType::kSetting,
+       {.setting = mojom::Setting::kHotspotOnOff}},
+  });
+  return *tags;
+}
+
+const std::vector<SearchConcept>& GetHotspotOffSearchConcepts() {
+  static const base::NoDestructor<std::vector<SearchConcept>> tags({
+      {IDS_OS_SETTINGS_TAG_HOTSPOT_TURN_ON,
+       mojom::kHotspotSubpagePath,
+       mojom::SearchResultIcon::kHotspot,
+       mojom::SearchResultDefaultRank::kMedium,
+       mojom::SearchResultType::kSetting,
+       {.setting = mojom::Setting::kHotspotOnOff}},
+  });
+  return *tags;
+}
+
+const std::vector<SearchConcept>& GetHotspotAutoDisabledSearchConcepts() {
+  static const base::NoDestructor<std::vector<SearchConcept>> tags({
+      {IDS_OS_SETTINGS_TAG_HOTSPOT_AUTO_DISABLED,
+       mojom::kHotspotSubpagePath,
+       mojom::SearchResultIcon::kHotspot,
+       mojom::SearchResultDefaultRank::kMedium,
+       mojom::SearchResultType::kSetting,
+       {.setting = mojom::Setting::kHotspotAutoDisabled},
+       {IDS_OS_SETTINGS_TAG_HOTSPOT_AUTO_DISABLED_ALT1,
+        IDS_OS_SETTINGS_TAG_HOTSPOT_AUTO_DISABLED_ALT2,
+        SearchConcept::kAltTagEnd}},
+  });
+  return *tags;
+}
+
 const std::vector<mojom::Setting>& GetEthernetDetailsSettings() {
   static const base::NoDestructor<std::vector<mojom::Setting>> settings({
       mojom::Setting::kConfigureEthernet,
@@ -543,6 +595,14 @@
   return *settings;
 }
 
+const std::vector<mojom::Setting>& GetHotspotDetailsSettings() {
+  static const base::NoDestructor<std::vector<mojom::Setting>> settings({
+      mojom::Setting::kHotspotOnOff,
+      mojom::Setting::kHotspotAutoDisabled,
+  });
+  return *settings;
+}
+
 const std::vector<mojom::Setting>& GetTetherDetailsSettings() {
   static const base::NoDestructor<std::vector<mojom::Setting>> settings({
       mojom::Setting::kDisconnectTetherNetwork,
@@ -626,11 +686,22 @@
 
   // Receive updates when devices (e.g., Ethernet, Wi-Fi) go on/offline.
   GetNetworkConfigService(cros_network_config_.BindNewPipeAndPassReceiver());
-  cros_network_config_->AddObserver(receiver_.BindNewPipeAndPassRemote());
+  cros_network_config_->AddObserver(
+      network_config_receiver_.BindNewPipeAndPassRemote());
+
+  if (ash::features::IsHotspotEnabled()) {
+    // Receive updates when hotspot info changed.
+    GetHotspotConfigService(cros_hotspot_config_.BindNewPipeAndPassReceiver());
+    cros_hotspot_config_->AddObserver(
+        hotspot_config_receiver_.BindNewPipeAndPassRemote());
+  }
 
   // Fetch initial list of devices and active networks.
   FetchDeviceList();
   FetchNetworkList();
+
+  // Fetch initial hotspot info.
+  FetchHotspotInfo();
 }
 
 InternetSection::~InternetSection() = default;
@@ -1054,8 +1125,11 @@
   // Hotspot details.
   generator->RegisterTopLevelSubpage(
       IDS_SETTINGS_INTERNET_HOTSPOT_DETAILS, mojom::Subpage::kHotspotDetails,
-      mojom::SearchResultIcon::kCellular,
+      mojom::SearchResultIcon::kHotspot,
       mojom::SearchResultDefaultRank::kMedium, mojom::kHotspotSubpagePath);
+  RegisterNestedSettingBulk(mojom::Subpage::kHotspotDetails,
+                            GetHotspotDetailsSettings(), generator);
+  generator->RegisterTopLevelAltSetting(mojom::Setting::kHotspotOnOff);
 
   // APN.
   generator->RegisterNestedSubpage(
@@ -1116,6 +1190,43 @@
   FetchNetworkList();
 }
 
+void InternetSection::OnHotspotInfoChanged() {
+  FetchHotspotInfo();
+}
+
+void InternetSection::FetchHotspotInfo() {
+  if (ash::features::IsHotspotEnabled()) {
+    cros_hotspot_config_->GetHotspotInfo(base::BindOnce(
+        &InternetSection::OnHotspotInfo, base::Unretained(this)));
+  }
+}
+
+void InternetSection::OnHotspotInfo(
+    hotspot_config::mojom::HotspotInfoPtr hotspot_info) {
+  using hotspot_config::mojom::HotspotAllowStatus;
+  using hotspot_config::mojom::HotspotState;
+
+  SearchTagRegistry::ScopedTagUpdater updater = registry()->StartUpdate();
+  updater.RemoveSearchTags(GetHotspotSubpageSearchConcepts());
+  updater.RemoveSearchTags(GetHotspotOnSearchConcepts());
+  updater.RemoveSearchTags(GetHotspotOffSearchConcepts());
+  updater.RemoveSearchTags(GetHotspotAutoDisabledSearchConcepts());
+  if (hotspot_info->allow_status != HotspotAllowStatus::kAllowed) {
+    return;
+  }
+  if (hotspot_info->config) {
+    updater.AddSearchTags(GetHotspotAutoDisabledSearchConcepts());
+  }
+  updater.AddSearchTags(GetHotspotSubpageSearchConcepts());
+
+  if (hotspot_info->state == HotspotState::kEnabled) {
+    updater.AddSearchTags(GetHotspotOnSearchConcepts());
+  }
+  if (hotspot_info->state == HotspotState::kDisabled) {
+    updater.AddSearchTags(GetHotspotOffSearchConcepts());
+  }
+}
+
 void InternetSection::FetchDeviceList() {
   cros_network_config_->GetGlobalPolicy(
       base::BindOnce(&InternetSection::OnGlobalPolicy, base::Unretained(this)));
diff --git a/chrome/browser/ui/webui/settings/ash/internet_section.h b/chrome/browser/ui/webui/settings/ash/internet_section.h
index 53b6b60..156caf1 100644
--- a/chrome/browser/ui/webui/settings/ash/internet_section.h
+++ b/chrome/browser/ui/webui/settings/ash/internet_section.h
@@ -10,6 +10,7 @@
 
 #include "base/values.h"
 #include "chrome/browser/ui/webui/settings/ash/os_settings_section.h"
+#include "chromeos/ash/services/hotspot_config/public/cpp/cros_hotspot_config_observer.h"
 #include "chromeos/services/network_config/public/cpp/cros_network_config_observer.h"
 #include "mojo/public/cpp/bindings/receiver.h"
 #include "mojo/public/cpp/bindings/remote.h"
@@ -26,7 +27,8 @@
 class SearchTagRegistry;
 
 class InternetSection : public OsSettingsSection,
-                        public network_config::CrosNetworkConfigObserver {
+                        public network_config::CrosNetworkConfigObserver,
+                        public hotspot_config::CrosHotspotConfigObserver {
  public:
   InternetSection(Profile* profile, SearchTagRegistry* search_tag_registry);
   ~InternetSection() override;
@@ -53,6 +55,9 @@
           networks) override;
   void OnDeviceStateListChanged() override;
 
+  // hotspot_config::CrosHotspotConfigObserver:
+  void OnHotspotInfoChanged() override;
+
   void FetchDeviceList();
   void OnGlobalPolicy(
       chromeos::network_config::mojom::GlobalPolicyPtr global_policy);
@@ -66,6 +71,9 @@
       std::vector<chromeos::network_config::mojom::NetworkStatePropertiesPtr>
           networks);
 
+  void FetchHotspotInfo();
+  void OnHotspotInfo(hotspot_config::mojom::HotspotInfoPtr hotspot_info);
+
   // Null if no active cellular network exists. The active cellular network
   // corresponds to the currently active SIM slot, and may not be
   // currently connected. A connected cellular network will always be the
@@ -82,9 +90,12 @@
   bool does_ethernet_device_exist_ = false;
 
   mojo::Receiver<chromeos::network_config::mojom::CrosNetworkConfigObserver>
-      receiver_{this};
+      network_config_receiver_{this};
   mojo::Remote<chromeos::network_config::mojom::CrosNetworkConfig>
       cros_network_config_;
+  mojo::Receiver<hotspot_config::mojom::CrosHotspotConfigObserver>
+      hotspot_config_receiver_{this};
+  mojo::Remote<hotspot_config::mojom::CrosHotspotConfig> cros_hotspot_config_;
 };
 
 }  // namespace ash::settings
diff --git a/chrome/browser/ui/webui/settings/ash/os_settings_browser_test_mixin.cc b/chrome/browser/ui/webui/settings/ash/os_settings_browser_test_mixin.cc
index 041bf9e2..7f7270c0 100644
--- a/chrome/browser/ui/webui/settings/ash/os_settings_browser_test_mixin.cc
+++ b/chrome/browser/ui/webui/settings/ash/os_settings_browser_test_mixin.cc
@@ -61,8 +61,8 @@
     content::WebUI* web_ui,
     const GURL& url) {
   auto controller = std::make_unique<OSSettingsUI>(web_ui);
-  content::WebUIDataSource::Add(web_ui->GetWebContents()->GetBrowserContext(),
-                                webui::CreateWebUITestDataSource());
+  webui::CreateAndAddWebUITestDataSource(
+      web_ui->GetWebContents()->GetBrowserContext());
   return controller;
 }
 
diff --git a/chrome/browser/ui/webui/settings/ash/search/search_result_icon.mojom b/chrome/browser/ui/webui/settings/ash/search/search_result_icon.mojom
index 2f14904..161f6a78 100644
--- a/chrome/browser/ui/webui/settings/ash/search/search_result_icon.mojom
+++ b/chrome/browser/ui/webui/settings/ash/search/search_result_icon.mojom
@@ -34,6 +34,7 @@
   kGlobe,
   kGooglePlay,
   kHardDrive,
+  kHotspot,
   kInstantTethering,
   kKeyboard,
   kLaptop,
diff --git a/chrome/browser/ui/webui/settings/chromeos/constants/setting.mojom b/chrome/browser/ui/webui/settings/chromeos/constants/setting.mojom
index 91898de..ba3ef67 100644
--- a/chrome/browser/ui/webui/settings/chromeos/constants/setting.mojom
+++ b/chrome/browser/ui/webui/settings/chromeos/constants/setting.mojom
@@ -39,6 +39,8 @@
   kCellularRemoveESimNetwork = 27,
   kCellularRenameESimNetwork = 28,
   kWifiHidden = 29,
+  kHotspotOnOff = 30,
+  kHotspotAutoDisabled = 31,
 
   // Bluetooth section.
   kBluetoothOnOff = 100,
diff --git a/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
index 1668c91..bbcc731 100644
--- a/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
@@ -837,6 +837,7 @@
     {"offerToEnableTranslateSublabel",
      IDS_SETTINGS_LANGUAGES_OFFER_TO_ENABLE_TRANSLATE_SUBLABEL},
     {"noLanguagesAdded", IDS_SETTINGS_LANGUAGES_NO_LANGUAGES_ADDED},
+    {"addLanguageAriaLabel", IDS_SETTINGS_LANGUAGES_ADD_ARIA_LABEL},
     {"removeLanguageAriaLabel", IDS_SETTINGS_LANGUAGES_REMOVE_ARIA_LABEL},
     {"translatePageTitle", IDS_SETTINGS_TRANSLATE_PAGE_TITLE},
     {"targetLanguageLabel", IDS_SETTINGS_TARGET_TRANSLATE_LABEL},
diff --git a/chrome/browser/web_applications/commands/os_integration_synchronize_command_unittest.cc b/chrome/browser/web_applications/commands/os_integration_synchronize_command_unittest.cc
index 0ffacb28..6222e01f 100644
--- a/chrome/browser/web_applications/commands/os_integration_synchronize_command_unittest.cc
+++ b/chrome/browser/web_applications/commands/os_integration_synchronize_command_unittest.cc
@@ -59,7 +59,7 @@
     {
       base::ScopedAllowBlockingForTesting allow_blocking;
       shortcut_override_ =
-          ShortcutOverrideForTesting::OverrideForTesting(base::GetHomeDir());
+          OsIntegrationTestOverride::OverrideForTesting(base::GetHomeDir());
     }
 
     provider_ = FakeWebAppProvider::Get(profile());
@@ -120,7 +120,7 @@
  private:
   raw_ptr<FakeWebAppProvider> provider_;
   base::test::ScopedFeatureList scoped_feature_list_;
-  std::unique_ptr<ShortcutOverrideForTesting::BlockingRegistration>
+  std::unique_ptr<OsIntegrationTestOverride::BlockingRegistration>
       shortcut_override_;
 };
 
diff --git a/chrome/browser/web_applications/commands/update_protocol_handler_approval_command_browsertest.cc b/chrome/browser/web_applications/commands/update_protocol_handler_approval_command_browsertest.cc
index 1d5ace1..22fa118 100644
--- a/chrome/browser/web_applications/commands/update_protocol_handler_approval_command_browsertest.cc
+++ b/chrome/browser/web_applications/commands/update_protocol_handler_approval_command_browsertest.cc
@@ -45,7 +45,7 @@
     {
       base::ScopedAllowBlockingForTesting allow_blocking;
       shortcut_override_ =
-          ShortcutOverrideForTesting::OverrideForTesting(base::GetHomeDir());
+          OsIntegrationTestOverride::OverrideForTesting(base::GetHomeDir());
     }
     WebAppControllerBrowserTest::SetUpOnMainThread();
   }
@@ -127,7 +127,7 @@
 
  private:
   base::test::ScopedFeatureList scoped_feature_list_;
-  std::unique_ptr<ShortcutOverrideForTesting::BlockingRegistration>
+  std::unique_ptr<OsIntegrationTestOverride::BlockingRegistration>
       shortcut_override_;
 };
 
@@ -153,7 +153,7 @@
 
   if (AreProtocolsRegisteredWithOs()) {
     // Installation registers the protocol handlers.
-    EXPECT_THAT(GetShortcutOverrideForTesting()->protocol_scheme_registrations,
+    EXPECT_THAT(GetOsIntegrationTestOverride()->protocol_scheme_registrations,
                 testing::ElementsAre(std::make_tuple(
                     app_id, std::vector({protocol_handler.protocol}))));
   }
@@ -189,7 +189,7 @@
   if (AreProtocolsRegisteredWithOs()) {
     // Since they were already registered, no work needed to register them
     // again.
-    EXPECT_THAT(GetShortcutOverrideForTesting()->protocol_scheme_registrations,
+    EXPECT_THAT(GetOsIntegrationTestOverride()->protocol_scheme_registrations,
                 testing::ElementsAre(std::make_tuple(
                     app_id, std::vector({protocol_handler.protocol}))));
   }
@@ -230,7 +230,7 @@
   if (AreProtocolsRegisteredWithOs()) {
     // Since they were already registered, no work needed to register them
     // again.
-    EXPECT_THAT(GetShortcutOverrideForTesting()->protocol_scheme_registrations,
+    EXPECT_THAT(GetOsIntegrationTestOverride()->protocol_scheme_registrations,
                 testing::ElementsAre(std::make_tuple(
                     app_id, std::vector({protocol_handler.protocol}))));
   }
@@ -265,7 +265,7 @@
   if (AreProtocolsRegisteredWithOs()) {
     // They should be registered on first install, then removed on disallow.
     EXPECT_THAT(
-        GetShortcutOverrideForTesting()->protocol_scheme_registrations,
+        GetOsIntegrationTestOverride()->protocol_scheme_registrations,
         testing::ElementsAre(
             std::make_tuple(app_id, std::vector({protocol_handler.protocol})),
             std::make_tuple(app_id, std::vector<std::string>())));
@@ -308,7 +308,7 @@
     // the 2nd command run with the same inputs, this should not change because
     // OS integration does not re-run again.
     EXPECT_THAT(
-        GetShortcutOverrideForTesting()->protocol_scheme_registrations,
+        GetOsIntegrationTestOverride()->protocol_scheme_registrations,
         testing::ElementsAre(
             std::make_tuple(app_id, std::vector({protocol_handler.protocol})),
             std::make_tuple(app_id, std::vector<std::string>())));
@@ -352,7 +352,7 @@
   if (AreProtocolsRegisteredWithOs()) {
     // They should be registered on first install, then removed on disallow.
     EXPECT_THAT(
-        GetShortcutOverrideForTesting()->protocol_scheme_registrations,
+        GetOsIntegrationTestOverride()->protocol_scheme_registrations,
         testing::ElementsAre(
             std::make_tuple(app_id, std::vector({protocol_handler.protocol})),
             std::make_tuple(app_id, std::vector<std::string>())));
@@ -397,7 +397,7 @@
 
   if (AreProtocolsRegisteredWithOs()) {
     EXPECT_THAT(
-        GetShortcutOverrideForTesting()->protocol_scheme_registrations,
+        GetOsIntegrationTestOverride()->protocol_scheme_registrations,
         testing::ElementsAre(
             std::make_tuple(app_id, std::vector({protocol_handler.protocol})),
             std::make_tuple(app_id, std::vector<std::string>()),
@@ -446,7 +446,7 @@
   // added back when removed from the disallowed list.
   if (AreProtocolsRegisteredWithOs()) {
     EXPECT_THAT(
-        GetShortcutOverrideForTesting()->protocol_scheme_registrations,
+        GetOsIntegrationTestOverride()->protocol_scheme_registrations,
         testing::ElementsAre(
             std::make_tuple(app_id, std::vector({protocol_handler.protocol})),
             std::make_tuple(app_id, std::vector<std::string>()),
@@ -494,7 +494,7 @@
   // They should be registered on first install and not modified on addition or
   // removal from the allowed list.
   if (AreProtocolsRegisteredWithOs()) {
-    EXPECT_THAT(GetShortcutOverrideForTesting()->protocol_scheme_registrations,
+    EXPECT_THAT(GetOsIntegrationTestOverride()->protocol_scheme_registrations,
                 testing::ElementsAre(std::make_tuple(
                     app_id, std::vector({protocol_handler.protocol}))));
   }
diff --git a/chrome/browser/web_applications/os_integration/os_integration_manager.cc b/chrome/browser/web_applications/os_integration/os_integration_manager.cc
index e540d6c..a5ec5e7c 100644
--- a/chrome/browser/web_applications/os_integration/os_integration_manager.cc
+++ b/chrome/browser/web_applications/os_integration/os_integration_manager.cc
@@ -185,16 +185,16 @@
     url_handler_manager_->SetSubsystems(registrar);
 
   sub_managers_.clear();
-  auto shortcut_handling_sub_manager = std::make_unique<ShortcutSubManager>(
+
+  auto shortcut_sub_manager = std::make_unique<ShortcutSubManager>(
       *profile_, *icon_manager, *registrar);
   auto protocol_handling_sub_manager =
-      std::make_unique<ProtocolHandlingSubManager>(*registrar);
+      std::make_unique<ProtocolHandlingSubManager>(profile_, *registrar);
   auto run_on_os_login_sub_manager =
       std::make_unique<RunOnOsLoginSubManager>(*registrar);
-
   auto uninstallation_via_os_settings_sub_manager =
       std::make_unique<UninstallationViaOsSettingsSubManager>(*registrar);
-  sub_managers_.push_back(std::move(shortcut_handling_sub_manager));
+  sub_managers_.push_back(std::move(shortcut_sub_manager));
   sub_managers_.push_back(std::move(protocol_handling_sub_manager));
   sub_managers_.push_back(std::move(run_on_os_login_sub_manager));
   sub_managers_.push_back(
@@ -823,6 +823,15 @@
     const AppId& app_id,
     bool force_shortcut_updates_if_needed,
     base::OnceClosure callback) {
+  // If the "Execute" step is enabled for sub-managers, then the 'old' os
+  // integration path needs to be turned off so that os integration doesn't get
+  // done twice.
+  if (AreSubManagersExecuteEnabled()) {
+    base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
+        FROM_HERE, std::move(callback));
+    return;
+  }
+
   if (!protocol_handler_manager_) {
     std::move(callback).Run();
     return;
diff --git a/chrome/browser/web_applications/os_integration/protocol_handling_sub_manager.cc b/chrome/browser/web_applications/os_integration/protocol_handling_sub_manager.cc
index b206e8a..ff16c5c8 100644
--- a/chrome/browser/web_applications/os_integration/protocol_handling_sub_manager.cc
+++ b/chrome/browser/web_applications/os_integration/protocol_handling_sub_manager.cc
@@ -6,14 +6,21 @@
 
 #include <memory>
 #include <utility>
+#include <vector>
 
 #include "base/containers/contains.h"
 #include "base/functional/callback_forward.h"
+#include "base/functional/callback_helpers.h"
+#include "base/metrics/histogram_functions.h"
+#include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/web_applications/os_integration/os_integration_sub_manager.h"
+#include "chrome/browser/web_applications/os_integration/web_app_protocol_handler_registration.h"
 #include "chrome/browser/web_applications/proto/web_app_os_integration_state.pb.h"
 #include "chrome/browser/web_applications/web_app.h"
+#include "chrome/browser/web_applications/web_app_constants.h"
 #include "chrome/browser/web_applications/web_app_registrar.h"
 #include "components/services/app_service/public/cpp/protocol_handler_info.h"
+#include "url/gurl.h"
 
 #if BUILDFLAG(IS_WIN)
 #include "base/win/windows_version.h"
@@ -21,9 +28,31 @@
 
 namespace web_app {
 
+namespace {
+
+std::vector<apps::ProtocolHandlerInfo> GetApprovedProtocolHandlers(
+    const proto::WebAppOsIntegrationState& state) {
+  std::vector<apps::ProtocolHandlerInfo> protocol_handlers_info;
+  for (const auto& proto_data : state.protocols_handled().protocols()) {
+    apps::ProtocolHandlerInfo info;
+    info.protocol = proto_data.protocol();
+    info.url = GURL(proto_data.url());
+    protocol_handlers_info.push_back(info);
+  }
+  return protocol_handlers_info;
+}
+
+void RecordProtocolHandlingResult(const std::string histogram_name,
+                                  Result result) {
+  base::UmaHistogramBoolean(histogram_name, (result == Result::kOk));
+}
+
+}  // namespace
+
 ProtocolHandlingSubManager::ProtocolHandlingSubManager(
+    Profile* profile,
     WebAppRegistrar& registrar)
-    : registrar_(registrar) {}
+    : profile_(profile), registrar_(registrar) {}
 
 ProtocolHandlingSubManager::~ProtocolHandlingSubManager() = default;
 
@@ -49,10 +78,7 @@
   }
 
   const WebApp* web_app = registrar_->GetAppById(app_id);
-  if (!web_app) {
-    std::move(configure_done).Run();
-    return;
-  }
+  DCHECK(web_app);
 
   for (const auto& protocol_handler : web_app->protocol_handlers()) {
     if (base::Contains(web_app->disallowed_launch_protocols(),
@@ -78,8 +104,63 @@
     const proto::WebAppOsIntegrationState& desired_state,
     const proto::WebAppOsIntegrationState& current_state,
     base::OnceClosure callback) {
-  // Not implemented yet.
-  std::move(callback).Run();
+  // No-op if both the desired and current states are empty.
+  if (!desired_state.has_protocols_handled() &&
+      !current_state.has_protocols_handled()) {
+    std::move(callback).Run();
+    return;
+  }
+
+  // Handle unregistration case.
+  if (current_state.has_protocols_handled() &&
+      !desired_state.has_protocols_handled()) {
+    UnregisterProtocolHandlersWithOs(
+        app_id, profile_,
+        base::BindOnce(&RecordProtocolHandlingResult,
+                       "WebApp.ProtocolHandlers.Unregistration.Result")
+            .Then(std::move(callback)));
+    return;
+  }
+
+  // Handle registration case.
+  if (!current_state.has_protocols_handled() &&
+      desired_state.has_protocols_handled()) {
+    RegisterProtocolHandlersWithOs(
+        app_id, registrar_->GetAppShortName(app_id), profile_,
+        GetApprovedProtocolHandlers(desired_state),
+        base::BindOnce(&RecordProtocolHandlingResult,
+                       "WebApp.ProtocolHandlers.Registration.Result")
+            .Then(std::move(callback)));
+    return;
+  }
+
+  // If an update is required, then both states should have protocol handling
+  // information.
+  DCHECK(desired_state.has_protocols_handled());
+  DCHECK(current_state.has_protocols_handled());
+
+  // Protocol Handling Diff detection.
+  std::string desired_protocols_handled =
+      desired_state.protocols_handled().SerializeAsString();
+  std::string current_protocols_handled =
+      current_state.protocols_handled().SerializeAsString();
+  if (desired_protocols_handled == current_protocols_handled) {
+    std::move(callback).Run();
+    return;
+  }
+
+  // If either a registration or unregistration is not done, then an update
+  // needs to happen.
+  auto register_and_complete =
+      base::BindOnce(&RegisterProtocolHandlersWithOs, app_id,
+                     registrar_->GetAppShortName(app_id), profile_,
+                     GetApprovedProtocolHandlers(desired_state),
+                     base::BindOnce(&RecordProtocolHandlingResult,
+                                    "WebApp.ProtocolHandlers.Update.Result")
+                         .Then(std::move(callback)));
+  UnregisterProtocolHandlersWithOs(
+      app_id, profile_,
+      base::IgnoreArgs<Result>(std::move(register_and_complete)));
 }
 
 }  // namespace web_app
diff --git a/chrome/browser/web_applications/os_integration/protocol_handling_sub_manager.h b/chrome/browser/web_applications/os_integration/protocol_handling_sub_manager.h
index 5def372b..802f1c6 100644
--- a/chrome/browser/web_applications/os_integration/protocol_handling_sub_manager.h
+++ b/chrome/browser/web_applications/os_integration/protocol_handling_sub_manager.h
@@ -12,13 +12,16 @@
 #include "chrome/browser/web_applications/proto/web_app_os_integration_state.pb.h"
 #include "chrome/browser/web_applications/web_app_id.h"
 
+class Profile;
+
 namespace web_app {
 
 class WebAppRegistrar;
 
 class ProtocolHandlingSubManager : public OsIntegrationSubManager {
  public:
-  explicit ProtocolHandlingSubManager(WebAppRegistrar& registrar);
+  explicit ProtocolHandlingSubManager(Profile* profile,
+                                      WebAppRegistrar& registrar);
   ~ProtocolHandlingSubManager() override;
   void Start() override;
   void Shutdown() override;
@@ -32,6 +35,7 @@
                base::OnceClosure callback) override;
 
  private:
+  const raw_ptr<Profile> profile_;
   const raw_ref<WebAppRegistrar> registrar_;
 
   base::WeakPtrFactory<ProtocolHandlingSubManager> weak_ptr_factory_{this};
diff --git a/chrome/browser/web_applications/os_integration/protocol_handling_sub_manager_unittest.cc b/chrome/browser/web_applications/os_integration/protocol_handling_sub_manager_unittest.cc
index aba1e5e..7b9ad8e 100644
--- a/chrome/browser/web_applications/os_integration/protocol_handling_sub_manager_unittest.cc
+++ b/chrome/browser/web_applications/os_integration/protocol_handling_sub_manager_unittest.cc
@@ -10,6 +10,8 @@
 #include "base/run_loop.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/test/test_future.h"
+#include "build/build_config.h"
+#include "build/chromeos_buildflags.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/web_applications/os_integration/os_integration_manager.h"
 #include "chrome/browser/web_applications/os_integration/web_app_file_handler_manager.h"
@@ -33,44 +35,33 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
 
+#if BUILDFLAG(IS_MAC)
+#include "chrome/browser/web_applications/app_shim_registry_mac.h"
+#endif
+
 namespace web_app {
 
+using ::testing::ElementsAre;
 using ::testing::Eq;
+using ::testing::IsEmpty;
 enum class ApiApprovalState;
 
 namespace {
 
-class ProtocolHandlingSubManagerTest
-    : public WebAppTest,
-      public ::testing::WithParamInterface<OsIntegrationSubManagersState> {
+class ProtocolHandlingSubManagerTestBase : public WebAppTest {
  public:
   const GURL kWebAppUrl = GURL("https://example.com/path/index.html");
 
-  ProtocolHandlingSubManagerTest() = default;
-  ~ProtocolHandlingSubManagerTest() override = default;
+  ProtocolHandlingSubManagerTestBase() = default;
+  ~ProtocolHandlingSubManagerTestBase() override = default;
 
   void SetUp() override {
     WebAppTest::SetUp();
     {
       base::ScopedAllowBlockingForTesting allow_blocking;
       shortcut_override_ =
-          ShortcutOverrideForTesting::OverrideForTesting(base::GetHomeDir());
+          OsIntegrationTestOverride::OverrideForTesting(base::GetHomeDir());
     }
-
-    if (GetParam() == OsIntegrationSubManagersState::kSaveStateToDB) {
-      scoped_feature_list_.InitAndEnableFeatureWithParameters(
-          features::kOsIntegrationSubManagers, {{"stage", "write_config"}});
-    } else if (GetParam() ==
-               OsIntegrationSubManagersState::kSaveStateAndExecute) {
-      scoped_feature_list_.InitAndEnableFeatureWithParameters(
-          features::kOsIntegrationSubManagers,
-          {{"stage", "execute_and_write_config"}});
-    } else {
-      scoped_feature_list_.InitWithFeatures(
-          /*enabled_features=*/{},
-          /*disabled_features=*/{features::kOsIntegrationSubManagers});
-    }
-
     provider_ = FakeWebAppProvider::Get(profile());
 
     auto file_handler_manager =
@@ -128,12 +119,35 @@
 
  private:
   raw_ptr<FakeWebAppProvider> provider_;
-  base::test::ScopedFeatureList scoped_feature_list_;
-  std::unique_ptr<ShortcutOverrideForTesting::BlockingRegistration>
+  std::unique_ptr<OsIntegrationTestOverride::BlockingRegistration>
       shortcut_override_;
 };
 
-TEST_P(ProtocolHandlingSubManagerTest, ConfigureOnlyProtocolHandler) {
+// Synchronize tests only. Tests here should only verify DB updates.
+class ProtocolHandlingSynchronizeTest
+    : public ProtocolHandlingSubManagerTestBase,
+      public ::testing::WithParamInterface<OsIntegrationSubManagersState> {
+ public:
+  ProtocolHandlingSynchronizeTest() = default;
+  ~ProtocolHandlingSynchronizeTest() override = default;
+
+  void SetUp() override {
+    if (GetParam() == OsIntegrationSubManagersState::kSaveStateToDB) {
+      scoped_feature_list_.InitAndEnableFeatureWithParameters(
+          features::kOsIntegrationSubManagers, {{"stage", "write_config"}});
+    } else {
+      scoped_feature_list_.InitWithFeatures(
+          /*enabled_features=*/{},
+          /*disabled_features=*/{features::kOsIntegrationSubManagers});
+    }
+    ProtocolHandlingSubManagerTestBase::SetUp();
+  }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+TEST_P(ProtocolHandlingSynchronizeTest, ConfigureOnlyProtocolHandler) {
   apps::ProtocolHandlerInfo protocol_handler;
   const std::string handler_url =
       std::string(kWebAppUrl.spec()) + "/testing=%s";
@@ -161,7 +175,7 @@
   }
 }
 
-TEST_P(ProtocolHandlingSubManagerTest, UninstalledAppDoesNotConfigure) {
+TEST_P(ProtocolHandlingSynchronizeTest, UninstalledAppDoesNotConfigure) {
   apps::ProtocolHandlerInfo protocol_handler;
   const std::string handler_url =
       std::string(kWebAppUrl.spec()) + "/testing=%s";
@@ -176,7 +190,7 @@
   ASSERT_FALSE(state.has_value());
 }
 
-TEST_P(ProtocolHandlingSubManagerTest, ConfigureProtocolHandlerDisallowed) {
+TEST_P(ProtocolHandlingSynchronizeTest, ConfigureProtocolHandlerDisallowed) {
   apps::ProtocolHandlerInfo protocol_handler1;
   const std::string handler_url1 =
       std::string(kWebAppUrl.spec()) + "/testing=%s";
@@ -220,11 +234,278 @@
 
 INSTANTIATE_TEST_SUITE_P(
     All,
-    ProtocolHandlingSubManagerTest,
+    ProtocolHandlingSynchronizeTest,
     ::testing::Values(OsIntegrationSubManagersState::kSaveStateToDB,
                       OsIntegrationSubManagersState::kDisabled),
     test::GetOsIntegrationSubManagersTestName);
 
+// Synchronize and Execute tests from here onwards. Tests here should
+// verify both DB updates as well as OS registrations/unregistrations.
+class ProtocolHandlingSynchronizeAndExecuteTest
+    : public ProtocolHandlingSubManagerTestBase,
+      public ::testing::WithParamInterface<OsIntegrationSubManagersState> {
+ public:
+  ProtocolHandlingSynchronizeAndExecuteTest() = default;
+  ~ProtocolHandlingSynchronizeAndExecuteTest() override = default;
+
+  void SetUp() override {
+    if (GetParam() == OsIntegrationSubManagersState::kSaveStateAndExecute) {
+      scoped_feature_list_.InitAndEnableFeatureWithParameters(
+          features::kOsIntegrationSubManagers,
+          {{"stage", "execute_and_write_config"}});
+    } else {
+      scoped_feature_list_.InitWithFeatures(
+          /*enabled_features=*/{},
+          /*disabled_features=*/{features::kOsIntegrationSubManagers});
+    }
+    ProtocolHandlingSubManagerTestBase::SetUp();
+  }
+
+#if BUILDFLAG(IS_MAC)
+  std::vector<std::string> GetAppShimRegisteredProtocolHandlers(
+      const AppId& app_id) {
+    std::vector<std::string> protocol_schemes;
+    for (const auto& [file_path, handler] :
+         AppShimRegistry::Get()->GetHandlersForApp(app_id)) {
+      protocol_schemes.insert(protocol_schemes.end(),
+                              handler.protocol_handlers.begin(),
+                              handler.protocol_handlers.end());
+    }
+    return protocol_schemes;
+  }
+#endif  // BUILDFLAG(IS_MAC)
+
+  bool AreProtocolsRegisteredWithOs() {
+#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+    return false;
+#else
+    return true;
+#endif
+  }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+TEST_P(ProtocolHandlingSynchronizeAndExecuteTest, Register) {
+  apps::ProtocolHandlerInfo protocol_handler;
+  const std::string handler_url =
+      std::string(kWebAppUrl.spec()) + "/testing=%s";
+  protocol_handler.url = GURL(handler_url);
+  protocol_handler.protocol = "web+test";
+  const AppId app_id = InstallWebAppWithProtocolHandlers({protocol_handler});
+
+  auto state =
+      provider().registrar_unsafe().GetAppCurrentOsIntegrationState(app_id);
+  ASSERT_TRUE(state.has_value());
+  const proto::WebAppOsIntegrationState& os_integration_state = state.value();
+  if (AreSubManagersExecuteEnabled()) {
+    ASSERT_THAT(os_integration_state.protocols_handled().protocols_size(),
+                testing::Eq(1));
+
+    const proto::ProtocolsHandled::Protocol& protocol_handler_state =
+        os_integration_state.protocols_handled().protocols(0);
+
+    ASSERT_THAT(protocol_handler_state.protocol(),
+                testing::Eq(protocol_handler.protocol));
+    ASSERT_THAT(protocol_handler_state.url(), testing::Eq(handler_url));
+
+#if BUILDFLAG(IS_MAC)
+    EXPECT_THAT(GetAppShimRegisteredProtocolHandlers(app_id),
+                testing::ElementsAre(protocol_handler.protocol));
+#endif
+
+    if (AreProtocolsRegisteredWithOs()) {
+      // Installation registers the protocol handlers.
+      EXPECT_THAT(GetOsIntegrationTestOverride()->protocol_scheme_registrations,
+                  testing::ElementsAre(std::make_tuple(
+                      app_id, std::vector({protocol_handler.protocol}))));
+    }
+  } else {
+    ASSERT_FALSE(os_integration_state.has_protocols_handled());
+  }
+}
+
+TEST_P(ProtocolHandlingSynchronizeAndExecuteTest, Unregister) {
+  apps::ProtocolHandlerInfo protocol_handler;
+  const std::string handler_url =
+      std::string(kWebAppUrl.spec()) + "/testing=%s";
+  protocol_handler.url = GURL(handler_url);
+  protocol_handler.protocol = "web+test";
+  const AppId app_id = InstallWebAppWithProtocolHandlers({protocol_handler});
+  test::UninstallAllWebApps(profile());
+
+  auto state =
+      provider().registrar_unsafe().GetAppCurrentOsIntegrationState(app_id);
+  ASSERT_FALSE(state.has_value());
+
+#if BUILDFLAG(IS_MAC)
+  ASSERT_THAT(GetAppShimRegisteredProtocolHandlers(app_id), testing::IsEmpty());
+#endif
+
+  if (AreProtocolsRegisteredWithOs()) {
+    // TODO(crbug.com/1404819): Update tests to verify protocol handling
+    // unregistration as part of update.
+    // There should only be a single value for registration, as unregistration
+    // is a no-op for GetOsIntegrationTestOverride().
+    ASSERT_THAT(GetOsIntegrationTestOverride()->protocol_scheme_registrations,
+                testing::ElementsAre(std::make_tuple(
+                    app_id, std::vector({protocol_handler.protocol}))));
+  }
+}
+
+TEST_P(ProtocolHandlingSynchronizeAndExecuteTest, UpdateHandlers) {
+  apps::ProtocolHandlerInfo protocol_handler_approved;
+  const std::string handler_url1 =
+      std::string(kWebAppUrl.spec()) + "/testing=%s";
+  protocol_handler_approved.url = GURL(handler_url1);
+  protocol_handler_approved.protocol = "web+test";
+
+  apps::ProtocolHandlerInfo protocol_handler_disapproved;
+  const std::string handler_url2 =
+      std::string(kWebAppUrl.spec()) + "/testing_protocol=%s";
+  protocol_handler_disapproved.url = GURL(handler_url2);
+  protocol_handler_disapproved.protocol = "web+test+protocol";
+
+  const AppId app_id = InstallWebAppWithProtocolHandlers(
+      {protocol_handler_approved, protocol_handler_disapproved});
+  {
+    base::test::TestFuture<void> disallowed_future;
+    provider().scheduler().UpdateProtocolHandlerUserApproval(
+        app_id, protocol_handler_disapproved.protocol,
+        ApiApprovalState::kDisallowed, disallowed_future.GetCallback());
+    EXPECT_TRUE(disallowed_future.Wait());
+  }
+
+  auto state =
+      provider().registrar_unsafe().GetAppCurrentOsIntegrationState(app_id);
+  ASSERT_TRUE(state.has_value());
+  const proto::WebAppOsIntegrationState& os_integration_state = state.value();
+
+  if (AreSubManagersExecuteEnabled()) {
+    ASSERT_THAT(os_integration_state.protocols_handled().protocols_size(),
+                testing::Eq(1));
+
+    const proto::ProtocolsHandled::Protocol& protocol_handler_state =
+        os_integration_state.protocols_handled().protocols(0);
+
+    ASSERT_THAT(protocol_handler_state.protocol(),
+                testing::Eq(protocol_handler_approved.protocol));
+    ASSERT_THAT(protocol_handler_state.url(), testing::Eq(handler_url1));
+
+#if BUILDFLAG(IS_MAC)
+    ASSERT_THAT(GetAppShimRegisteredProtocolHandlers(app_id),
+                testing::ElementsAre(protocol_handler_approved.protocol));
+#endif
+    if (AreProtocolsRegisteredWithOs()) {
+      // TODO(crbug.com/1404819): Update tests to verify protocol handling
+      // unregistration as part of update.
+      ASSERT_THAT(
+          GetOsIntegrationTestOverride()->protocol_scheme_registrations,
+          testing::ElementsAre(
+              std::make_tuple(
+                  app_id, std::vector({protocol_handler_approved.protocol,
+                                       protocol_handler_disapproved.protocol})),
+              std::make_tuple(
+                  app_id, std::vector({protocol_handler_approved.protocol}))));
+    }
+  } else {
+    ASSERT_FALSE(os_integration_state.has_protocols_handled());
+  }
+}
+
+TEST_P(ProtocolHandlingSynchronizeAndExecuteTest, DataEqualNoOp) {
+  apps::ProtocolHandlerInfo protocol_handler;
+  const std::string handler_url =
+      std::string(kWebAppUrl.spec()) + "/testing=%s";
+  protocol_handler.url = GURL(handler_url);
+  protocol_handler.protocol = "web+test";
+
+  const AppId app_id = InstallWebAppWithProtocolHandlers({protocol_handler});
+  {
+    base::test::TestFuture<void> future;
+    provider().scheduler().UpdateProtocolHandlerUserApproval(
+        app_id, protocol_handler.protocol, ApiApprovalState::kAllowed,
+        future.GetCallback());
+    EXPECT_TRUE(future.Wait());
+  }
+
+  auto state =
+      provider().registrar_unsafe().GetAppCurrentOsIntegrationState(app_id);
+  ASSERT_TRUE(state.has_value());
+  const proto::WebAppOsIntegrationState& os_integration_state = state.value();
+
+  if (AreSubManagersExecuteEnabled()) {
+    ASSERT_THAT(os_integration_state.protocols_handled().protocols_size(),
+                testing::Eq(1));
+
+    const proto::ProtocolsHandled::Protocol& protocol_handler_state =
+        os_integration_state.protocols_handled().protocols(0);
+
+    ASSERT_THAT(protocol_handler_state.protocol(),
+                testing::Eq(protocol_handler.protocol));
+    ASSERT_THAT(protocol_handler_state.url(), testing::Eq(handler_url));
+
+#if BUILDFLAG(IS_MAC)
+    ASSERT_THAT(GetAppShimRegisteredProtocolHandlers(app_id),
+                testing::ElementsAre(protocol_handler.protocol));
+#endif
+    if (AreProtocolsRegisteredWithOs()) {
+      ASSERT_THAT(GetOsIntegrationTestOverride()->protocol_scheme_registrations,
+                  testing::ElementsAre(std::make_tuple(
+                      app_id, std::vector({protocol_handler.protocol}))));
+    }
+  } else {
+    ASSERT_FALSE(os_integration_state.has_protocols_handled());
+  }
+}
+
+TEST_P(ProtocolHandlingSynchronizeAndExecuteTest,
+       MultipleSynchronizeEmptyData) {
+  const AppId app_id1 = InstallWebAppWithProtocolHandlers(
+      std::vector<apps::ProtocolHandlerInfo>());
+  const AppId app_id2 = InstallWebAppWithProtocolHandlers(
+      std::vector<apps::ProtocolHandlerInfo>());
+  ASSERT_THAT(app_id1, testing::Eq(app_id2));
+
+  auto state =
+      provider().registrar_unsafe().GetAppCurrentOsIntegrationState(app_id1);
+  ASSERT_TRUE(state.has_value());
+  const proto::WebAppOsIntegrationState& os_integration_state = state.value();
+
+  if (AreSubManagersExecuteEnabled()) {
+    ASSERT_THAT(os_integration_state.protocols_handled().protocols_size(),
+                testing::Eq(0));
+#if BUILDFLAG(IS_MAC)
+    ASSERT_THAT(GetAppShimRegisteredProtocolHandlers(app_id1),
+                testing::IsEmpty());
+#endif
+    if (AreProtocolsRegisteredWithOs()) {
+      // TODO(crbug.com/1404819): Update tests to streamline proper
+      // representation of protocols registered, independent of OSes.
+      // These values are set by the ShortcutHandlingSubManager.
+#if BUILDFLAG(IS_WIN)
+      ASSERT_THAT(GetOsIntegrationTestOverride()->protocol_scheme_registrations,
+                  testing::IsEmpty());
+#else
+      ASSERT_THAT(GetOsIntegrationTestOverride()->protocol_scheme_registrations,
+                  testing::ElementsAre(
+                      std::make_tuple(app_id1, std::vector<std::string>()),
+                      std::make_tuple(app_id1, std::vector<std::string>())));
+#endif
+    }
+  } else {
+    ASSERT_FALSE(os_integration_state.has_protocols_handled());
+  }
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    All,
+    ProtocolHandlingSynchronizeAndExecuteTest,
+    ::testing::Values(OsIntegrationSubManagersState::kSaveStateAndExecute,
+                      OsIntegrationSubManagersState::kDisabled),
+    test::GetOsIntegrationSubManagersTestName);
+
 }  // namespace
 
 }  // namespace web_app
diff --git a/chrome/browser/web_applications/os_integration/run_on_os_login_sub_manager.cc b/chrome/browser/web_applications/os_integration/run_on_os_login_sub_manager.cc
index 468313c..88e7781b 100644
--- a/chrome/browser/web_applications/os_integration/run_on_os_login_sub_manager.cc
+++ b/chrome/browser/web_applications/os_integration/run_on_os_login_sub_manager.cc
@@ -41,13 +41,13 @@
     const AppId& app_id,
     proto::WebAppOsIntegrationState& desired_state,
     base::OnceClosure configure_done) {
-  if (!registrar_->GetAppById(app_id)) {
+  DCHECK(!desired_state.has_run_on_os_login());
+
+  if (!registrar_->IsLocallyInstalled(app_id)) {
     std::move(configure_done).Run();
     return;
   }
 
-  DCHECK(!desired_state.has_run_on_os_login());
-
   proto::RunOnOsLogin* run_on_os_login =
       desired_state.mutable_run_on_os_login();
 
diff --git a/chrome/browser/web_applications/os_integration/run_on_os_login_sub_manager_unittest.cc b/chrome/browser/web_applications/os_integration/run_on_os_login_sub_manager_unittest.cc
index c7eac38..cc3f8156 100644
--- a/chrome/browser/web_applications/os_integration/run_on_os_login_sub_manager_unittest.cc
+++ b/chrome/browser/web_applications/os_integration/run_on_os_login_sub_manager_unittest.cc
@@ -47,7 +47,7 @@
     {
       base::ScopedAllowBlockingForTesting allow_blocking;
       shortcut_override_ =
-          ShortcutOverrideForTesting::OverrideForTesting(base::GetHomeDir());
+          OsIntegrationTestOverride::OverrideForTesting(base::GetHomeDir());
     }
     if (GetParam() == OsIntegrationSubManagersState::kSaveStateToDB) {
       scoped_feature_list_.InitAndEnableFeatureWithParameters(
@@ -129,7 +129,7 @@
  private:
   raw_ptr<FakeWebAppProvider> provider_;
   base::test::ScopedFeatureList scoped_feature_list_;
-  std::unique_ptr<ShortcutOverrideForTesting::BlockingRegistration>
+  std::unique_ptr<OsIntegrationTestOverride::BlockingRegistration>
       shortcut_override_;
 };
 
diff --git a/chrome/browser/web_applications/os_integration/shortcut_sub_manager.cc b/chrome/browser/web_applications/os_integration/shortcut_sub_manager.cc
index 6983f416..e33a4e9 100644
--- a/chrome/browser/web_applications/os_integration/shortcut_sub_manager.cc
+++ b/chrome/browser/web_applications/os_integration/shortcut_sub_manager.cc
@@ -16,6 +16,7 @@
 #include "base/stl_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/web_applications/app_shim_registry_mac.h"
 #include "chrome/browser/web_applications/os_integration/os_integration_manager.h"
 #include "chrome/browser/web_applications/os_integration/web_app_shortcut.h"
 #include "chrome/browser/web_applications/proto/web_app_os_integration_state.pb.h"
@@ -152,6 +153,12 @@
 
   // Second, handle shortcut creation.
   if (desired_state.has_shortcut() && !current_state.has_shortcut()) {
+    // This is required to create the app shim registry for the current profile
+    // on Mac, otherwise updates to the AppShimRegistry do not happen.
+#if BUILDFLAG(IS_MAC)
+    AppShimRegistry::Get()->OnAppInstalledForProfile(app_id,
+                                                     profile_->GetPath());
+#endif
     std::unique_ptr<ShortcutInfo> desired_shortcut_info =
         BuildShortcutInfoWithoutFavicon(
             app_id, registrar_->GetAppStartUrl(app_id), profile_->GetPath(),
@@ -172,11 +179,12 @@
             app_id, registrar_->GetAppStartUrl(app_id), profile_->GetPath(),
             profile_->GetPrefs()->GetString(prefs::kProfileName),
             current_state);
+
     internals::ScheduleDeletePlatformShortcuts(
         shortcut_data_dir, std::move(current_shortcut_info),
-        base::BindOnce([](bool success) {
-          base::UmaHistogramBoolean("WebApp.Shortcuts.Delete.Result", success);
-        }).Then(std::move(callback)));
+        base::BindOnce(&ShortcutSubManager::OnShortcutsDeleted,
+                       weak_ptr_factory_.GetWeakPtr(), app_id,
+                       std::move(callback)));
     return;
   }
 
@@ -286,6 +294,32 @@
       }).Then(std::move(on_complete)));
 }
 
+void ShortcutSubManager::OnShortcutsDeleted(const AppId& app_id,
+                                            base::OnceClosure final_callback,
+                                            bool success) {
+  ResultCallback final_result_callback =
+      base::BindOnce([](Result result) {
+        bool final_success = (result == Result::kOk) ? true : false;
+        base::UmaHistogramBoolean("WebApp.Shortcuts.Delete.Result",
+                                  final_success);
+      }).Then(std::move(final_callback));
+
+#if BUILDFLAG(IS_MAC)
+  bool delete_multi_profile_shortcuts =
+      AppShimRegistry::Get()->OnAppUninstalledForProfile(app_id,
+                                                         profile_->GetPath());
+  if (delete_multi_profile_shortcuts) {
+    internals::ScheduleDeleteMultiProfileShortcutsForApp(
+        app_id, std::move(final_result_callback));
+  } else {
+    std::move(final_result_callback)
+        .Run(success ? Result::kOk : Result::kError);
+  }
+#else
+  std::move(final_result_callback).Run(success ? Result::kOk : Result::kError);
+#endif
+}
+
 void ShortcutSubManager::StoreIconDataFromDisk(
     proto::ShortcutDescription* shortcut,
     base::flat_map<SquareSizePx, base::Time> time_map) {
diff --git a/chrome/browser/web_applications/os_integration/shortcut_sub_manager.h b/chrome/browser/web_applications/os_integration/shortcut_sub_manager.h
index f24a84e4..11a100f 100644
--- a/chrome/browser/web_applications/os_integration/shortcut_sub_manager.h
+++ b/chrome/browser/web_applications/os_integration/shortcut_sub_manager.h
@@ -54,7 +54,9 @@
                       const std::u16string& old_app_title,
                       base::OnceClosure on_complete,
                       std::unique_ptr<ShortcutInfo> shortcut_info);
-
+  void OnShortcutsDeleted(const AppId& app_id,
+                          base::OnceClosure final_callback,
+                          bool success);
   void StoreIconDataFromDisk(proto::ShortcutDescription* shortcut,
                              base::flat_map<SquareSizePx, base::Time> time_map);
 
diff --git a/chrome/browser/web_applications/os_integration/shortcut_sub_manager_browsertest.cc b/chrome/browser/web_applications/os_integration/shortcut_sub_manager_browsertest.cc
index ca01f4d..c1f2e5d 100644
--- a/chrome/browser/web_applications/os_integration/shortcut_sub_manager_browsertest.cc
+++ b/chrome/browser/web_applications/os_integration/shortcut_sub_manager_browsertest.cc
@@ -45,7 +45,7 @@
     {
       base::ScopedAllowBlockingForTesting allow_blocking;
       shortcut_override_ =
-          ShortcutOverrideForTesting::OverrideForTesting(base::GetHomeDir());
+          OsIntegrationTestOverride::OverrideForTesting(base::GetHomeDir());
     }
     WebAppControllerBrowserTest::SetUpOnMainThread();
   }
@@ -101,7 +101,7 @@
   }
 
  private:
-  std::unique_ptr<ShortcutOverrideForTesting::BlockingRegistration>
+  std::unique_ptr<OsIntegrationTestOverride::BlockingRegistration>
       shortcut_override_;
   base::test::ScopedFeatureList scoped_feature_list_;
 };
diff --git a/chrome/browser/web_applications/os_integration/uninstallation_via_os_settings_sub_manager_unittest.cc b/chrome/browser/web_applications/os_integration/uninstallation_via_os_settings_sub_manager_unittest.cc
index 76356b6c..9fcdfb2 100644
--- a/chrome/browser/web_applications/os_integration/uninstallation_via_os_settings_sub_manager_unittest.cc
+++ b/chrome/browser/web_applications/os_integration/uninstallation_via_os_settings_sub_manager_unittest.cc
@@ -43,7 +43,7 @@
     {
       base::ScopedAllowBlockingForTesting allow_blocking;
       shortcut_override_ =
-          ShortcutOverrideForTesting::OverrideForTesting(base::GetHomeDir());
+          OsIntegrationTestOverride::OverrideForTesting(base::GetHomeDir());
     }
     if (GetParam() == OsIntegrationSubManagersState::kSaveStateToDB) {
       scoped_feature_list_.InitAndEnableFeatureWithParameters(
@@ -116,7 +116,7 @@
  private:
   raw_ptr<FakeWebAppProvider> provider_;
   base::test::ScopedFeatureList scoped_feature_list_;
-  std::unique_ptr<ShortcutOverrideForTesting::BlockingRegistration>
+  std::unique_ptr<OsIntegrationTestOverride::BlockingRegistration>
       shortcut_override_;
 };
 
diff --git a/chrome/browser/web_applications/os_integration/web_app_protocol_handler_registration_win.cc b/chrome/browser/web_applications/os_integration/web_app_protocol_handler_registration_win.cc
index ff1699b..92b73ed 100644
--- a/chrome/browser/web_applications/os_integration/web_app_protocol_handler_registration_win.cc
+++ b/chrome/browser/web_applications/os_integration/web_app_protocol_handler_registration_win.cc
@@ -48,13 +48,13 @@
     const std::wstring& app_name_extension) {
   base::AssertLongCPUWorkAllowed();
 
-  if (web_app::GetShortcutOverrideForTesting()) {  // IN-TEST
+  if (web_app::GetOsIntegrationTestOverride()) {  // IN-TEST
     // Instead of modifying the registry, add them to the testing data.
     std::vector<std::string> protocols_registered;
     for (apps::ProtocolHandlerInfo& info : protocol_handlers) {
       protocols_registered.push_back(info.protocol);
     }
-    web_app::GetShortcutOverrideForTesting()
+    web_app::GetOsIntegrationTestOverride()
         ->protocol_scheme_registrations.emplace_back(
             app_id, std::move(protocols_registered));
     return;
@@ -102,7 +102,7 @@
     const base::FilePath& profile_path) {
   base::AssertLongCPUWorkAllowed();
 
-  if (web_app::GetShortcutOverrideForTesting()) {  // IN-TEST
+  if (web_app::GetOsIntegrationTestOverride()) {  // IN-TEST
     // The unregistration is not tested due to complication in the
     // implementation of other OS's. Instead, we check if the updated
     // registrations are empty / don't have the offending protocol.
@@ -140,8 +140,8 @@
     std::vector<apps::ProtocolHandlerInfo> protocol_handlers,
     ResultCallback callback) {
   if (protocol_handlers.empty()) {
-    if (web_app::GetShortcutOverrideForTesting()) {  // IN-TEST
-      web_app::GetShortcutOverrideForTesting()
+    if (web_app::GetOsIntegrationTestOverride()) {  // IN-TEST
+      web_app::GetOsIntegrationTestOverride()
           ->protocol_scheme_registrations.emplace_back(
               app_id, std::vector<std::string>());
     }
diff --git a/chrome/browser/web_applications/os_integration/web_app_run_on_os_login_mac_unittest.mm b/chrome/browser/web_applications/os_integration/web_app_run_on_os_login_mac_unittest.mm
index 90999583..feb299a 100644
--- a/chrome/browser/web_applications/os_integration/web_app_run_on_os_login_mac_unittest.mm
+++ b/chrome/browser/web_applications/os_integration/web_app_run_on_os_login_mac_unittest.mm
@@ -79,7 +79,7 @@
     WebAppTest::SetUp();
     base::mac::SetBaseBundleID(kFakeChromeBundleId);
 
-    override_registration_ = ShortcutOverrideForTesting::OverrideForTesting();
+    override_registration_ = OsIntegrationTestOverride::OverrideForTesting();
     destination_dir_ =
         override_registration_->shortcut_override->chrome_apps_folder.GetPath();
 
@@ -143,7 +143,7 @@
   std::unique_ptr<WebAppAutoLoginUtilMock> auto_login_util_mock_;
   std::unique_ptr<ShortcutInfo> info_;
   base::FilePath shim_path_;
-  std::unique_ptr<ShortcutOverrideForTesting::BlockingRegistration>
+  std::unique_ptr<OsIntegrationTestOverride::BlockingRegistration>
       override_registration_;
 };
 
diff --git a/chrome/browser/web_applications/os_integration/web_app_shortcut.cc b/chrome/browser/web_applications/os_integration/web_app_shortcut.cc
index b5d7e587..06f68f0 100644
--- a/chrome/browser/web_applications/os_integration/web_app_shortcut.cc
+++ b/chrome/browser/web_applications/os_integration/web_app_shortcut.cc
@@ -127,15 +127,14 @@
       FROM_HERE, base::BindOnce(std::move(callback), Result::kOk));
 }
 
-struct ShortcutOverrideForTestingState {
+struct OsIntegrationTestOverrideState {
   base::Lock lock;
-  raw_ptr<ShortcutOverrideForTesting> global_shortcut_override
-      GUARDED_BY(lock) = nullptr;
+  raw_ptr<OsIntegrationTestOverride> global_shortcut_override GUARDED_BY(lock) =
+      nullptr;
 };
 
-ShortcutOverrideForTestingState& GetMutableShortcutOverrideStateForTesting() {
-  static base::NoDestructor<ShortcutOverrideForTestingState>
-      g_shortcut_override;
+OsIntegrationTestOverrideState& GetMutableShortcutOverrideStateForTesting() {
+  static base::NoDestructor<OsIntegrationTestOverrideState> g_shortcut_override;
   return *g_shortcut_override.get();
 }
 
@@ -151,9 +150,9 @@
 
 }  // namespace
 
-ShortcutOverrideForTesting::BlockingRegistration::BlockingRegistration() =
+OsIntegrationTestOverride::BlockingRegistration::BlockingRegistration() =
     default;
-ShortcutOverrideForTesting::BlockingRegistration::~BlockingRegistration() {
+OsIntegrationTestOverride::BlockingRegistration::~BlockingRegistration() {
   base::ScopedAllowBlockingForTesting blocking;
   base::RunLoop wait_until_destruction_loop;
   // Lock the global state.
@@ -180,15 +179,14 @@
 }
 
 // static
-std::unique_ptr<ShortcutOverrideForTesting::BlockingRegistration>
-ShortcutOverrideForTesting::OverrideForTesting(
-    const base::FilePath& base_path) {
+std::unique_ptr<OsIntegrationTestOverride::BlockingRegistration>
+OsIntegrationTestOverride::OverrideForTesting(const base::FilePath& base_path) {
   auto& state = GetMutableShortcutOverrideStateForTesting();
   base::AutoLock state_lock(state.lock);
   DCHECK(!state.global_shortcut_override)
       << "Cannot have multiple registrations at the same time.";
   auto shortcut_override =
-      base::WrapRefCounted(new ShortcutOverrideForTesting(base_path));
+      base::WrapRefCounted(new OsIntegrationTestOverride(base_path));
   state.global_shortcut_override = shortcut_override.get();
 
   std::unique_ptr<BlockingRegistration> registration =
@@ -197,7 +195,7 @@
   return registration;
 }
 
-ShortcutOverrideForTesting::ShortcutOverrideForTesting(
+OsIntegrationTestOverride::OsIntegrationTestOverride(
     const base::FilePath& base_path) {
   // Initialize all directories used. The success & the DCHECK are separated to
   // ensure that these function calls occur on release builds.
@@ -245,7 +243,7 @@
   auto callback =
       base::BindRepeating([](base::FilePath filename, std::string xdg_command,
                              std::string file_contents) {
-        auto shortcut_override = GetShortcutOverrideForTesting();
+        auto shortcut_override = GetOsIntegrationTestOverride();
         DCHECK(shortcut_override);
         LinuxFileRegistration file_registration = LinuxFileRegistration();
         file_registration.xdg_command = xdg_command;
@@ -257,7 +255,7 @@
 #endif
 }
 
-ShortcutOverrideForTesting::~ShortcutOverrideForTesting() {
+OsIntegrationTestOverride::~OsIntegrationTestOverride() {
   std::vector<base::ScopedTempDir*> directories;
 #if BUILDFLAG(IS_WIN)
   directories = {&desktop, &application_menu, &quick_launch, &startup};
@@ -291,7 +289,7 @@
   }
 }
 
-scoped_refptr<ShortcutOverrideForTesting> GetShortcutOverrideForTesting() {
+scoped_refptr<OsIntegrationTestOverride> GetOsIntegrationTestOverride() {
   auto& state = GetMutableShortcutOverrideStateForTesting();
   base::AutoLock state_lock(state.lock);
   return base::WrapRefCounted(state.global_shortcut_override.get());
diff --git a/chrome/browser/web_applications/os_integration/web_app_shortcut.h b/chrome/browser/web_applications/os_integration/web_app_shortcut.h
index a27abf6..d1c34e7 100644
--- a/chrome/browser/web_applications/os_integration/web_app_shortcut.h
+++ b/chrome/browser/web_applications/os_integration/web_app_shortcut.h
@@ -54,27 +54,49 @@
 };
 #endif
 
+// This class is used to help test OS integration code and operations running on
+// trybots. Among other complexities, trybots are often running multiple tests
+// at the same times, so anything that operates in shared OS state could have
+// side effects that this class attempts to solve. (For example, this class
+// makes sure that on Mac, we 'install' the application to a temporary directory
+// to avoid overwriting one from another test).
+//
+// The general rules for adding / using this are:
+// - If the OS integration CAN be fully tested on a trybot, do so. The presence
+//   of this class can allow customization of the integration if needed (e.g.
+//   changing folders).
+//   - If the information 'written' to the OS CAN be easily read back / verified
+//     in a test, then no further work needed, and tests can do this.
+//   - If the information 'written' to the OS CANNOT be easily read back /
+//     verified in a test, then populate metadata in this object about the
+//     final OS call for tests to check.
+// - If the OS integration CANNOT be fully tested on a trybot (it doesn't work
+// or
+//   messes up the environment), then the presence of this object disables the
+//   os integration, and information is populated about the final OS call in
+//   this class.
+//
 // This class is used across multiple different sequenced task runners:
 // - Created on the UI thread.
 // - Accessed & sometimes modified by the shortcut task runner.
 // - Accessed by the UI thread.
 // It is up to the user to ensure thread safety of this struct through
 // ordering guarantees.
-struct ShortcutOverrideForTesting
-    : public base::RefCountedThreadSafe<ShortcutOverrideForTesting> {
+struct OsIntegrationTestOverride
+    : public base::RefCountedThreadSafe<OsIntegrationTestOverride> {
  public:
   // Destroying this class blocks the thread until all users of
-  // GetShortcutOverrideForTesting() have completed.
+  // GetOsIntegrationTestOverride() have completed.
   struct BlockingRegistration {
     BlockingRegistration();
     ~BlockingRegistration();
 
-    scoped_refptr<ShortcutOverrideForTesting> shortcut_override;
+    scoped_refptr<OsIntegrationTestOverride> shortcut_override;
   };
 
-  ShortcutOverrideForTesting(const ShortcutOverrideForTesting&) = delete;
+  OsIntegrationTestOverride(const OsIntegrationTestOverride&) = delete;
 
-  // Overrides applicable directories for shortcut integration and returns an
+  // Overrides applicable directories for OS integration and returns an
   // object that:
   // 1) Contains the directories.
   // 2) Keeps the override active until the object is destroyed.
@@ -82,10 +104,10 @@
   //    hooks are NOT cleanup by the test. This ensures that trybots don't have
   //    old test artifacts on them that can make future tests flaky.
   // All installs that occur during the lifetime of the
-  // ShortcutOverrideForTesting MUST be uninstalled before it is
+  // OsIntegrationTestOverride MUST be uninstalled before it is
   // destroyed.
   // The returned value, on destruction, will block until all usages of the
-  // GetShortcutOverrideForTesting() are destroyed.
+  // GetOsIntegrationTestOverride() are destroyed.
   static std::unique_ptr<BlockingRegistration> OverrideForTesting(
       const base::FilePath& base_path = base::FilePath());
 
@@ -112,13 +134,13 @@
       protocol_scheme_registrations;
 
  private:
-  friend class base::RefCountedThreadSafe<ShortcutOverrideForTesting>;
+  friend class base::RefCountedThreadSafe<OsIntegrationTestOverride>;
 
-  explicit ShortcutOverrideForTesting(const base::FilePath& base_path);
-  ~ShortcutOverrideForTesting();
+  explicit OsIntegrationTestOverride(const base::FilePath& base_path);
+  ~OsIntegrationTestOverride();
 
   // |on_destruction| has it's closure set only once (when BlockingRegistration
-  // is destroyed) and executed when ShortcutOverrideForTesting is destroyed.
+  // is destroyed) and executed when OsIntegrationTestOverride is destroyed.
   // The destructor of BlockingRegistration explicitly sets this closure with a
   // global lock, then destroys the object, then waits on the closure, so it is
   // thread-compatible.
@@ -126,7 +148,7 @@
 };
 
 // Returns an active shortcut override for testing, if there is one.
-scoped_refptr<ShortcutOverrideForTesting> GetShortcutOverrideForTesting();
+scoped_refptr<OsIntegrationTestOverride> GetOsIntegrationTestOverride();
 
 // Represents the info required to create a shortcut for an app.
 struct ShortcutInfo {
diff --git a/chrome/browser/web_applications/os_integration/web_app_shortcut_linux.cc b/chrome/browser/web_applications/os_integration/web_app_shortcut_linux.cc
index 78f8963a..afe24ed 100644
--- a/chrome/browser/web_applications/os_integration/web_app_shortcut_linux.cc
+++ b/chrome/browser/web_applications/os_integration/web_app_shortcut_linux.cc
@@ -59,7 +59,7 @@
 
 base::FilePath GetDesktopPath() {
   base::FilePath desktop_path;
-  auto shortcut_override = web_app::GetShortcutOverrideForTesting();
+  auto shortcut_override = web_app::GetOsIntegrationTestOverride();
   if (shortcut_override)
     return shortcut_override->desktop.GetPath();
   base::PathService::Get(base::DIR_USER_DESKTOP, &desktop_path);
@@ -67,7 +67,7 @@
 }
 
 base::FilePath GetAutostartPath(base::Environment* env) {
-  auto shortcut_override = web_app::GetShortcutOverrideForTesting();
+  auto shortcut_override = web_app::GetOsIntegrationTestOverride();
   if (shortcut_override)
     return shortcut_override->startup.GetPath();
   return AutoStart::GetAutostartDirectory(env);
@@ -256,7 +256,7 @@
                                       const std::string& contents,
                                       const base::FilePath& directory_filename,
                                       const std::string& directory_contents) {
-  DCHECK(!web_app::GetShortcutOverrideForTesting());
+  DCHECK(!web_app::GetOsIntegrationTestOverride());
   base::ScopedTempDir temp_dir;
   if (!temp_dir.CreateUniqueTempDir()) {
     RecordCreateShortcut(CreateShortcutResult::kFailToCreateTempDir);
@@ -387,7 +387,7 @@
     const base::FilePath& shortcut_filename,
     const base::FilePath& directory_filename) {
   // TODO(crbug.com/1276141): Support shortcut testing in Applications Menu.
-  DCHECK(!web_app::GetShortcutOverrideForTesting());
+  DCHECK(!web_app::GetOsIntegrationTestOverride());
   std::vector<std::string> argv;
   argv.push_back("xdg-desktop-menu");
   argv.push_back("uninstall");
@@ -414,8 +414,8 @@
                                                 base::BlockingType::MAY_BLOCK);
   // If this is set, then keeping this as a local variable ensures it is not
   // destroyed while we use it.
-  scoped_refptr<ShortcutOverrideForTesting> shortcut_override =
-      web_app::GetShortcutOverrideForTesting();
+  scoped_refptr<OsIntegrationTestOverride> shortcut_override =
+      web_app::GetOsIntegrationTestOverride();
 
   bool create_shortcut_in_startup = creation_locations.in_startup;
 
@@ -591,8 +591,8 @@
                                                 base::BlockingType::MAY_BLOCK);
   // If this is set, then keeping this as a local variable ensures it is not
   // destroyed while we use it.
-  scoped_refptr<ShortcutOverrideForTesting> shortcut_override =
-      web_app::GetShortcutOverrideForTesting();
+  scoped_refptr<OsIntegrationTestOverride> shortcut_override =
+      web_app::GetOsIntegrationTestOverride();
 
   base::FilePath shortcut_filename =
       GetAppShortcutFilename(profile_path, extension_id);
@@ -622,8 +622,8 @@
                                                 base::BlockingType::MAY_BLOCK);
   // If this is set, then keeping this as a local variable ensures it is not
   // destroyed while we use it.
-  scoped_refptr<ShortcutOverrideForTesting> shortcut_override =
-      web_app::GetShortcutOverrideForTesting();
+  scoped_refptr<OsIntegrationTestOverride> shortcut_override =
+      web_app::GetOsIntegrationTestOverride();
 
   bool result = true;
   // Delete shortcuts from Desktop.
@@ -694,8 +694,8 @@
                                                 base::BlockingType::MAY_BLOCK);
   // If this is set, then keeping this as a local variable ensures it is not
   // destroyed while we use it.
-  scoped_refptr<ShortcutOverrideForTesting> shortcut_override =
-      web_app::GetShortcutOverrideForTesting();
+  scoped_refptr<OsIntegrationTestOverride> shortcut_override =
+      web_app::GetOsIntegrationTestOverride();
 
   std::vector<base::FilePath> shortcut_locations;
   base::FilePath shortcut_filename =
diff --git a/chrome/browser/web_applications/os_integration/web_app_shortcut_mac.mm b/chrome/browser/web_applications/os_integration/web_app_shortcut_mac.mm
index 9585cbc..db647c75 100644
--- a/chrome/browser/web_applications/os_integration/web_app_shortcut_mac.mm
+++ b/chrome/browser/web_applications/os_integration/web_app_shortcut_mac.mm
@@ -219,7 +219,7 @@
   // Because shims created in ~/Applications will not be cleaned up.
   return base::CommandLine::ForCurrentProcess()->HasSwitch(
              switches::kTestType) &&
-         !GetShortcutOverrideForTesting();
+         !GetOsIntegrationTestOverride();
 }
 
 bool AppShimRevealDisabledForTest() {
@@ -732,7 +732,7 @@
 }
 
 base::FilePath GetChromeAppsFolder() {
-  auto override = GetShortcutOverrideForTesting();
+  auto override = GetOsIntegrationTestOverride();
   if (override) {
     if (override->chrome_apps_folder.IsValid())
       return override->chrome_apps_folder.GetPath();
@@ -763,7 +763,7 @@
 
 void WebAppAutoLoginUtil::AddToLoginItems(const base::FilePath& app_bundle_path,
                                           bool hide_on_startup) {
-  auto override = GetShortcutOverrideForTesting();
+  auto override = GetOsIntegrationTestOverride();
   if (override) {
     override->startup_enabled[app_bundle_path] = true;
   } else {
@@ -773,7 +773,7 @@
 
 void WebAppAutoLoginUtil::RemoveFromLoginItems(
     const base::FilePath& app_bundle_path) {
-  auto override = GetShortcutOverrideForTesting();
+  auto override = GetOsIntegrationTestOverride();
   if (override) {
     override->startup_enabled[app_bundle_path] = false;
   } else {
@@ -1228,12 +1228,12 @@
       app_mode::kCFBundleURLSchemesKey : handlers
     } ];
   }
-  if (GetShortcutOverrideForTesting()) {  // IN-TEST
+  if (GetOsIntegrationTestOverride()) {  // IN-TEST
     std::vector<std::string> protocol_handlers_vec;
     protocol_handlers_vec.insert(protocol_handlers_vec.end(),
                                  protocol_handlers.begin(),
                                  protocol_handlers.end());
-    GetShortcutOverrideForTesting()  // IN-TEST
+    GetOsIntegrationTestOverride()  // IN-TEST
         ->protocol_scheme_registrations.emplace_back(
             info_->extension_id, std::move(protocol_handlers_vec));
   }
@@ -1500,8 +1500,8 @@
   // If this is set, then keeping this as a local variable ensures it is not
   // destroyed while we use state from it (retrieved in
   // `GetChromeAppsFolder()`).
-  scoped_refptr<ShortcutOverrideForTesting> shortcut_override =
-      web_app::GetShortcutOverrideForTesting();
+  scoped_refptr<OsIntegrationTestOverride> shortcut_override =
+      web_app::GetOsIntegrationTestOverride();
   if (AppShimCreationDisabledForTest())
     return true;
 
@@ -1516,8 +1516,8 @@
   // If this is set, then keeping this as a local variable ensures it is not
   // destroyed while we use state from it (retrieved in
   // `GetChromeAppsFolder()`).
-  scoped_refptr<ShortcutOverrideForTesting> shortcut_override =
-      web_app::GetShortcutOverrideForTesting();
+  scoped_refptr<OsIntegrationTestOverride> shortcut_override =
+      web_app::GetOsIntegrationTestOverride();
   WebAppShortcutCreator shortcut_creator(
       internals::GetShortcutDataDir(shortcut_info), &shortcut_info);
   ShortcutLocations locations;
@@ -1536,8 +1536,8 @@
   // If this is set, then keeping this as a local variable ensures it is not
   // destroyed while we use state from it (retrieved in
   // `GetChromeAppsFolder()`).
-  scoped_refptr<ShortcutOverrideForTesting> shortcut_override =
-      web_app::GetShortcutOverrideForTesting();
+  scoped_refptr<OsIntegrationTestOverride> shortcut_override =
+      web_app::GetOsIntegrationTestOverride();
   const std::string bundle_id = GetBundleIdentifier(shortcut_info.extension_id,
                                                     shortcut_info.profile_path);
   auto bundle_infos = SearchForBundlesById(bundle_id);
@@ -1558,8 +1558,8 @@
   // If this is set, then keeping this as a local variable ensures it is not
   // destroyed while we use state from it (retrieved in
   // `GetChromeAppsFolder()`).
-  scoped_refptr<ShortcutOverrideForTesting> shortcut_override =
-      web_app::GetShortcutOverrideForTesting();
+  scoped_refptr<OsIntegrationTestOverride> shortcut_override =
+      web_app::GetOsIntegrationTestOverride();
   const std::string bundle_id = GetBundleIdentifier(app_id);
   auto bundle_infos = SearchForBundlesById(bundle_id);
   for (const auto& bundle_info : bundle_infos) {
@@ -1577,8 +1577,8 @@
   // If this is set, then keeping this as a local variable ensures it is not
   // destroyed while we use state from it (retrieved in
   // `GetChromeAppsFolder()`).
-  scoped_refptr<ShortcutOverrideForTesting> shortcut_override =
-      web_app::GetShortcutOverrideForTesting();
+  scoped_refptr<OsIntegrationTestOverride> shortcut_override =
+      web_app::GetOsIntegrationTestOverride();
   if (AppShimLaunchDisabled())
     return Result::kOk;
 
@@ -1601,8 +1601,8 @@
   // If this is set, then keeping this as a local variable ensures it is not
   // destroyed while we use state from it (retrieved in
   // `GetChromeAppsFolder()`).
-  scoped_refptr<ShortcutOverrideForTesting> shortcut_override =
-      web_app::GetShortcutOverrideForTesting();
+  scoped_refptr<OsIntegrationTestOverride> shortcut_override =
+      web_app::GetOsIntegrationTestOverride();
   std::list<BundleInfoPlist> bundles_info = BundleInfoPlist::GetAllInPath(
       GetChromeAppsFolder(), true /* recursive */);
   for (const auto& info : bundles_info) {
diff --git a/chrome/browser/web_applications/os_integration/web_app_shortcut_mac_unittest.mm b/chrome/browser/web_applications/os_integration/web_app_shortcut_mac_unittest.mm
index 66bfc11..804352d 100644
--- a/chrome/browser/web_applications/os_integration/web_app_shortcut_mac_unittest.mm
+++ b/chrome/browser/web_applications/os_integration/web_app_shortcut_mac_unittest.mm
@@ -130,7 +130,7 @@
   void SetUp() override {
     base::mac::SetBaseBundleID(kFakeChromeBundleId);
 
-    override_registration_ = ShortcutOverrideForTesting::OverrideForTesting();
+    override_registration_ = OsIntegrationTestOverride::OverrideForTesting();
     destination_dir_ =
         override_registration_->shortcut_override->chrome_apps_folder.GetPath();
 
@@ -190,7 +190,7 @@
   base::FilePath shim_base_name_;
   base::FilePath shim_path_;
 
-  std::unique_ptr<ShortcutOverrideForTesting::BlockingRegistration>
+  std::unique_ptr<OsIntegrationTestOverride::BlockingRegistration>
       override_registration_;
 };
 
diff --git a/chrome/browser/web_applications/os_integration/web_app_shortcut_manager_mac_unittest.cc b/chrome/browser/web_applications/os_integration/web_app_shortcut_manager_mac_unittest.cc
index 8c71bcfb..a9d5132 100644
--- a/chrome/browser/web_applications/os_integration/web_app_shortcut_manager_mac_unittest.cc
+++ b/chrome/browser/web_applications/os_integration/web_app_shortcut_manager_mac_unittest.cc
@@ -36,7 +36,7 @@
     // Put shortcuts somewhere under the home dir, as otherwise LaunchServices
     // won't be able to find them.
     override_registration_ =
-        ShortcutOverrideForTesting::OverrideForTesting(base::GetHomeDir());
+        OsIntegrationTestOverride::OverrideForTesting(base::GetHomeDir());
 
     provider_ = FakeWebAppProvider::Get(profile());
 
@@ -107,7 +107,7 @@
   const GURL kTestApp2Url = GURL("https://example.com");
 
  private:
-  std::unique_ptr<ShortcutOverrideForTesting::BlockingRegistration>
+  std::unique_ptr<OsIntegrationTestOverride::BlockingRegistration>
       override_registration_;
 
   raw_ptr<FakeWebAppProvider> provider_;
diff --git a/chrome/browser/web_applications/os_integration/web_app_shortcut_win.cc b/chrome/browser/web_applications/os_integration/web_app_shortcut_win.cc
index d24291ce..79b086d 100644
--- a/chrome/browser/web_applications/os_integration/web_app_shortcut_win.cc
+++ b/chrome/browser/web_applications/os_integration/web_app_shortcut_win.cc
@@ -424,7 +424,7 @@
 
   // Calling UnpinShortcuts in unit-tests currently crashes the test, so skip it
   // for now using the shortcut override mechanism.
-  if (web_app::GetShortcutOverrideForTesting()) {
+  if (web_app::GetOsIntegrationTestOverride()) {
     DeleteShortcuts(all_shortcuts, std::move(result_callback));
     return;
   }
@@ -567,8 +567,8 @@
 
   // If this is set, then keeping this as a local variable ensures it is not
   // destroyed while we use state from it (retrieved in `GetShortcutPaths()`).
-  scoped_refptr<ShortcutOverrideForTesting> shortcut_override =
-      web_app::GetShortcutOverrideForTesting();
+  scoped_refptr<OsIntegrationTestOverride> shortcut_override =
+      web_app::GetOsIntegrationTestOverride();
 
   // Shortcut paths under which to create shortcuts.
   std::vector<base::FilePath> shortcut_paths =
@@ -622,8 +622,8 @@
                                                 base::BlockingType::MAY_BLOCK);
   // If this is set, then keeping this as a local variable ensures it is not
   // destroyed while we use state from it (retrieved in `GetShortcutPaths()`).
-  scoped_refptr<ShortcutOverrideForTesting> shortcut_override =
-      web_app::GetShortcutOverrideForTesting();
+  scoped_refptr<OsIntegrationTestOverride> shortcut_override =
+      web_app::GetOsIntegrationTestOverride();
 
   // Update the icon if necessary.
   const base::FilePath icon_file =
@@ -656,8 +656,8 @@
                                                 base::BlockingType::MAY_BLOCK);
   // If this is set, then keeping this as a local variable ensures it is not
   // destroyed while we use state from it (retrieved in `GetShortcutPaths()`).
-  scoped_refptr<ShortcutOverrideForTesting> shortcut_override =
-      web_app::GetShortcutOverrideForTesting();
+  scoped_refptr<OsIntegrationTestOverride> shortcut_override =
+      web_app::GetOsIntegrationTestOverride();
   ShortcutLocations result;
   ShortcutLocations desktop;
   desktop.on_desktop = true;
@@ -732,8 +732,8 @@
                              DeleteShortcutsCallback callback) {
   // If this is set, then keeping this as a local variable ensures it is not
   // destroyed while we use state from it (retrieved in `GetShortcutPaths()`).
-  scoped_refptr<ShortcutOverrideForTesting> shortcut_override =
-      web_app::GetShortcutOverrideForTesting();
+  scoped_refptr<OsIntegrationTestOverride> shortcut_override =
+      web_app::GetOsIntegrationTestOverride();
   GetShortcutLocationsAndDeleteShortcuts(
       web_app_path, shortcut_info.profile_path, shortcut_info.title,
       base::BindOnce(&FinishDeletingPlatformShortcuts, web_app_path,
@@ -756,8 +756,8 @@
                                                 base::BlockingType::MAY_BLOCK);
   // If this is set, then keeping this as a local variable ensures it is not
   // destroyed while we use state from it (retrieved in `GetShortcutPaths()`).
-  scoped_refptr<ShortcutOverrideForTesting> shortcut_override =
-      web_app::GetShortcutOverrideForTesting();
+  scoped_refptr<OsIntegrationTestOverride> shortcut_override =
+      web_app::GetOsIntegrationTestOverride();
   GetShortcutLocationsAndDeleteShortcuts(
       base::FilePath(), profile_path, std::u16string(),
       base::BindOnce(&FinishDeletingAllShortcutsForProfile));
@@ -768,8 +768,8 @@
   // Shortcut paths under which to create shortcuts.
   std::vector<base::FilePath> shortcut_paths;
   // if there is no ShortcutOverrirdeForTesting, set it to empty.
-  scoped_refptr<ShortcutOverrideForTesting> testing_shortcuts =
-      GetShortcutOverrideForTesting();
+  scoped_refptr<OsIntegrationTestOverride> testing_shortcuts =
+      GetOsIntegrationTestOverride();
   // Locations to add to shortcut_paths.
   struct {
     bool use_this_location;
diff --git a/chrome/browser/webauthn/local_credential_management_mac_unittest.mm b/chrome/browser/webauthn/local_credential_management_mac_unittest.mm
index ce15a2b8..d6ea3aa7 100644
--- a/chrome/browser/webauthn/local_credential_management_mac_unittest.mm
+++ b/chrome/browser/webauthn/local_credential_management_mac_unittest.mm
@@ -14,6 +14,7 @@
 #include "device/fido/mac/fake_keychain.h"
 #include "device/fido/public_key_credential_user_entity.h"
 #include "device/fido/test_callback_receiver.h"
+#include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
@@ -157,4 +158,45 @@
   EXPECT_FALSE(std::get<0>(callback.TakeResult()));
 }
 
+class ScopedMockKeychain : device::fido::mac::Keychain {
+ public:
+  ScopedMockKeychain() { SetInstanceOverride(this); }
+  ~ScopedMockKeychain() override { ClearInstanceOverride(); }
+
+  MOCK_METHOD(OSStatus,
+              ItemCopyMatching,
+              (CFDictionaryRef query, CFTypeRef* result),
+              (override));
+};
+
+class MockKeychainLocalCredentialManagementTest : public testing::Test {
+ protected:
+  content::BrowserTaskEnvironment task_environment_;
+  ScopedMockKeychain mock_keychain_;
+  LocalCredentialManagementMac local_cred_man_{
+      {.keychain_access_group = "test-keychain-access-group",
+       .metadata_secret = "TestMetadataSecret"}};
+};
+
+// Regression test for crbug.com/1401342.
+TEST_F(MockKeychainLocalCredentialManagementTest, KeychainError) {
+  EXPECT_CALL(mock_keychain_, ItemCopyMatching)
+      .WillOnce(testing::Return(errSecInternalComponent));
+  device::test::TestCallbackReceiver<bool> callback;
+  local_cred_man_.HasCredentials(callback.callback());
+  callback.WaitForCallback();
+  EXPECT_FALSE(std::get<0>(callback.TakeResult()));
+  testing::Mock::VerifyAndClearExpectations(&mock_keychain_);
+
+  EXPECT_CALL(mock_keychain_, ItemCopyMatching)
+      .WillOnce(testing::Return(errSecInternalComponent));
+  device::test::TestCallbackReceiver<
+      absl::optional<std::vector<device::DiscoverableCredentialMetadata>>>
+      enumerate_callback;
+  local_cred_man_.Enumerate(enumerate_callback.callback());
+  enumerate_callback.WaitForCallback();
+  auto result = std::get<0>(enumerate_callback.TakeResult());
+  EXPECT_FALSE(result.has_value());
+}
+
 }  // namespace
diff --git a/chrome/browser/window_management/DEPS b/chrome/browser/window_management/DEPS
index 733ea6c..8d9e947 100644
--- a/chrome/browser/window_management/DEPS
+++ b/chrome/browser/window_management/DEPS
@@ -1,6 +1,6 @@
 specific_include_rules = {
   "screen_details_browsertest\.cc": [
-    "+content/browser/screen_enumeration/screen_details_test_utils.h",
+    "+content/browser/screen_details/screen_details_test_utils.h",
   ],
   "window_management_browsertest\.cc": [
     "+ash/shell.h",
diff --git a/chrome/browser/window_management/screen_details_browsertest.cc b/chrome/browser/window_management/screen_details_browsertest.cc
index 03393f2..ad7e8419 100644
--- a/chrome/browser/window_management/screen_details_browsertest.cc
+++ b/chrome/browser/window_management/screen_details_browsertest.cc
@@ -8,7 +8,7 @@
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "components/permissions/permission_request_manager.h"
-#include "content/browser/screen_enumeration/screen_details_test_utils.h"
+#include "content/browser/screen_details/screen_details_test_utils.h"
 #include "content/public/browser/render_widget_host_view.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/test/browser_test.h"
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt
index 7f539509..cc7daf02 100644
--- a/chrome/build/mac-arm.pgo.txt
+++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@
-chrome-mac-arm-main-1672919956-2b1784593c97318e28dacfa309632fd23ff201bc.profdata
+chrome-mac-arm-main-1672941398-29982939c7e18d2ee1dfd07e1b1158ea109bdcd4.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index 29bf49ba..db576b7 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1672897988-e85f4ba7ebd8bc06b4c4ef0db809ae6813eea1ff.profdata
+chrome-win32-main-1672930778-c33f699eaa1c33894b387e6404ef944815118f28.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index b80c008b..eb6f0e5 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1672919956-67b0b1a7895193241d4ab68c7a47da7563da32db.profdata
+chrome-win64-main-1672930778-d319be1d8fed4445e2231006eac2e1c78f5d48d7.profdata
diff --git a/chrome/chrome_cleaner/engines/broker/cleaner_sandbox_interface.cc b/chrome/chrome_cleaner/engines/broker/cleaner_sandbox_interface.cc
index 9d5a248..b3555846 100644
--- a/chrome/chrome_cleaner/engines/broker/cleaner_sandbox_interface.cc
+++ b/chrome/chrome_cleaner/engines/broker/cleaner_sandbox_interface.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/chrome_cleaner/engines/broker/cleaner_sandbox_interface.h"
 
+#include <ntstatus.h>
 #include <windows.h>
 
 #include <memory>
diff --git a/chrome/chrome_cleaner/engines/broker/cleaner_sandbox_interface_unittest.cc b/chrome/chrome_cleaner/engines/broker/cleaner_sandbox_interface_unittest.cc
index 36cff74..2274116 100644
--- a/chrome/chrome_cleaner/engines/broker/cleaner_sandbox_interface_unittest.cc
+++ b/chrome/chrome_cleaner/engines/broker/cleaner_sandbox_interface_unittest.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/chrome_cleaner/engines/broker/cleaner_sandbox_interface.h"
 
+#include <ntstatus.h>
+
 #include <limits>
 #include <memory>
 #include <string>
@@ -881,7 +883,7 @@
   // Double check the process is still around.
   DWORD exit_code = 420042;
   EXPECT_EQ(TRUE, ::GetExitCodeProcess(test_process.Handle(), &exit_code));
-  EXPECT_EQ(STILL_ACTIVE, exit_code);
+  EXPECT_EQ(DWORD{STILL_ACTIVE}, exit_code);
 
   // Unprotect the process and kill it.
   process_protector.Release();
@@ -918,7 +920,7 @@
   // Make sure the process is actually still running.
   DWORD exit_code = 4711;
   EXPECT_EQ(TRUE, ::GetExitCodeProcess(test_process.Handle(), &exit_code));
-  EXPECT_EQ(STILL_ACTIVE, exit_code);
+  EXPECT_EQ(DWORD{STILL_ACTIVE}, exit_code);
 
   test_process.Terminate(0, false);
   *command_line = original_command_line;
diff --git a/chrome/chrome_cleaner/engines/broker/scanner_sandbox_interface.cc b/chrome/chrome_cleaner/engines/broker/scanner_sandbox_interface.cc
index 2c42ce04..e276432 100644
--- a/chrome/chrome_cleaner/engines/broker/scanner_sandbox_interface.cc
+++ b/chrome/chrome_cleaner/engines/broker/scanner_sandbox_interface.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/chrome_cleaner/engines/broker/scanner_sandbox_interface.h"
 
+#include <ntstatus.h>
 #include <psapi.h>
 
 #include <memory>
diff --git a/chrome/chrome_cleaner/engines/broker/scanner_sandbox_interface_unittest.cc b/chrome/chrome_cleaner/engines/broker/scanner_sandbox_interface_unittest.cc
index fc8b06e..12cf547 100644
--- a/chrome/chrome_cleaner/engines/broker/scanner_sandbox_interface_unittest.cc
+++ b/chrome/chrome_cleaner/engines/broker/scanner_sandbox_interface_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/chrome_cleaner/engines/broker/scanner_sandbox_interface.h"
 
+#include <ntstatus.h>
 #include <windows.h>
 
 #include <string>
diff --git a/chrome/chrome_cleaner/engines/common/registry_util.cc b/chrome/chrome_cleaner/engines/common/registry_util.cc
index a3a54bb..43da2c8 100644
--- a/chrome/chrome_cleaner/engines/common/registry_util.cc
+++ b/chrome/chrome_cleaner/engines/common/registry_util.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/chrome_cleaner/engines/common/registry_util.h"
 
+#include <ntstatus.h>
+
 #include "base/logging.h"
 #include "base/notreached.h"
 #include "base/numerics/safe_conversions.h"
diff --git a/chrome/chrome_cleaner/engines/common/registry_util_unittest.cc b/chrome/chrome_cleaner/engines/common/registry_util_unittest.cc
index 87a76dc7..5553a73 100644
--- a/chrome/chrome_cleaner/engines/common/registry_util_unittest.cc
+++ b/chrome/chrome_cleaner/engines/common/registry_util_unittest.cc
@@ -4,7 +4,9 @@
 
 #include "chrome/chrome_cleaner/engines/common/registry_util.h"
 
+#include <ntstatus.h>
 #include <windows.h>
+
 #include <algorithm>
 #include <memory>
 #include <sstream>
diff --git a/chrome/chrome_cleaner/engines/target/cleaner_engine_requests_proxy_unittest.cc b/chrome/chrome_cleaner/engines/target/cleaner_engine_requests_proxy_unittest.cc
index a6bb007..d358f66 100644
--- a/chrome/chrome_cleaner/engines/target/cleaner_engine_requests_proxy_unittest.cc
+++ b/chrome/chrome_cleaner/engines/target/cleaner_engine_requests_proxy_unittest.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/chrome_cleaner/engines/target/cleaner_engine_requests_proxy.h"
 
+#include <ntstatus.h>
+
 #include <limits>
 #include <memory>
 #include <string>
diff --git a/chrome/chrome_cleaner/engines/target/engine_requests_proxy_unittest.cc b/chrome/chrome_cleaner/engines/target/engine_requests_proxy_unittest.cc
index cd3d87f..10ebd6a7 100644
--- a/chrome/chrome_cleaner/engines/target/engine_requests_proxy_unittest.cc
+++ b/chrome/chrome_cleaner/engines/target/engine_requests_proxy_unittest.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/chrome_cleaner/engines/target/engine_requests_proxy.h"
 
+#include <ntstatus.h>
+
 #include <limits>
 #include <memory>
 #include <sstream>
diff --git a/chrome/chrome_elf/chrome_elf_security.cc b/chrome/chrome_elf/chrome_elf_security.cc
index 9a5c22bbd1c..ea44babf 100644
--- a/chrome/chrome_elf/chrome_elf_security.cc
+++ b/chrome/chrome_elf/chrome_elf_security.cc
@@ -7,6 +7,7 @@
 #include <windows.h>
 
 #include <assert.h>
+#include <ntstatus.h>
 #include <versionhelpers.h>  // windows.h must be before
 
 #include "base/check.h"
@@ -14,7 +15,6 @@
 #include "base/logging.h"
 #include "base/threading/thread_checker.h"
 #include "base/win/current_module.h"
-
 #include "chrome/chrome_elf/chrome_elf_constants.h"
 #include "chrome/chrome_elf/nt_registry/nt_registry.h"
 #include "chrome/install_static/install_util.h"
diff --git a/chrome/chrome_elf/nt_registry/nt_registry.cc b/chrome/chrome_elf/nt_registry/nt_registry.cc
index 513496df..3452e0f2 100644
--- a/chrome/chrome_elf/nt_registry/nt_registry.cc
+++ b/chrome/chrome_elf/nt_registry/nt_registry.cc
@@ -5,6 +5,7 @@
 #include "chrome/chrome_elf/nt_registry/nt_registry.h"
 
 #include <assert.h>
+#include <ntstatus.h>
 #include <stdlib.h>
 
 #include <memory>
diff --git a/chrome/chrome_elf/third_party_dlls/hook.cc b/chrome/chrome_elf/third_party_dlls/hook.cc
index fd7e38c..a7657dc9 100644
--- a/chrome/chrome_elf/third_party_dlls/hook.cc
+++ b/chrome/chrome_elf/third_party_dlls/hook.cc
@@ -8,6 +8,7 @@
 #include <limits>
 
 #include <assert.h>
+#include <ntstatus.h>
 
 #include "chrome/chrome_elf/crash/crash_helper.h"
 #include "chrome/chrome_elf/hook_util/hook_util.h"
diff --git a/chrome/common/webui_url_constants.cc b/chrome/common/webui_url_constants.cc
index 68a0524..51465f8 100644
--- a/chrome/common/webui_url_constants.cc
+++ b/chrome/common/webui_url_constants.cc
@@ -578,6 +578,7 @@
 // AutocompleteProvider.
 
 const char kAccessibilitySubPage[] = "accessibility";
+const char kAdPrivacySubPage[] = "adPrivacy";
 const char kAddressesSubPage[] = "addresses";
 const char kAppearanceSubPage[] = "appearance";
 const char kAutofillSubPage[] = "autofill";
diff --git a/chrome/common/webui_url_constants.h b/chrome/common/webui_url_constants.h
index b61cb818..4f217a72 100644
--- a/chrome/common/webui_url_constants.h
+++ b/chrome/common/webui_url_constants.h
@@ -504,6 +504,7 @@
 
 // Settings sub-pages.
 extern const char kAccessibilitySubPage[];
+extern const char kAdPrivacySubPage[];
 extern const char kAddressesSubPage[];
 extern const char kAppearanceSubPage[];
 extern const char kAutofillSubPage[];
@@ -535,6 +536,8 @@
 extern const char kSignOutSubPage[];
 extern const char kSyncSetupSubPage[];
 extern const char kTriggeredResetProfileSettingsSubPage[];
+// TODO(crbug.com/1378703): Remove these constants after the feature is
+// launched.
 extern const char kPrivacySandboxAdPersonalizationSubPage[];
 extern const char kPrivacySandboxLearnMoreSubPage[];
 extern const char kPrivacySandboxSubPage[];
diff --git a/chrome/installer/setup/setup_main.cc b/chrome/installer/setup/setup_main.cc
index bb75b6f..1cf37fb 100644
--- a/chrome/installer/setup/setup_main.cc
+++ b/chrome/installer/setup/setup_main.cc
@@ -1562,10 +1562,10 @@
   if (is_uninstall)
     persistent_histogram_storage.Disable();
 
-  // Check to make sure current system is Win7 or later. If not, log
+  // Check to make sure current system is Win10 or later. If not, log
   // error message and get out.
   if (!InstallUtil::IsOSSupported()) {
-    LOG(ERROR) << "Chrome only supports Windows 7 or later.";
+    LOG(ERROR) << "Chrome only supports Windows 10 or later.";
     installer_state.WriteInstallerResult(installer::OS_NOT_SUPPORTED,
                                          IDS_INSTALL_OS_NOT_SUPPORTED_BASE,
                                          nullptr);
diff --git a/chrome/installer/util/install_util.cc b/chrome/installer/util/install_util.cc
index 02d5b2c..8013329 100644
--- a/chrome/installer/util/install_util.cc
+++ b/chrome/installer/util/install_util.cc
@@ -242,10 +242,10 @@
 }
 
 bool InstallUtil::IsOSSupported() {
-  // We do not support anything prior to Windows 7.
+  // We do not support anything prior to Windows 10.
   VLOG(1) << base::SysInfo::OperatingSystemName() << ' '
           << base::SysInfo::OperatingSystemVersion();
-  return base::win::GetVersion() >= base::win::Version::WIN7;
+  return base::win::GetVersion() >= base::win::Version::WIN10;
 }
 
 void InstallUtil::AddInstallerResultItems(bool system_install,
diff --git a/chrome/test/base/test_chrome_web_ui_controller_factory.cc b/chrome/test/base/test_chrome_web_ui_controller_factory.cc
index 2a3abc67..2e6e2f3 100644
--- a/chrome/test/base/test_chrome_web_ui_controller_factory.cc
+++ b/chrome/test/base/test_chrome_web_ui_controller_factory.cc
@@ -68,10 +68,10 @@
 
   // Add an empty callback since managed-footnote always sends this message.
   web_ui->RegisterMessageCallback("observeManagedUI", base::DoNothing());
-  content::WebUIDataSource* source = webui::CreateWebUITestDataSource();
+  content::WebUIDataSource* source =
+      webui::CreateAndAddWebUITestDataSource(profile);
   if (provider)
     provider->DataSourceOverrides(source);
-  content::WebUIDataSource::Add(profile, source);
 
   return controller;
 }
diff --git a/chrome/test/base/web_ui_browser_test.cc b/chrome/test/base/web_ui_browser_test.cc
index d6bcef7c..be25f41 100644
--- a/chrome/test/base/web_ui_browser_test.cc
+++ b/chrome/test/base/web_ui_browser_test.cc
@@ -470,8 +470,7 @@
 
     // Register data sources for chrome://webui-test/ URLs
     // e.g. `chrome://webui-test/chai_assert.js`.
-    content::WebUIDataSource* source = webui::CreateWebUITestDataSource();
-    content::WebUIDataSource::Add(browser()->profile(), source);
+    webui::CreateAndAddWebUITestDataSource(browser()->profile());
   }
 
   test_factory_ = std::make_unique<TestChromeWebUIControllerFactory>();
diff --git a/chrome/test/base/web_ui_test_data_source.cc b/chrome/test/base/web_ui_test_data_source.cc
index 095866c..4f2858d 100644
--- a/chrome/test/base/web_ui_test_data_source.cc
+++ b/chrome/test/base/web_ui_test_data_source.cc
@@ -8,15 +8,17 @@
 #include "chrome/common/webui_url_constants.h"
 #include "chrome/test/data/grit/webui_generated_test_resources.h"
 #include "chrome/test/data/grit/webui_generated_test_resources_map.h"
+#include "content/public/browser/browser_context.h"
 #include "content/public/browser/web_ui_data_source.h"
 #include "services/network/public/mojom/content_security_policy.mojom.h"
 #include "ui/resources/grit/webui_resources.h"
 
 namespace webui {
 
-content::WebUIDataSource* CreateWebUITestDataSource() {
-  content::WebUIDataSource* source =
-      content::WebUIDataSource::Create(chrome::kChromeUIWebUITestHost);
+content::WebUIDataSource* CreateAndAddWebUITestDataSource(
+    content::BrowserContext* browser_context) {
+  content::WebUIDataSource* source = content::WebUIDataSource::CreateAndAdd(
+      browser_context, chrome::kChromeUIWebUITestHost);
 
   webui::EnableTrustedTypesCSP(source);
   source->OverrideContentSecurityPolicy(
diff --git a/chrome/test/base/web_ui_test_data_source.h b/chrome/test/base/web_ui_test_data_source.h
index 8a12f828..5d169cf 100644
--- a/chrome/test/base/web_ui_test_data_source.h
+++ b/chrome/test/base/web_ui_test_data_source.h
@@ -7,10 +7,15 @@
 
 #include "content/public/browser/web_ui_data_source.h"
 
+namespace content {
+class BrowserContext;
+}
+
 namespace webui {
 
 // Creates a data source for for chrome://webui-test/ URLs.
-content::WebUIDataSource* CreateWebUITestDataSource();
+content::WebUIDataSource* CreateAndAddWebUITestDataSource(
+    content::BrowserContext* browser_context);
 
 }  // namespace webui
 
diff --git a/chrome/test/data/policy/policy_test_cases.json b/chrome/test/data/policy/policy_test_cases.json
index d285e76b..c865e7b 100644
--- a/chrome/test/data/policy/policy_test_cases.json
+++ b/chrome/test/data/policy/policy_test_cases.json
@@ -13144,7 +13144,8 @@
       "mac",
       "android",
       "chromeos_ash",
-      "chromeos_lacros"
+      "chromeos_lacros",
+      "fuchsia"
     ],
     "policy_pref_mapping_tests": [
       {
@@ -21093,7 +21094,8 @@
       "mac",
       "chromeos_ash",
       "chromeos_lacros",
-      "android"
+      "android",
+      "fuchsia"
     ],
     "policy_pref_mapping_tests": [
       {
@@ -21298,7 +21300,8 @@
       "mac",
       "android",
       "chromeos_ash",
-      "chromeos_lacros"
+      "chromeos_lacros",
+      "fuchsia"
     ],
     "policy_pref_mapping_tests": [
       {
@@ -21334,7 +21337,8 @@
       "mac",
       "android",
       "chromeos_ash",
-      "chromeos_lacros"
+      "chromeos_lacros",
+      "fuchsia"
     ],
     "policy_pref_mapping_tests": [
       {
diff --git a/chrome/test/data/webui/bookmarks/edit_dialog_test.ts b/chrome/test/data/webui/bookmarks/edit_dialog_test.ts
index 1743aaa..1e3895c 100644
--- a/chrome/test/data/webui/bookmarks/edit_dialog_test.ts
+++ b/chrome/test/data/webui/bookmarks/edit_dialog_test.ts
@@ -2,30 +2,30 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {BookmarksEditDialogElement, normalizeNode} from 'chrome://bookmarks/bookmarks.js';
+import {BookmarksApiProxyImpl, BookmarksEditDialogElement, normalizeNode, setDebouncerForTesting} from 'chrome://bookmarks/bookmarks.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 
+import {TestBookmarksApiProxy} from './test_bookmarks_api_proxy.js';
 import {createFolder, createItem, replaceBody} from './test_util.js';
 
 suite('<bookmarks-edit-dialog>', function() {
+  let bookmarksApi: TestBookmarksApiProxy;
   let dialog: BookmarksEditDialogElement;
   let lastUpdate: {id: string, edit: {url?: string, title?: string}};
-  let lastCreation: chrome.bookmarks.CreateDetails;
 
   suiteSetup(function() {
     chrome.bookmarks.update = function(id, edit) {
       lastUpdate.id = id;
       lastUpdate.edit = edit;
-    };
-    chrome.bookmarks.create = function(node) {
-      lastCreation = node;
+      return Promise.resolve({id: '', title: ''});
     };
   });
 
   setup(function() {
+    bookmarksApi = new TestBookmarksApiProxy();
+    BookmarksApiProxyImpl.setInstance(bookmarksApi);
     lastUpdate = {id: '', edit: {}};
-    lastCreation = {};
     dialog = document.createElement('bookmarks-edit-dialog');
     replaceBody(dialog);
   });
@@ -49,7 +49,7 @@
     assertTrue(dialog.$.url.hidden);
   });
 
-  test('editing passes the correct details to the update', function() {
+  test('editing passes the correct details to the update', async function() {
     // Editing an item without changing anything.
     const item = normalizeNode(
         createItem('1', {url: 'http://website.com', title: 'website'}));
@@ -73,18 +73,22 @@
     assertEquals('Awesome websites', lastUpdate.edit.title);
   });
 
-  test('add passes the correct details to the backend', function() {
+  test('add passes the correct details to the backend', async function() {
     dialog.showAddDialog(false, '1');
 
     dialog.$.name.value = 'Permission Site';
     dialog.$.url.value = 'permission.site';
     flush();
 
+    setDebouncerForTesting();
+
     dialog.$.saveButton.click();
 
-    assertEquals('1', lastCreation.parentId);
-    assertEquals('http://permission.site', lastCreation.url);
-    assertEquals('Permission Site', lastCreation.title);
+    const args = await bookmarksApi.whenCalled('create');
+
+    assertEquals('1', args.parentId);
+    assertEquals('http://permission.site', args.url);
+    assertEquals('Permission Site', args.title);
   });
 
   test('validates urls correctly', function() {
diff --git a/chrome/test/data/webui/chromeos/shortcut_customization/accelerator_edit_view_test.ts b/chrome/test/data/webui/chromeos/shortcut_customization/accelerator_edit_view_test.ts
index db3711ac5..c217d70 100644
--- a/chrome/test/data/webui/chromeos/shortcut_customization/accelerator_edit_view_test.ts
+++ b/chrome/test/data/webui/chromeos/shortcut_customization/accelerator_edit_view_test.ts
@@ -72,25 +72,6 @@
     assertFalse(isVisible(getElementById('cancelButtonContainer')));
   });
 
-  test('LockedAccelerator', async () => {
-    const acceleratorInfo = createUserAcceleratorInfo(
-        Modifier.CONTROL | Modifier.SHIFT,
-        /*key=*/ 71,
-        /*keyDisplay=*/ 'g',
-        /*locked=*/ true);
-
-    editViewElement!.acceleratorInfo = acceleratorInfo;
-    await flush();
-
-    // Check that the edit buttons are not visible.
-    assertFalse(
-        !!editViewElement!.shadowRoot!.querySelector('#editButtonsContainer'));
-    assertFalse(
-        !!editViewElement!.shadowRoot!.querySelector('#cancelButtonContainer'));
-    // Lock icon should be visible.
-    assertTrue(isVisible(getElementById('lockContainer')));
-  });
-
   test('DetectShortcutConflict', async () => {
     const acceleratorInfo = createStandardAcceleratorInfo(
         Modifier.ALT,
diff --git a/chrome/test/data/webui/chromeos/shortcut_customization/accelerator_row_test.ts b/chrome/test/data/webui/chromeos/shortcut_customization/accelerator_row_test.ts
index 9ee6afef..a40d05f 100644
--- a/chrome/test/data/webui/chromeos/shortcut_customization/accelerator_row_test.ts
+++ b/chrome/test/data/webui/chromeos/shortcut_customization/accelerator_row_test.ts
@@ -84,59 +84,6 @@
         'c', keys2[1]!.shadowRoot!.querySelector('#key')!.textContent!.trim());
   });
 
-  test('LockIconVisibleWhenCustomizationEnabled', async () => {
-    loadTimeData.overrideValues({isCustomizationEnabled: true});
-    rowElement = initAcceleratorRowElement();
-    const acceleratorInfo1 = createUserAcceleratorInfo(
-        Modifier.CONTROL | Modifier.SHIFT,
-        /*key=*/ 71,
-        /*keyDisplay=*/ 'g');
-
-    const accelerators = [acceleratorInfo1];
-    const description = 'test shortcut';
-
-    rowElement.acceleratorInfos = accelerators;
-    rowElement.description = description;
-    rowElement.layoutStyle = LayoutStyle.kDefault;
-    rowElement.source = AcceleratorSource.kBrowser;
-    await flushTasks();
-
-    // Expected the lock icon to appear if the source is kBrowser.
-    let lockItemContainer = rowElement!.shadowRoot!.querySelector(
-                                '#lockIconContainer') as HTMLDivElement;
-    assertFalse(lockItemContainer.hidden);
-
-    // Update source to be kAsh, lock icon should no longer appear.
-    rowElement!.source = AcceleratorSource.kAsh;
-    await flushTasks();
-    lockItemContainer = rowElement!.shadowRoot!.querySelector(
-                            '#lockIconContainer') as HTMLDivElement;
-
-    assertTrue(lockItemContainer.hidden);
-  });
-
-  test('LockIconHiddenWhenCustomizationDisabled', async () => {
-    loadTimeData.overrideValues({isCustomizationEnabled: false});
-    rowElement = initAcceleratorRowElement();
-    const acceleratorInfo1 = createUserAcceleratorInfo(
-        Modifier.CONTROL | Modifier.SHIFT,
-        /*key=*/ 71,
-        /*keyDisplay=*/ 'g');
-
-    const accelerators = [acceleratorInfo1];
-    const description = 'test shortcut';
-
-    rowElement.acceleratorInfos = accelerators;
-    rowElement.description = description;
-    rowElement.source = AcceleratorSource.kBrowser;
-    await flushTasks();
-
-    // Expected the lock icon to appear if the source is kBrowser.
-    const lockItemContainer = rowElement.shadowRoot!.querySelector(
-                                  '#lockIconContainer') as HTMLDivElement;
-    assertTrue(lockItemContainer.hidden);
-  });
-
   test('ShowDialogOnClickWhenCustomizationEnabled', async () => {
     loadTimeData.overrideValues({isCustomizationEnabled: true});
     rowElement = initAcceleratorRowElement();
diff --git a/chrome/test/data/webui/chromeos/shortcut_customization/accelerator_view_test.ts b/chrome/test/data/webui/chromeos/shortcut_customization/accelerator_view_test.ts
index 11ae8b00..25fd8b4 100644
--- a/chrome/test/data/webui/chromeos/shortcut_customization/accelerator_view_test.ts
+++ b/chrome/test/data/webui/chromeos/shortcut_customization/accelerator_view_test.ts
@@ -5,16 +5,25 @@
 import 'chrome://shortcut-customization/js/accelerator_view.js';
 import 'chrome://webui-test/mojo_webui_test_support.js';
 
+import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {AcceleratorLookupManager} from 'chrome://shortcut-customization/js/accelerator_lookup_manager.js';
 import {AcceleratorViewElement, ViewState} from 'chrome://shortcut-customization/js/accelerator_view.js';
 import {fakeAcceleratorConfig, fakeLayoutInfo} from 'chrome://shortcut-customization/js/fake_data.js';
 import {InputKeyElement, KeyInputState} from 'chrome://shortcut-customization/js/input_key.js';
 import {AcceleratorSource, Modifier} from 'chrome://shortcut-customization/js/shortcut_types.js';
-import {assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
+import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
+import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
 
 import {createStandardAcceleratorInfo, createUserAcceleratorInfo} from './shortcut_customization_test_util.js';
 
+export function initAcceleratorViewElement(): AcceleratorViewElement {
+  const element = document.createElement('accelerator-view');
+  document.body.appendChild(element);
+  flush();
+  return element;
+}
+
 suite('acceleratorViewTest', function() {
   let viewElement: AcceleratorViewElement|null = null;
 
@@ -24,9 +33,6 @@
     manager = AcceleratorLookupManager.getInstance();
     manager.setAcceleratorLookup(fakeAcceleratorConfig);
     manager.setAcceleratorLayoutLookup(fakeLayoutInfo);
-
-    viewElement = document.createElement('accelerator-view');
-    document.body.appendChild(viewElement);
   });
 
   teardown(() => {
@@ -34,7 +40,9 @@
       manager.reset();
     }
 
-    viewElement!.remove();
+    if (viewElement) {
+      viewElement.remove();
+    }
     viewElement = null;
   });
 
@@ -45,15 +53,17 @@
   }
 
   test('LoadsBasicAccelerator', async () => {
+    viewElement = initAcceleratorViewElement();
+    await flushTasks();
+
     const acceleratorInfo = createUserAcceleratorInfo(
         Modifier.CONTROL | Modifier.SHIFT,
         /*key=*/ 71,
         /*keyDisplay=*/ 'g');
 
-
-    viewElement!.acceleratorInfo = acceleratorInfo;
+    viewElement.acceleratorInfo = acceleratorInfo;
     await flush();
-    const keys = viewElement!.shadowRoot!.querySelectorAll('input-key');
+    const keys = viewElement.shadowRoot!.querySelectorAll('input-key');
     // Three keys: shift, control, g
     assertEquals(3, keys.length);
 
@@ -68,17 +78,20 @@
   });
 
   test('EditableAccelerator', async () => {
+    viewElement = initAcceleratorViewElement();
+    await flushTasks();
+
     const acceleratorInfo = createStandardAcceleratorInfo(
         Modifier.ALT,
         /*key=*/ 221,
         /*keyDisplay=*/ ']');
 
-    viewElement!.acceleratorInfo = acceleratorInfo;
-    viewElement!.source = AcceleratorSource.kAsh;
-    viewElement!.action = 1;
+    viewElement.acceleratorInfo = acceleratorInfo;
+    viewElement.source = AcceleratorSource.kAsh;
+    viewElement.action = 1;
     await flush();
     // Enable the edit view.
-    viewElement!.viewState = ViewState.EDIT;
+    viewElement.viewState = ViewState.EDIT;
 
     await flush();
 
@@ -97,7 +110,7 @@
     assertEquals('key', pendingKey.key);
 
     // Simulate Ctrl + Alt + e.
-    viewElement!.dispatchEvent(new KeyboardEvent('keydown', {
+    viewElement.dispatchEvent(new KeyboardEvent('keydown', {
       key: 'e',
       keyCode: 69,
       code: 'KeyE',
@@ -116,4 +129,107 @@
     assertEquals('alpha-numeric-selected', pendingKey.keyState);
     assertEquals('e', pendingKey.key);
   });
+
+  test('LockIconVisibleWhenCustomizationEnabled', async () => {
+    loadTimeData.overrideValues({isCustomizationEnabled: true});
+    viewElement = initAcceleratorViewElement();
+    await flushTasks();
+    const acceleratorInfo = createStandardAcceleratorInfo(
+        Modifier.CONTROL | Modifier.SHIFT,
+        /*key=*/ 71,
+        /*keyDisplay=*/ 'g');
+
+    viewElement.acceleratorInfo = acceleratorInfo;
+
+    // This set() call is necessary to notify the element that a sub-property
+    // has been updated.
+    viewElement.set('acceleratorInfo.locked', false);
+    viewElement.sourceIsLocked = false;
+    await flushTasks();
+    let lockItemContainer = viewElement.shadowRoot!.querySelector(
+                                '#lockIconContainer') as HTMLDivElement;
+    // If customization is enabled, the acceleratorInfo is not locked, and the
+    // sourceIsLocked property is false, we expect the lock icon to be hidden.
+    assertTrue(lockItemContainer.hidden);
+
+    viewElement.set('acceleratorInfo.locked', true);
+    viewElement.sourceIsLocked = false;
+    await flushTasks();
+    lockItemContainer = viewElement.shadowRoot!.querySelector(
+                            '#lockIconContainer') as HTMLDivElement;
+    // If customization is enabled, the acceleratorInfo is locked, and the
+    // sourceIsLocked property is false, we expect the lock icon to be visible.
+    assertFalse(lockItemContainer.hidden);
+
+    viewElement.set('acceleratorInfo.locked', false);
+    viewElement.sourceIsLocked = true;
+    await flushTasks();
+    lockItemContainer = viewElement.shadowRoot!.querySelector(
+                            '#lockIconContainer') as HTMLDivElement;
+    // If customization is enabled, the acceleratorInfo is not locked, and the
+    // sourceIsLocked property is true, we expect the lock icon to be visible.
+    assertFalse(lockItemContainer.hidden);
+
+    viewElement.set('acceleratorInfo.locked', true);
+    viewElement.sourceIsLocked = true;
+    await flushTasks();
+    lockItemContainer = viewElement.shadowRoot!.querySelector(
+                            '#lockIconContainer') as HTMLDivElement;
+    // If customization is enabled, the acceleratorInfo is locked, and the
+    // sourceIsLocked property is true, we expect the lock icon to be visible.
+    assertFalse(lockItemContainer.hidden);
+  });
+
+  test('LockIconHiddenWhenCustomizationDisabled', async () => {
+    loadTimeData.overrideValues({isCustomizationEnabled: false});
+    viewElement = initAcceleratorViewElement();
+    const acceleratorInfo = createStandardAcceleratorInfo(
+        Modifier.CONTROL | Modifier.SHIFT,
+        /*key=*/ 71,
+        /*keyDisplay=*/ 'g');
+
+    viewElement.acceleratorInfo = acceleratorInfo;
+
+    viewElement.set('acceleratorInfo.locked', false);
+    viewElement.sourceIsLocked = false;
+    await flush();
+
+    let lockItemContainer = viewElement.shadowRoot!.querySelector(
+                                '#lockIconContainer') as HTMLDivElement;
+
+    // If customization is disabled, the lock icon should always be hidden,
+    // regardless of the acceleratorInfo.locked or sourceIsLocked properties.
+    assertTrue(lockItemContainer.hidden);
+
+    viewElement.set('acceleratorInfo.locked', true);
+    viewElement.sourceIsLocked = false;
+    await flush();
+    lockItemContainer = viewElement.shadowRoot!.querySelector(
+                            '#lockIconContainer') as HTMLDivElement;
+
+    // If customization is disabled, the lock icon should always be hidden,
+    // regardless of the acceleratorInfo.locked or sourceIsLocked properties.
+    assertTrue(lockItemContainer.hidden);
+
+    viewElement.set('acceleratorInfo.locked', false);
+    viewElement.sourceIsLocked = true;
+    await flush();
+    lockItemContainer = viewElement.shadowRoot!.querySelector(
+                            '#lockIconContainer') as HTMLDivElement;
+
+    // If customization is disabled, the lock icon should always be hidden,
+    // regardless of the acceleratorInfo.locked or sourceIsLocked properties.
+    assertTrue(lockItemContainer.hidden);
+
+    viewElement.set('acceleratorInfo.locked', true);
+    viewElement.sourceIsLocked = true;
+    await flush();
+    lockItemContainer = viewElement.shadowRoot!.querySelector(
+                            '#lockIconContainer') as HTMLDivElement;
+
+    // If customization is disabled, the lock icon should always be hidden,
+    // regardless of the acceleratorInfo.locked or sourceIsLocked properties.
+    assertTrue(lockItemContainer.hidden);
+  });
+
 });
\ No newline at end of file
diff --git a/chrome/test/data/webui/cr_components/BUILD.gn b/chrome/test/data/webui/cr_components/BUILD.gn
index facb179..7656f98 100644
--- a/chrome/test/data/webui/cr_components/BUILD.gn
+++ b/chrome/test/data/webui/cr_components/BUILD.gn
@@ -67,6 +67,7 @@
     "//ui/webui/resources/cr_components/customize_themes:build_ts",
     "//ui/webui/resources/cr_components/help_bubble:build_ts",
     "//ui/webui/resources/cr_components/history_clusters:build_ts",
+    "//ui/webui/resources/cr_components/localized_link:build_ts",
     "//ui/webui/resources/cr_components/managed_dialog:build_ts",
     "//ui/webui/resources/cr_components/managed_footnote:build_ts",
     "//ui/webui/resources/cr_components/most_visited:build_ts",
diff --git a/chrome/test/data/webui/inline_login/BUILD.gn b/chrome/test/data/webui/inline_login/BUILD.gn
index fc4c6f80..01da8a90 100644
--- a/chrome/test/data/webui/inline_login/BUILD.gn
+++ b/chrome/test/data/webui/inline_login/BUILD.gn
@@ -2,51 +2,37 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import("//tools/grit/preprocess_if_expr.gni")
-import("//tools/typescript/ts_library.gni")
-import("//ui/webui/resources/tools/generate_grd.gni")
+import("../build_webui_tests.gni")
 
 assert(is_chromeos_ash || is_win || is_mac)
 
-preprocessed_folder = "$target_gen_dir/preprocessed"
+build_webui_tests("build") {
+  resource_path_prefix = "inline_login"
 
-test_files = [
-  "inline_login_test.ts",
-  "inline_login_test_util.ts",
-]
-
-if (is_chromeos_ash) {
-  test_files += [
-    "arc_account_picker_page_test.ts",
-    "inline_login_signin_blocked_by_policy_page_test.ts",
-    "inline_login_welcome_page_test.ts",
+  files = [
+    "inline_login_test.ts",
+    "inline_login_test_util.ts",
   ]
-}
 
-preprocess_if_expr("preprocess") {
-  in_folder = "."
-  out_folder = preprocessed_folder
-  out_manifest = "$target_gen_dir/preprocessed_manifest.json"
-  in_files = test_files
-}
+  if (is_chromeos_ash) {
+    files += [
+      "arc_account_picker_page_test.ts",
+      "inline_login_signin_blocked_by_policy_page_test.ts",
+      "inline_login_welcome_page_test.ts",
+    ]
+  }
 
-ts_library("build_ts") {
-  root_dir = preprocessed_folder
-  out_dir = "$target_gen_dir/tsc"
-  tsconfig_base = "tsconfig_base.json"
-  path_mappings = [
+  ts_path_mappings = [
     "chrome://chrome-signin/gaia_auth_host/*|" +
         rebase_path("//chrome/browser/resources/gaia_auth_host/*",
                     target_gen_dir),
     "chrome://chrome-signin/*|" +
         rebase_path("$root_gen_dir/chrome/browser/resources/inline_login/tsc/*",
                     target_gen_dir),
-    "chrome://webui-test/*|" +
-        rebase_path("$root_gen_dir/chrome/test/data/webui/tsc/*",
-                    target_gen_dir),
   ]
+
   if (is_chromeos_ash) {
-    path_mappings += [
+    ts_path_mappings += [
       "chrome://chrome-signin/arc_account_picker/*|" +
           rebase_path(
               "//chrome/browser/resources/chromeos/arc_account_picker/*",
@@ -57,19 +43,5 @@
     ]
   }
 
-  in_files = test_files
-  deps = [
-    "..:build_ts",
-    "//chrome/browser/resources/inline_login:build_ts",
-  ]
-  extra_deps = [ ":preprocess" ]
-}
-
-generate_grd("build_grdp") {
-  grd_prefix = "webui_inline_login"
-  out_grd = "$target_gen_dir/resources.grdp"
-  deps = [ ":build_ts" ]
-  manifest_files =
-      filter_include(get_target_outputs(":build_ts"), [ "*.manifest" ])
-  resource_path_prefix = "inline_login"
+  ts_deps = [ "//chrome/browser/resources/inline_login:build_ts" ]
 }
diff --git a/chrome/test/data/webui/intro/BUILD.gn b/chrome/test/data/webui/intro/BUILD.gn
index 2649d47..b9b70f1e 100644
--- a/chrome/test/data/webui/intro/BUILD.gn
+++ b/chrome/test/data/webui/intro/BUILD.gn
@@ -3,51 +3,21 @@
 # found in the LICENSE file.
 
 import("//components/signin/features.gni")
-import("//tools/typescript/ts_library.gni")
-import("//ui/webui/resources/tools/generate_grd.gni")
+import("../build_webui_tests.gni")
 
 assert(enable_dice_support)
 
-# Test files that do not require preprocessing. If adding // <if expr> to any
-# file below, create a processed_files list and add it to it
-non_preprocessed_files = [
-  "dice_app_test.ts",
-  "sign_in_promo_test.ts",
-  "test_intro_browser_proxy.ts",
-]
-
-preprocessed_folder = "$target_gen_dir/preprocessed"
-
-copy("copy") {
-  sources = non_preprocessed_files
-  outputs = [ "$preprocessed_folder/{{source_target_relative}}" ]
-}
-
-ts_library("build_ts") {
-  root_dir = preprocessed_folder
-  out_dir = "$target_gen_dir/tsc"
-  tsconfig_base = "tsconfig_base.json"
-  path_mappings = [
-    "chrome://intro/*|" +
-        rebase_path("$root_gen_dir/chrome/browser/resources/intro/tsc/*",
-                    target_gen_dir),
-    "chrome://webui-test/*|" +
-        rebase_path("$root_gen_dir/chrome/test/data/webui/tsc/*",
-                    target_gen_dir),
-  ]
-  in_files = non_preprocessed_files
-  deps = [
-    "..:build_ts",
-    "//chrome/browser/resources/intro:build_ts",
-  ]
-  extra_deps = [ ":copy" ]
-}
-
-generate_grd("build_grdp") {
-  grd_prefix = "webui_intro"
-  out_grd = "$target_gen_dir/resources.grdp"
-  deps = [ ":build_ts" ]
-  manifest_files =
-      filter_include(get_target_outputs(":build_ts"), [ "*.manifest" ])
+build_webui_tests("build") {
   resource_path_prefix = "intro"
+
+  files = [
+    "dice_app_test.ts",
+    "sign_in_promo_test.ts",
+    "test_intro_browser_proxy.ts",
+  ]
+
+  ts_path_mappings = [ "chrome://intro/*|" + rebase_path(
+                           "$root_gen_dir/chrome/browser/resources/intro/tsc/*",
+                           target_gen_dir) ]
+  ts_deps = [ "//chrome/browser/resources/intro:build_ts" ]
 }
diff --git a/chrome/test/data/webui/js/BUILD.gn b/chrome/test/data/webui/js/BUILD.gn
index 12a0953..d4f04fd 100644
--- a/chrome/test/data/webui/js/BUILD.gn
+++ b/chrome/test/data/webui/js/BUILD.gn
@@ -2,17 +2,12 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import("//tools/typescript/ts_library.gni")
-import("//ui/webui/resources/tools/generate_grd.gni")
+import("../build_webui_tests.gni")
 
-ts_library("build_ts") {
-  root_dir = "."
-  out_dir = "$target_gen_dir/tsc"
-  tsconfig_base = "tsconfig_base.json"
-  path_mappings = [ "chrome://webui-test/*|" +
-                    rebase_path("$root_gen_dir/chrome/test/data/webui/tsc/*",
-                                target_gen_dir) ]
-  in_files = [
+build_webui_tests("build") {
+  resource_path_prefix = "js"
+
+  files = [
     "color_utils_test.ts",
     "cr_test.ts",
     "custom_element_test.ts",
@@ -24,22 +19,9 @@
     "static_types_test.ts",
     "util_test.ts",
   ]
-
-  definitions = [ "//tools/typescript/definitions/chrome_send.d.ts" ]
-
-  deps = [
-    "..:build_ts",
+  ts_definitions = [ "//tools/typescript/definitions/chrome_send.d.ts" ]
+  ts_deps = [
     "//ui/webui/resources:library",
     "//ui/webui/resources/mojo:library",
   ]
 }
-
-generate_grd("build_grdp") {
-  grd_prefix = "webui_js"
-  out_grd = "$target_gen_dir/resources.grdp"
-
-  deps = [ ":build_ts" ]
-  manifest_files =
-      filter_include(get_target_outputs(":build_ts"), [ "*.manifest" ])
-  resource_path_prefix = "js"
-}
diff --git a/chrome/test/data/webui/media_router/BUILD.gn b/chrome/test/data/webui/media_router/BUILD.gn
index 173a548..f25e433 100644
--- a/chrome/test/data/webui/media_router/BUILD.gn
+++ b/chrome/test/data/webui/media_router/BUILD.gn
@@ -3,40 +3,19 @@
 # found in the LICENSE file.
 
 import("//build/config/chrome_build.gni")
-import("//tools/typescript/ts_library.gni")
-import("//ui/webui/resources/tools/generate_grd.gni")
+import("../build_webui_tests.gni")
 
 assert(!is_android)
 assert(is_chrome_branded)
 
-ts_library("build_ts") {
-  root_dir = "."
-  out_dir = "$target_gen_dir/tsc"
-  tsconfig_base = "tsconfig_base.json"
-  path_mappings = [
-    "chrome://cast-feedback/*|" + rebase_path(
-            "$root_gen_dir/chrome/browser/resources/media_router/cast_feedback/tsc/*",
-            target_gen_dir),
-    "chrome://webui-test/*|" +
-        rebase_path("$root_gen_dir/chrome/test/data/webui/tsc/*",
-                    target_gen_dir),
-  ]
-  in_files = [ "cast_feedback_ui_test.ts" ]
-
-  definitions = [ "//tools/typescript/definitions/feedback_private.d.ts" ]
-
-  deps = [
-    "..:build_ts",
-    "//chrome/browser/resources/media_router/cast_feedback:build_ts",
-  ]
-}
-
-generate_grd("build_grdp") {
-  grd_prefix = "media_router"
-  out_grd = "$target_gen_dir/resources.grdp"
-
-  deps = [ ":build_ts" ]
-  manifest_files =
-      filter_include(get_target_outputs(":build_ts"), [ "*.manifest" ])
+build_webui_tests("build") {
   resource_path_prefix = "media_router"
+
+  files = [ "cast_feedback_ui_test.ts" ]
+
+  ts_path_mappings = [ "chrome://cast-feedback/*|" + rebase_path(
+                           "$root_gen_dir/chrome/browser/resources/media_router/cast_feedback/tsc/*",
+                           target_gen_dir) ]
+  ts_definitions = [ "//tools/typescript/definitions/feedback_private.d.ts" ]
+  ts_deps = [ "//chrome/browser/resources/media_router/cast_feedback:build_ts" ]
 }
diff --git a/chrome/test/data/webui/metrics_internals/BUILD.gn b/chrome/test/data/webui/metrics_internals/BUILD.gn
index fc76b79..c4370ab 100644
--- a/chrome/test/data/webui/metrics_internals/BUILD.gn
+++ b/chrome/test/data/webui/metrics_internals/BUILD.gn
@@ -2,40 +2,20 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import("//tools/typescript/ts_library.gni")
-import("//ui/webui/resources/tools/generate_grd.gni")
+import("../build_webui_tests.gni")
 
-ts_library("build_ts") {
-  root_dir = "."
-  out_dir = "$target_gen_dir/tsc"
-  tsconfig_base = "tsconfig_base.json"
-  path_mappings = [
-    "chrome://metrics-internals/*|" +
-        rebase_path("$root_gen_dir/components/metrics/debug/tsc/*",
-                    target_gen_dir),
-    "chrome://webui-test/*|" +
-        rebase_path("$root_gen_dir/chrome/test/data/webui/tsc/*",
-                    target_gen_dir),
-  ]
+build_webui_tests("build") {
+  resource_path_prefix = "metrics_internals"
 
-  in_files = [
+  files = [
     "no_logs_test.ts",
     "with_log_test.ts",
     "utils.ts",
   ]
 
-  deps = [
-    "..:build_ts",
-    "//components/metrics/debug:build_ts",
-  ]
-}
-
-generate_grd("build_grdp") {
-  grd_prefix = "webui_metrics_internals"
-  out_grd = "$target_gen_dir/resources.grdp"
-
-  deps = [ ":build_ts" ]
-  manifest_files =
-      filter_include(get_target_outputs(":build_ts"), [ "*.manifest" ])
-  resource_path_prefix = "metrics_internals"
+  ts_path_mappings =
+      [ "chrome://metrics-internals/*|" +
+        rebase_path("$root_gen_dir/components/metrics/debug/tsc/*",
+                    target_gen_dir) ]
+  ts_deps = [ "//components/metrics/debug:build_ts" ]
 }
diff --git a/chrome/test/data/webui/mojo/mojo_file_system_access_browsertest.cc b/chrome/test/data/webui/mojo/mojo_file_system_access_browsertest.cc
index e7d1a7a..3d086a29 100644
--- a/chrome/test/data/webui/mojo/mojo_file_system_access_browsertest.cc
+++ b/chrome/test/data/webui/mojo/mojo_file_system_access_browsertest.cc
@@ -56,7 +56,8 @@
   explicit MojoFileSystemAccessUI(content::WebUI* web_ui)
       : ui::MojoWebUIController(web_ui), receiver_(this) {
     content::WebUIDataSource* data_source =
-        content::WebUIDataSource::Create(kTestWebUIHost);
+        content::WebUIDataSource::CreateAndAdd(
+            web_ui->GetWebContents()->GetBrowserContext(), kTestWebUIHost);
     data_source->SetDefaultResource(IDR_MOJO_FILE_SYSTEM_ACCESS_TEST_HTML);
     data_source->DisableContentSecurityPolicy();
     data_source->AddResourcePath(
@@ -64,8 +65,6 @@
         IDR_MOJO_FILE_SYSTEM_ACCESS_TEST_MOJOM_WEBUI_JS);
     data_source->AddResourcePath("mojo_file_system_access_test.js",
                                  IDR_MOJO_FILE_SYSTEM_ACCESS_TEST_JS);
-    content::WebUIDataSource::Add(web_ui->GetWebContents()->GetBrowserContext(),
-                                  data_source);
   }
 
   MojoFileSystemAccessUI(const MojoFileSystemAccessUI&) = delete;
@@ -123,11 +122,10 @@
   explicit OrdinaryMojoWebUI(content::WebUI* web_ui)
       : ui::MojoWebUIController(web_ui) {
     content::WebUIDataSource* data_source =
-        content::WebUIDataSource::Create(kOrdinaryWebUIHost);
+        content::WebUIDataSource::CreateAndAdd(
+            web_ui->GetWebContents()->GetBrowserContext(), kOrdinaryWebUIHost);
     data_source->DisableContentSecurityPolicy();
     data_source->SetDefaultResource(IDR_MOJO_JS_INTERFACE_BROKER_TEST_BUZ_HTML);
-    content::WebUIDataSource::Add(web_ui->GetWebContents()->GetBrowserContext(),
-                                  data_source);
   }
 };
 
diff --git a/chrome/test/data/webui/mojo/mojo_js_interface_broker_browsertest.cc b/chrome/test/data/webui/mojo/mojo_js_interface_broker_browsertest.cc
index 5a8e94a..91ae8e5 100644
--- a/chrome/test/data/webui/mojo/mojo_js_interface_broker_browsertest.cc
+++ b/chrome/test/data/webui/mojo/mojo_js_interface_broker_browsertest.cc
@@ -49,7 +49,8 @@
   explicit FooUI(content::WebUI* web_ui)
       : content::WebUIController(web_ui), foo_receiver_(this) {
     content::WebUIDataSource* data_source =
-        content::WebUIDataSource::Create("foo");
+        content::WebUIDataSource::CreateAndAdd(
+            web_ui->GetWebContents()->GetBrowserContext(), "foo");
     data_source->SetDefaultResource(IDR_MOJO_JS_INTERFACE_BROKER_TEST_FOO_HTML);
     data_source->AddResourcePath("foobar.mojom-webui.js",
                                  IDR_FOOBAR_MOJOM_WEBUI_JS);
@@ -63,8 +64,6 @@
     data_source->OverrideContentSecurityPolicy(
         network::mojom::CSPDirectiveName::ScriptSrc,
         "script-src 'self' chrome://resources/ 'nonce-test';");
-    content::WebUIDataSource::Add(web_ui->GetWebContents()->GetBrowserContext(),
-                                  data_source);
 
     // Allow requesting chrome-untrusted://bar in iframe.
     web_ui->AddRequestableScheme(content::kChromeUIUntrustedScheme);
@@ -94,7 +93,8 @@
   explicit BarUI(content::WebUI* web_ui)
       : ui::UntrustedWebUIController(web_ui), bar_receiver_(this) {
     content::WebUIDataSource* data_source =
-        content::WebUIDataSource::Create(kBarURL);
+        content::WebUIDataSource::CreateAndAdd(
+            web_ui->GetWebContents()->GetBrowserContext(), kBarURL);
     data_source->SetDefaultResource(IDR_MOJO_JS_INTERFACE_BROKER_TEST_BAR_HTML);
 
     // Allow Foo to embed this UI.
@@ -112,8 +112,6 @@
                content::WebUIDataSource::GotDataCallback callback) {
               std::move(callback).Run(nullptr);
             }));
-    content::WebUIDataSource::Add(web_ui->GetWebContents()->GetBrowserContext(),
-                                  data_source);
   }
 
   void BindInterface(mojo::PendingReceiver<::test::mojom::Bar> receiver) {
@@ -140,10 +138,9 @@
   explicit BuzUI(content::WebUI* web_ui)
       : ui::UntrustedWebUIController(web_ui) {
     content::WebUIDataSource* data_source =
-        content::WebUIDataSource::Create(kBuzURL);
+        content::WebUIDataSource::CreateAndAdd(
+            web_ui->GetWebContents()->GetBrowserContext(), kBuzURL);
     data_source->SetDefaultResource(IDR_MOJO_JS_INTERFACE_BROKER_TEST_BUZ_HTML);
-    content::WebUIDataSource::Add(web_ui->GetWebContents()->GetBrowserContext(),
-                                  data_source);
   }
 };
 
diff --git a/chrome/test/data/webui/mojo/mojo_web_ui_controller_browsertest.cc b/chrome/test/data/webui/mojo/mojo_web_ui_controller_browsertest.cc
index a76bacbd..4d23082 100644
--- a/chrome/test/data/webui/mojo/mojo_web_ui_controller_browsertest.cc
+++ b/chrome/test/data/webui/mojo/mojo_web_ui_controller_browsertest.cc
@@ -41,14 +41,13 @@
   explicit FooUI(content::WebUI* web_ui)
       : ui::MojoWebUIController(web_ui), foo_receiver_(this) {
     content::WebUIDataSource* data_source =
-        content::WebUIDataSource::Create("foo");
+        content::WebUIDataSource::CreateAndAdd(
+            web_ui->GetWebContents()->GetBrowserContext(), "foo");
     data_source->SetDefaultResource(IDR_MOJO_WEB_UI_CONTROLLER_TEST_HTML);
     data_source->DisableContentSecurityPolicy();
     data_source->AddResourcePath("foobar.mojom-webui.js",
                                  IDR_FOOBAR_MOJOM_WEBUI_JS);
     data_source->AddResourcePath("main.js", IDR_MOJO_MAIN_JS);
-    content::WebUIDataSource::Add(web_ui->GetWebContents()->GetBrowserContext(),
-                                  data_source);
   }
 
   FooUI(const FooUI&) = delete;
@@ -81,14 +80,13 @@
         foo_receiver_(this),
         bar_receiver_(this) {
     content::WebUIDataSource* data_source =
-        content::WebUIDataSource::Create("foobar");
+        content::WebUIDataSource::CreateAndAdd(
+            web_ui->GetWebContents()->GetBrowserContext(), "foobar");
     data_source->SetDefaultResource(IDR_MOJO_WEB_UI_CONTROLLER_TEST_HTML);
     data_source->DisableContentSecurityPolicy();
     data_source->AddResourcePath("foobar.mojom-webui.js",
                                  IDR_FOOBAR_MOJOM_WEBUI_JS);
     data_source->AddResourcePath("main.js", IDR_MOJO_MAIN_JS);
-    content::WebUIDataSource::Add(web_ui->GetWebContents()->GetBrowserContext(),
-                                  data_source);
   }
 
   FooBarUI(const FooBarUI&) = delete;
diff --git a/chrome/test/data/webui/settings/chromeos/os_a11y_page_tests.js b/chrome/test/data/webui/settings/chromeos/os_a11y_page_tests.js
index 68f897c7..c9b1da1d 100644
--- a/chrome/test/data/webui/settings/chromeos/os_a11y_page_tests.js
+++ b/chrome/test/data/webui/settings/chromeos/os_a11y_page_tests.js
@@ -2,8 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import 'chrome://os-settings/chromeos/lazy_load.js';
-
 import {CrSettingsPrefs, OsA11yPageBrowserProxyImpl, Router, routes} from 'chrome://os-settings/chromeos/os_settings.js';
 import {getDeepActiveElement} from 'chrome://resources/ash/common/util.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
diff --git a/chrome/test/data/webui/welcome/test_bookmark_proxy.ts b/chrome/test/data/webui/welcome/test_bookmark_proxy.ts
index c9a9183..c5499f4b 100644
--- a/chrome/test/data/webui/welcome/test_bookmark_proxy.ts
+++ b/chrome/test/data/webui/welcome/test_bookmark_proxy.ts
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
-import {AddBookmarkCallback, BookmarkData, BookmarkProxy} from 'chrome://welcome/shared/bookmark_proxy.js';
+import {BookmarkData, BookmarkProxy} from 'chrome://welcome/shared/bookmark_proxy.js';
 
 export class TestBookmarkProxy extends TestBrowserProxy implements
     BookmarkProxy {
@@ -18,9 +18,9 @@
     ]);
   }
 
-  addBookmark(data: BookmarkData, callback: AddBookmarkCallback) {
+  addBookmark(data: BookmarkData) {
     this.methodCalled('addBookmark', data);
-    callback({
+    return Promise.resolve({
       id: (this.fakeBookmarkId_++).toString(),
       title: '',
     });
diff --git a/chrome/updater/app/server/win/com_classes_legacy_unittest.cc b/chrome/updater/app/server/win/com_classes_legacy_unittest.cc
index 984b295b..ff173e59 100644
--- a/chrome/updater/app/server/win/com_classes_legacy_unittest.cc
+++ b/chrome/updater/app/server/win/com_classes_legacy_unittest.cc
@@ -192,16 +192,6 @@
   base::CommandLine command_line =
       GetTestProcessCommandLine(GetTestScope(), test::GetTestName());
 
-  const std::wstring event_name =
-      base::StrCat({kTestProcessExecutableName, L"-",
-                    base::NumberToWString(::GetCurrentProcessId())});
-  NamedObjectAttributes attr =
-      GetNamedObjectAttributes(event_name.c_str(), GetTestScope());
-
-  base::WaitableEvent event(base::win::ScopedHandle(
-      ::CreateEvent(&attr.sa, FALSE, FALSE, attr.name.c_str())));
-  ASSERT_NE(event.handle(), nullptr);
-
   command_line.AppendSwitchNative(kTestEventToWaitOn, L"%1");
   command_line.AppendSwitchNative(kTestExitCode, L"%2");
 
@@ -210,8 +200,10 @@
       command_line.GetCommandLineStringWithUnsafeInsertSequences(),
       app_command_web));
 
+  test::EventHolder event_holder(test::CreateWaitableEventForTest());
+
   ASSERT_HRESULT_SUCCEEDED(app_command_web->execute(
-      base::win::ScopedVariant(attr.name.c_str()),
+      base::win::ScopedVariant(event_holder.name.c_str()),
       base::win::ScopedVariant(L"999"), base::win::ScopedVariant::kEmptyVariant,
       base::win::ScopedVariant::kEmptyVariant,
       base::win::ScopedVariant::kEmptyVariant,
@@ -224,7 +216,7 @@
   EXPECT_HRESULT_SUCCEEDED(app_command_web->get_status(&status));
   EXPECT_EQ(status, COMMAND_STATUS_RUNNING);
 
-  event.Signal();
+  event_holder.event.Signal();
 
   WaitForUpdateCompletion(app_command_web);
 
diff --git a/chrome/updater/util/unittest_util.cc b/chrome/updater/util/unittest_util.cc
index 2434b55..0c7c1a5 100644
--- a/chrome/updater/util/unittest_util.cc
+++ b/chrome/updater/util/unittest_util.cc
@@ -18,14 +18,17 @@
 #include "base/process/launch.h"
 #include "base/process/process_iterator.h"
 #include "base/strings/strcat.h"
+#include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/synchronization/waitable_event.h"
 #include "base/threading/platform_thread.h"
 #include "base/time/time.h"
 #include "chrome/updater/constants.h"
 #include "chrome/updater/policy/manager.h"
 #include "chrome/updater/policy/service.h"
+#include "chrome/updater/test_scope.h"
 #include "chrome/updater/updater_scope.h"
 #include "chrome/updater/util/util.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -35,6 +38,7 @@
 #include <shlobj.h>
 
 #include "base/strings/string_number_conversions_win.h"
+#include "base/win/scoped_handle.h"
 #include "base/win/windows_version.h"
 #include "chrome/test/base/process_inspector_win.h"
 #include "chrome/updater/util/win_util.h"
@@ -408,6 +412,14 @@
   return message + demarcation;
 }
 
+EventHolder CreateWaitableEventForTest() {
+  NamedObjectAttributes attr = GetNamedObjectAttributes(
+      base::NumberToWString(::GetCurrentProcessId()).c_str(), GetTestScope());
+  return {base::WaitableEvent(base::win::ScopedHandle(
+              ::CreateEvent(&attr.sa, FALSE, FALSE, attr.name.c_str()))),
+          attr.name};
+}
+
 #endif  // BUILDFLAG(IS_WIN)
 
 }  // namespace updater::test
diff --git a/chrome/updater/util/unittest_util.h b/chrome/updater/util/unittest_util.h
index 05127de..678b30e 100644
--- a/chrome/updater/util/unittest_util.h
+++ b/chrome/updater/util/unittest_util.h
@@ -10,6 +10,7 @@
 #include "base/files/file_path.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/process/process_iterator.h"
+#include "base/synchronization/waitable_event.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace base {
@@ -95,6 +96,13 @@
     const base::FilePath::StringType& executable_name);
 #endif
 
+struct EventHolder {
+  base::WaitableEvent event;
+  std::wstring name;
+};
+
+EventHolder CreateWaitableEventForTest();
+
 }  // namespace updater::test
 
 #endif  // CHROME_UPDATER_UTIL_UNITTEST_UTIL_H_
diff --git a/chrome/updater/util/unittest_util_unittest.cc b/chrome/updater/util/unittest_util_unittest.cc
index 82a81f0ae..e70861a 100644
--- a/chrome/updater/util/unittest_util_unittest.cc
+++ b/chrome/updater/util/unittest_util_unittest.cc
@@ -129,17 +129,9 @@
 
   // Create a unique name for a shared event to be waited for in the test
   // process and signaled in this test.
-  const std::wstring event_name =
-      base::StrCat({kTestProcessExecutableName, L"-",
-                    base::NumberToWString(::GetCurrentProcessId())});
-  NamedObjectAttributes attr =
-      GetNamedObjectAttributes(event_name.c_str(), GetTestScope());
+  EventHolder event_holder(CreateWaitableEventForTest());
 
-  base::WaitableEvent event(base::win::ScopedHandle(
-      ::CreateEvent(&attr.sa, FALSE, FALSE, attr.name.c_str())));
-  ASSERT_NE(event.handle(), nullptr);
-
-  command_line.AppendSwitchNative(kTestEventToWaitOn, attr.name);
+  command_line.AppendSwitchNative(kTestEventToWaitOn, event_holder.name);
 
   const base::Process process = base::LaunchProcess(command_line, {});
   ASSERT_TRUE(process.IsValid());
@@ -148,7 +140,7 @@
       base::BindLambdaForTesting([&]() { return process.IsRunning(); })));
   EXPECT_EQ(test::FindProcesses(kTestProcessExecutableName).size(), 1U);
 
-  event.Signal();
+  event_holder.event.Signal();
 
   EXPECT_TRUE(test::WaitFor(
       base::BindLambdaForTesting([&]() { return !process.IsRunning(); })));
diff --git a/chrome/updater/win/installer/installer.cc b/chrome/updater/win/installer/installer.cc
index 40a0266e8..0bc559f 100644
--- a/chrome/updater/win/installer/installer.cc
+++ b/chrome/updater/win/installer/installer.cc
@@ -319,7 +319,7 @@
   CHECK(EnableSecureDllLoading());
   EnableProcessHeapMetadataProtection();
 
-  if (base::win::GetVersion() < base::win::Version::WIN7) {
+  if (base::win::GetVersion() < base::win::Version::WIN10) {
     return ProcessExitResult(UNSUPPORTED_WINDOWS_VERSION);
   }
 
diff --git a/chrome/updater/win/task_scheduler_unittest.cc b/chrome/updater/win/task_scheduler_unittest.cc
index afc31ab..40c8a89 100644
--- a/chrome/updater/win/task_scheduler_unittest.cc
+++ b/chrome/updater/win/task_scheduler_unittest.cc
@@ -108,17 +108,9 @@
 
     // Create a unique name for a shared event to be waited for in this process
     // and signaled in the test process to confirm it was scheduled and ran.
-    const std::wstring event_name =
-        base::StrCat({kTestProcessExecutableName, L"-",
-                      base::NumberToWString(::GetCurrentProcessId())});
-    NamedObjectAttributes attr =
-        GetNamedObjectAttributes(event_name.c_str(), GetTestScope());
+    test::EventHolder event_holder(test::CreateWaitableEventForTest());
 
-    base::WaitableEvent event(base::win::ScopedHandle(
-        ::CreateEvent(&attr.sa, FALSE, FALSE, attr.name.c_str())));
-    ASSERT_NE(event.handle(), nullptr);
-
-    command_line.AppendSwitchNative(kTestEventToSignal, attr.name);
+    command_line.AppendSwitchNative(kTestEventToSignal, event_holder.name);
     EXPECT_TRUE(task_scheduler_->RegisterTask(
         kTaskName1, kTaskDescription1, command_line, trigger_type, false));
     if (trigger_type != TaskScheduler::TRIGGER_TYPE_NOW) {
@@ -131,7 +123,8 @@
       return info;
     }();
 
-    EXPECT_TRUE(event.TimedWait(TestTimeouts::action_max_timeout()));
+    EXPECT_TRUE(
+        event_holder.event.TimedWait(TestTimeouts::action_max_timeout()));
     EXPECT_TRUE(test::WaitFor(base::BindLambdaForTesting(
         [&]() { return !task_scheduler_->IsTaskRunning(kTaskName1); })));
 
@@ -252,17 +245,9 @@
 
   // Create a unique name for a shared event to be waited for in the task and
   // signaled in this test.
-  const std::wstring event_name =
-      base::StrCat({kTestProcessExecutableName, L"-",
-                    base::NumberToWString(::GetCurrentProcessId())});
-  NamedObjectAttributes attr =
-      GetNamedObjectAttributes(event_name.c_str(), GetTestScope());
+  test::EventHolder event_holder(test::CreateWaitableEventForTest());
 
-  base::WaitableEvent event(base::win::ScopedHandle(
-      ::CreateEvent(&attr.sa, FALSE, FALSE, attr.name.c_str())));
-  ASSERT_NE(event.handle(), nullptr);
-
-  command_line.AppendSwitchNative(kTestEventToWaitOn, attr.name);
+  command_line.AppendSwitchNative(kTestEventToWaitOn, event_holder.name);
   EXPECT_TRUE(
       task_scheduler_->RegisterTask(kTaskName1, kTaskDescription1, command_line,
                                     TaskScheduler::TRIGGER_TYPE_NOW, false));
@@ -271,7 +256,7 @@
       [&]() { return task_scheduler_->IsTaskRunning(kTaskName1); })));
   EXPECT_EQ(test::FindProcesses(kTestProcessExecutableName).size(), 1U);
 
-  event.Signal();
+  event_holder.event.Signal();
 
   EXPECT_TRUE(test::WaitFor(base::BindLambdaForTesting(
       [&]() { return !task_scheduler_->IsTaskRunning(kTaskName1); })));
diff --git a/chromeos/ash/components/audio/cros_audio_config_impl.cc b/chromeos/ash/components/audio/cros_audio_config_impl.cc
index 5ca5457e..de270dad 100644
--- a/chromeos/ash/components/audio/cros_audio_config_impl.cc
+++ b/chromeos/ash/components/audio/cros_audio_config_impl.cc
@@ -115,6 +115,15 @@
   return mojom::MuteState::kNotMuted;
 }
 
+void CrosAudioConfigImpl::SetOutputMuted(bool muted) {
+  CrasAudioHandler* audio_handler = CrasAudioHandler::Get();
+  if (audio_handler->IsOutputMutedByPolicy()) {
+    return;
+  }
+
+  audio_handler->SetOutputMute(muted);
+}
+
 void CrosAudioConfigImpl::SetOutputVolumePercent(int8_t volume) {
   CrasAudioHandler* audio_handler = CrasAudioHandler::Get();
   audio_handler->SetOutputVolumePercent(volume);
diff --git a/chromeos/ash/components/audio/cros_audio_config_impl.h b/chromeos/ash/components/audio/cros_audio_config_impl.h
index c3763e2..a5e0563 100644
--- a/chromeos/ash/components/audio/cros_audio_config_impl.h
+++ b/chromeos/ash/components/audio/cros_audio_config_impl.h
@@ -26,6 +26,7 @@
       std::vector<mojom::AudioDevicePtr>* output_devices_out,
       std::vector<mojom::AudioDevicePtr>* input_devices_out) const override;
   mojom::MuteState GetInputMuteState() const override;
+  void SetOutputMuted(bool muted) override;
   void SetOutputVolumePercent(int8_t volume) override;
   void SetActiveDevice(uint64_t device_id) override;
 
diff --git a/chromeos/ash/components/audio/cros_audio_config_impl_unittest.cc b/chromeos/ash/components/audio/cros_audio_config_impl_unittest.cc
index a68c2479..bddb449d 100644
--- a/chromeos/ash/components/audio/cros_audio_config_impl_unittest.cc
+++ b/chromeos/ash/components/audio/cros_audio_config_impl_unittest.cc
@@ -116,11 +116,20 @@
     return fake_observer;
   }
 
+  bool GetDeviceMuted(const uint64_t id) {
+    return cras_audio_handler_->IsOutputMutedForDevice(id);
+  }
+
   void SimulateSetActiveDevice(const uint64_t& device_id) {
     remote_->SetActiveDevice(device_id);
     base::RunLoop().RunUntilIdle();
   }
 
+  void SimulateSetOutputMuted(bool muted) {
+    remote_->SetOutputMuted(muted);
+    base::RunLoop().RunUntilIdle();
+  }
+
   void SetOutputVolumePercent(uint8_t volume_percent) {
     remote_->SetOutputVolumePercent(volume_percent);
     base::RunLoop().RunUntilIdle();
@@ -336,7 +345,7 @@
       fake_observer->last_audio_system_properties_.value()->output_mute_state);
 }
 
-TEST_F(CrosAudioConfigImplTest, GetOutputMuteStateMutedByPolicy) {
+TEST_F(CrosAudioConfigImplTest, HandleOutputMuteStateMutedByPolicy) {
   SetOutputMuteState(mojom::MuteState::kMutedByPolicy);
   std::unique_ptr<FakeAudioSystemPropertiesObserver> fake_observer = Observe();
   ASSERT_EQ(1u, fake_observer->num_properties_updated_calls_);
@@ -344,6 +353,15 @@
   EXPECT_EQ(
       mojom::MuteState::kMutedByPolicy,
       fake_observer->last_audio_system_properties_.value()->output_mute_state);
+
+  // Simulate attempting to change mute state while policy is enabled.
+  SimulateSetOutputMuted(/*muted=*/true);
+  SimulateSetOutputMuted(/*muted=*/false);
+  ASSERT_EQ(1u, fake_observer->num_properties_updated_calls_);
+  ASSERT_TRUE(fake_observer->last_audio_system_properties_.has_value());
+  EXPECT_EQ(
+      mojom::MuteState::kMutedByPolicy,
+      fake_observer->last_audio_system_properties_.value()->output_mute_state);
 }
 
 TEST_F(CrosAudioConfigImplTest, GetOutputAudioDevices) {
@@ -576,4 +594,31 @@
       fake_observer->last_audio_system_properties_.value()->input_mute_state);
 }
 
+TEST_F(CrosAudioConfigImplTest, SetOutputMuted) {
+  std::unique_ptr<FakeAudioSystemPropertiesObserver> fake_observer = Observe();
+
+  // Test default audio node list.
+  SetAudioNodes({kInternalSpeaker, kHDMIOutput});
+  SetActiveOutputNodes({kInternalSpeakerId});
+  EXPECT_EQ(
+      mojom::MuteState::kNotMuted,
+      fake_observer->last_audio_system_properties_.value()->output_mute_state);
+  EXPECT_FALSE(GetDeviceMuted(kInternalSpeakerId));
+  EXPECT_FALSE(GetDeviceMuted(kHDMIOutputId));
+
+  SimulateSetOutputMuted(/*muted=*/true);
+  EXPECT_EQ(
+      mojom::MuteState::kMutedByUser,
+      fake_observer->last_audio_system_properties_.value()->output_mute_state);
+  EXPECT_TRUE(GetDeviceMuted(kInternalSpeakerId));
+  EXPECT_FALSE(GetDeviceMuted(kHDMIOutputId));
+
+  SimulateSetOutputMuted(/*muted=*/false);
+  EXPECT_EQ(
+      mojom::MuteState::kNotMuted,
+      fake_observer->last_audio_system_properties_.value()->output_mute_state);
+  EXPECT_FALSE(GetDeviceMuted(kInternalSpeakerId));
+  EXPECT_FALSE(GetDeviceMuted(kHDMIOutputId));
+}
+
 }  // namespace ash::audio_config
diff --git a/chromeos/ash/components/audio/public/mojom/cros_audio_config.mojom b/chromeos/ash/components/audio/public/mojom/cros_audio_config.mojom
index 7b3c303..060fe70 100644
--- a/chromeos/ash/components/audio/public/mojom/cros_audio_config.mojom
+++ b/chromeos/ash/components/audio/public/mojom/cros_audio_config.mojom
@@ -98,6 +98,10 @@
   ObserveAudioSystemProperties(
     pending_remote<AudioSystemPropertiesObserver> observer);
 
+  // Sets the mute state of the active output device to |muted|. Ignored if
+  // device is muted by policy.
+  SetOutputMuted(bool muted);
+
   // Sets the volume of the active output device. If |volume| is above a
   // threshold and the device is muted, it's unmuted.
   SetOutputVolumePercent(int8 volume);
diff --git a/chromeos/ash/components/network/apn_migrator.cc b/chromeos/ash/components/network/apn_migrator.cc
index 87a22ba..92e83eeb 100644
--- a/chromeos/ash/components/network/apn_migrator.cc
+++ b/chromeos/ash/components/network/apn_migrator.cc
@@ -63,7 +63,7 @@
   }
 
   const base::Value::List* custom_apn_list =
-      network_metadata_store_->GetCustomApnList(network.guid());
+      network_metadata_store_->GetPreRevampCustomApnList(network.guid());
   if (!custom_apn_list || custom_apn_list->empty()) {
     base::Value::List empty_apn_list;
     SetShillUserApnListForNetwork(network, &empty_apn_list);
@@ -114,7 +114,8 @@
       continue;
     }
     if (const base::Value::List* custom_apn_list =
-            network_metadata_store_->GetCustomApnList(network->guid())) {
+            network_metadata_store_->GetPreRevampCustomApnList(
+                network->guid())) {
       SetShillUserApnListForNetwork(*network, custom_apn_list);
       continue;
     }
diff --git a/chromeos/ash/components/network/apn_migrator_unittest.cc b/chromeos/ash/components/network/apn_migrator_unittest.cc
index 2d438da9..ecfd79b 100644
--- a/chromeos/ash/components/network/apn_migrator_unittest.cc
+++ b/chromeos/ash/components/network/apn_migrator_unittest.cc
@@ -177,7 +177,8 @@
 
   // For the migrated network, the routine should not check for the current
   // custom APN list, but rather just resets the UserApnList.
-  EXPECT_CALL(*network_metadata_store(), GetCustomApnList(kTestCellularGuid1))
+  EXPECT_CALL(*network_metadata_store(),
+              GetPreRevampCustomApnList(kTestCellularGuid1))
       .Times(0);
   base::Value::Dict expected_onc1 = chromeos::network_config::UserApnListToOnc(
       kTestCellularGuid1, /*user_apn_list=*/nullptr);
@@ -190,7 +191,8 @@
       .Times(1);
 
   // Ensure that the function does not modify the non-migrated network.
-  EXPECT_CALL(*network_metadata_store(), GetCustomApnList(kTestCellularGuid2))
+  EXPECT_CALL(*network_metadata_store(),
+              GetPreRevampCustomApnList(kTestCellularGuid2))
       .Times(0);
   EXPECT_CALL(*managed_network_configuration_handler(),
               SetProperties(cellular_service_path_2, _, _, _))
@@ -237,11 +239,13 @@
       .Times(0);
 
   // Return nullptr and empty list for the first two networks.
-  EXPECT_CALL(*network_metadata_store(), GetCustomApnList(kTestCellularGuid1))
+  EXPECT_CALL(*network_metadata_store(),
+              GetPreRevampCustomApnList(kTestCellularGuid1))
       .Times(1)
       .WillOnce(Return(nullptr));
   base::Value::List empty_apn_list;
-  EXPECT_CALL(*network_metadata_store(), GetCustomApnList(kTestCellularGuid2))
+  EXPECT_CALL(*network_metadata_store(),
+              GetPreRevampCustomApnList(kTestCellularGuid2))
       .Times(1)
       .WillOnce(Return(&empty_apn_list));
 
@@ -253,7 +257,8 @@
   base::Value::List populated_apn_list;
   populated_apn_list.Append(std::move(custom_apn_1));
   populated_apn_list.Append(std::move(custom_apn_2));
-  EXPECT_CALL(*network_metadata_store(), GetCustomApnList(kTestCellularGuid3))
+  EXPECT_CALL(*network_metadata_store(),
+              GetPreRevampCustomApnList(kTestCellularGuid3))
       .Times(1)
       .WillOnce(Return(&populated_apn_list));
 
@@ -315,11 +320,13 @@
       .WillOnce(Return(false));
 
   // Simulate that all networks do not have custom APNs
-  EXPECT_CALL(*network_metadata_store(), GetCustomApnList(kTestCellularGuid1))
+  EXPECT_CALL(*network_metadata_store(),
+              GetPreRevampCustomApnList(kTestCellularGuid1))
       .Times(1)
       .WillOnce(Return(nullptr));
   base::Value::List empty_apn_list;
-  EXPECT_CALL(*network_metadata_store(), GetCustomApnList(kTestCellularGuid2))
+  EXPECT_CALL(*network_metadata_store(),
+              GetPreRevampCustomApnList(kTestCellularGuid2))
       .Times(1)
       .WillOnce(Return(&empty_apn_list));
 
@@ -381,7 +388,8 @@
     base::Value::List populated_apn_list;
     populated_apn_list.Append(std::move(custom_apn_1));
     populated_apn_list.Append(std::move(custom_apn_2));
-    EXPECT_CALL(*network_metadata_store(), GetCustomApnList(kTestCellularGuid1))
+    EXPECT_CALL(*network_metadata_store(),
+                GetPreRevampCustomApnList(kTestCellularGuid1))
         .Times(1)
         .WillOnce(Return(&populated_apn_list));
     EXPECT_CALL(*managed_network_configuration_handler(),
@@ -407,7 +415,8 @@
         .Times(1)
         .WillOnce(Return(false));
 
-    EXPECT_CALL(*network_metadata_store(), GetCustomApnList(kTestCellularGuid1))
+    EXPECT_CALL(*network_metadata_store(),
+                GetPreRevampCustomApnList(kTestCellularGuid1))
         .Times(0);
     EXPECT_CALL(*managed_network_configuration_handler(),
                 GetManagedProperties(LoginState::Get()->primary_user_hash(),
diff --git a/chromeos/ash/components/network/mock_network_metadata_store.h b/chromeos/ash/components/network/mock_network_metadata_store.h
index 1a048e5e..8cd02b9 100644
--- a/chromeos/ash/components/network/mock_network_metadata_store.h
+++ b/chromeos/ash/components/network/mock_network_metadata_store.h
@@ -22,6 +22,8 @@
 
   // MockNetworkMetadataStore:
   MOCK_METHOD1(GetCustomApnList, const base::Value::List*(const std::string&));
+  MOCK_METHOD1(GetPreRevampCustomApnList,
+               const base::Value::List*(const std::string&));
 };
 
 }  // namespace ash
diff --git a/chromeos/crosapi/mojom/probe_service.mojom b/chromeos/crosapi/mojom/probe_service.mojom
index f734589..595c4a2a8 100644
--- a/chromeos/crosapi/mojom/probe_service.mojom
+++ b/chromeos/crosapi/mojom/probe_service.mojom
@@ -71,7 +71,7 @@
 
 // An enumeration of each category of information that cros_healthd can report.
 //
-// Next ID: 14
+// Next ID: 15
 [Stable, Extensible]
 enum ProbeCategoryEnum {
   [Default] kUnknown = 11,
@@ -88,6 +88,7 @@
   kSystem = 10,
   kNetwork = 12,
   kTpm = 13,
+  kAudio = 14,
 };
 
 // An enumeration of the different categories of errors that can occur when
@@ -590,6 +591,73 @@
   ProbeError error;
 };
 
+// Audio input node information.
+//
+// NextID: 5
+[Stable]
+struct ProbeAudioInputNodeInfo {
+  // Node id.
+  uint64 id@0;
+  // The name of this node. For example, "Internal Mic".
+  string name@1;
+  // The name of the device that this node belongs to. For example,
+  // "HDA Intel PCH: CA0132 Analog:0,0"
+  string device_name@2;
+  // Whether this node is currently used for input. There is one active
+  // node for input.
+  bool active@3;
+  // The input node gain set by UI, the value is in [0, 100].
+  uint8 node_gain@4;
+};
+
+// Audio output node information.
+//
+// NextID: 5
+[Stable]
+struct ProbeAudioOutputNodeInfo {
+  // Node id.
+  uint64 id@0;
+  // The name of this node. For example, "Speaker".
+  string name@1;
+  // The name of the device that this node belongs to. For example,
+  // "HDA Intel PCH: CA0132 Analog:0,0"
+  string device_name@2;
+  // Whether this node is currently used for output. There is one active
+  // node for output.
+  bool active@3;
+  // The node volume in [0, 100].
+  uint8 node_volume@4;
+};
+
+// Audio information.
+//
+// NextID: 6
+[Stable]
+struct ProbeAudioInfo {
+  // Is the active output device mute or not.
+  bool output_mute@0;
+  // Is the active input device mute or not.
+  bool input_mute@1;
+  // Number of underruns.
+  uint32 underruns@2;
+  // Number of severe underruns.
+  uint32 severe_underruns@3;
+  // Output nodes.
+  array<ProbeAudioOutputNodeInfo>? output_nodes@4;
+  // Input nodes.
+  array<ProbeAudioInputNodeInfo>? input_nodes@5;
+};
+
+// Audio probe result. Can either be populated with the ProbeAudioInfo or an
+// error retrieving the information.
+[Stable]
+union ProbeAudioResult {
+  // Valid ProbeAudioInfo.
+  ProbeAudioInfo audio_info;
+  // The error that occurred attempting to retrieve the AudioInfo.
+  ProbeError error;
+};
+
 // A collection of all the device's telemetry information that cros_healthd is
 // capable of reporting. Note that every field in TelemetryInfo is nullable, and
 // the response for a particular ProbeTelemetryInfo request will only contain
@@ -598,7 +666,7 @@
 // attempt to fetch that information, and size zero if cros_healthd did attempt
 // to fetch that information, but was unable to.
 //
-// Next ID: 13
+// Next ID: 14
 [Stable]
 struct ProbeTelemetryInfo {
   // Information about the device's main battery. Only present when kBattery was
@@ -643,6 +711,9 @@
   // Information about the TPM. Only present when kTpm was included in
   // the categories input to ProbeTelemetryInfo.
   ProbeTpmResult? tpm_result@12;
+  // Information about the audio devices. Only present when kAudio was included
+  // in the categories input to ProbeTelemetryInfo.
+  [MinVersion=1] ProbeAudioResult? audio_result@13;
 };
 
 // Result of running /usr/share/cros/oemdata.sh script.
diff --git a/chromeos/profiles/arm.afdo.newest.txt b/chromeos/profiles/arm.afdo.newest.txt
index 8613382..1bbd52a 100644
--- a/chromeos/profiles/arm.afdo.newest.txt
+++ b/chromeos/profiles/arm.afdo.newest.txt
@@ -1 +1 @@
-chromeos-chrome-arm-none-110-5447.0-1670239482-benchmark-110.0.5464.0-r1-redacted.afdo.xz
+chromeos-chrome-arm-none-110-5464.0-1672660372-benchmark-110.0.5481.24-r1-redacted.afdo.xz
diff --git a/chromeos/services/network_config/cros_network_config.cc b/chromeos/services/network_config/cros_network_config.cc
index 7ceedf1..433722f 100644
--- a/chromeos/services/network_config/cros_network_config.cc
+++ b/chromeos/services/network_config/cros_network_config.cc
@@ -1212,24 +1212,6 @@
   return ::onc::cellular_apn::kStateEnabled;
 }
 
-mojom::ApnAuthenticationType OncApnAuthenticationTypeToMojo(
-    const std::string& authentication_type) {
-  DCHECK(ash::features::IsApnRevampEnabled());
-  if (authentication_type.empty() ||
-      authentication_type ==
-          ::onc::cellular_apn::kAuthenticationTypeAutomatic) {
-    return mojom::ApnAuthenticationType::kAutomatic;
-  }
-  if (authentication_type == ::onc::cellular_apn::kAuthenticationTypePap)
-    return mojom::ApnAuthenticationType::kPap;
-  if (authentication_type == ::onc::cellular_apn::kAuthenticationTypeChap)
-    return mojom::ApnAuthenticationType::kChap;
-
-  NOTREACHED() << "Unexpected ONC APN Authentication type: "
-               << authentication_type;
-  return mojom::ApnAuthenticationType::kAutomatic;
-}
-
 std::string MojoApnAuthenticationTypeToOnc(
     mojom::ApnAuthenticationType authentication_type) {
   DCHECK(ash::features::IsApnRevampEnabled());
@@ -1246,21 +1228,6 @@
   return ::onc::cellular_apn::kAuthenticationTypeAutomatic;
 }
 
-mojom::ApnIpType OncApnIpTypeToMojo(const std::string& ip_type) {
-  DCHECK(ash::features::IsApnRevampEnabled());
-  if (ip_type.empty() || ip_type == ::onc::cellular_apn::kIpTypeAutomatic)
-    return mojom::ApnIpType::kAutomatic;
-  if (ip_type == ::onc::cellular_apn::kIpTypeIpv4)
-    return mojom::ApnIpType::kIpv4;
-  if (ip_type == ::onc::cellular_apn::kIpTypeIpv6)
-    return mojom::ApnIpType::kIpv6;
-  if (ip_type == ::onc::cellular_apn::kIpTypeIpv4Ipv6)
-    return mojom::ApnIpType::kIpv4Ipv6;
-
-  NOTREACHED() << "Unexpected ONC APN IP type: " << ip_type;
-  return mojom::ApnIpType::kAutomatic;
-}
-
 std::string MojoApnIpTypeToOnc(mojom::ApnIpType ip_type) {
   DCHECK(ash::features::IsApnRevampEnabled());
   switch (ip_type) {
@@ -1277,28 +1244,6 @@
   return ::onc::cellular_apn::kIpTypeAutomatic;
 }
 
-std::vector<mojom::ApnType> OncApnTypesToMojo(
-    const std::vector<std::string>& apn_types) {
-  DCHECK(ash::features::IsApnRevampEnabled());
-  DCHECK(!apn_types.empty());
-  std::vector<mojom::ApnType> apn_types_result;
-  apn_types_result.reserve(apn_types.size());
-  for (const std::string& apn_type : apn_types) {
-    if (apn_type == ::onc::cellular_apn::kApnTypeDefault) {
-      apn_types_result.push_back(mojom::ApnType::kDefault);
-      continue;
-    }
-    if (apn_type == ::onc::cellular_apn::kApnTypeAttach) {
-      apn_types_result.push_back(mojom::ApnType::kAttach);
-      continue;
-    }
-
-    NOTREACHED() << "Unexpected ONC APN Type: " << apn_type;
-  }
-
-  return apn_types_result;
-}
-
 std::vector<std::string> MojoApnTypesToOnc(
     const std::vector<mojom::ApnType>& apn_types) {
   DCHECK(ash::features::IsApnRevampEnabled());
@@ -1319,49 +1264,6 @@
   return apn_types_result;
 }
 
-mojom::ApnPropertiesPtr GetApnProperties(const base::Value* dict) {
-  auto apn = mojom::ApnProperties::New();
-  apn->access_point_name =
-      GetRequiredString(dict, ::onc::cellular_apn::kAccessPointName);
-  apn->authentication = GetString(dict, ::onc::cellular_apn::kAuthentication);
-  apn->language = GetString(dict, ::onc::cellular_apn::kLanguage);
-  apn->localized_name = GetString(dict, ::onc::cellular_apn::kLocalizedName);
-  apn->name = GetString(dict, ::onc::cellular_apn::kName);
-  apn->password = GetString(dict, ::onc::cellular_apn::kPassword);
-  apn->username = GetString(dict, ::onc::cellular_apn::kUsername);
-  apn->attach = GetString(dict, ::onc::cellular_apn::kAttach);
-
-  if (ash::features::IsApnRevampEnabled()) {
-    apn->id = GetString(dict, ::onc::cellular_apn::kId);
-    // TODO(b/162365553) Remove missing value checking after Shill implements
-    // the interface.
-    if (!GetString(dict, ::onc::cellular_apn::kAuthenticationType)) {
-      apn->authentication_type = mojom::ApnAuthenticationType::kAutomatic;
-    } else {
-      apn->authentication_type = OncApnAuthenticationTypeToMojo(
-          GetRequiredString(dict, ::onc::cellular_apn::kAuthenticationType));
-    }
-    // TODO(b/162365553) Remove missing value checking after Shill implements
-    // the interface.
-    if (!GetString(dict, ::onc::cellular_apn::kIpType)) {
-      apn->ip_type = mojom::ApnIpType::kAutomatic;
-    } else {
-      apn->ip_type = OncApnIpTypeToMojo(
-          GetRequiredString(dict, ::onc::cellular_apn::kIpType));
-    }
-    // TODO(b/162365553) Remove missing value checking after Shill implements
-    // the interface.
-    if (!GetStringList(dict, ::onc::cellular_apn::kApnTypes)) {
-      apn->apn_types = {mojom::ApnType::kDefault};
-    } else {
-      apn->apn_types = OncApnTypesToMojo(
-          GetRequiredStringList(dict, ::onc::cellular_apn::kApnTypes));
-    }
-  }
-
-  return apn;
-}
-
 mojom::ManagedApnPropertiesPtr GetManagedApnProperties(const base::Value* dict,
                                                        const char* key) {
   const base::Value* apn_dict = dict->FindKey(key);
@@ -1394,7 +1296,7 @@
     auto result = mojom::ManagedApnList::New();
     std::vector<mojom::ApnPropertiesPtr> active;
     for (const base::Value& e : value->GetList())
-      active.push_back(GetApnProperties(&e));
+      active.push_back(GetApnProperties(e.GetDict()));
     result->active_value = std::move(active);
     return result;
   } else if (value->is_dict()) {
@@ -1405,12 +1307,12 @@
     }
     auto result = mojom::ManagedApnList::New();
     for (const base::Value& e : managed_dict.active_value.GetList())
-      result->active_value.push_back(GetApnProperties(&e));
+      result->active_value.push_back(GetApnProperties(e.GetDict()));
     result->policy_source = managed_dict.policy_source;
     if (!managed_dict.policy_value.is_none()) {
       result->policy_value = std::vector<mojom::ApnPropertiesPtr>();
       for (const base::Value& e : managed_dict.policy_value.GetList())
-        result->policy_value->push_back(GetApnProperties(&e));
+        result->policy_value->push_back(GetApnProperties(e.GetDict()));
     }
     return result;
   }
@@ -1885,7 +1787,7 @@
       const base::Value* apn_dict =
           GetDictionary(cellular_dict, ::onc::cellular::kLastGoodAPN);
       if (apn_dict) {
-        cellular->last_good_apn = GetApnProperties(apn_dict);
+        cellular->last_good_apn = GetApnProperties(apn_dict->GetDict());
         if (ash::features::IsApnRevampEnabled()) {
           const absl::optional<std::string> connection_state =
               GetString(properties, ::onc::network_config::kConnectionState);
@@ -1896,7 +1798,7 @@
           // present when the network is not connected.
           if (connection_state &&
               *connection_state == ::onc::connection_state::kConnected) {
-            cellular->connected_apn = GetApnProperties(apn_dict);
+            cellular->connected_apn = GetApnProperties(apn_dict->GetDict());
           }
         }
       }
@@ -3189,48 +3091,11 @@
   }
   for (const auto& apn : *custom_apn_list) {
     DCHECK(apn.is_dict());
-    mojom::ApnPropertiesPtr mojo_apn = mojom::ApnProperties::New();
-    mojo_apn->access_point_name =
-        GetRequiredString(&apn, ::onc::cellular_apn::kAccessPointName);
-    mojo_apn->name = GetString(&apn, ::onc::cellular_apn::kName);
-    mojo_apn->username = GetString(&apn, ::onc::cellular_apn::kUsername);
-    mojo_apn->password = GetString(&apn, ::onc::cellular_apn::kPassword);
-    mojo_apn->authentication =
-        GetString(&apn, ::onc::cellular_apn::kAuthentication);
-    mojo_apn->localized_name =
-        GetString(&apn, ::onc::cellular_apn::kLocalizedName);
-    mojo_apn->language = GetString(&apn, ::onc::cellular_apn::kLanguage);
-    mojo_apn->attach = GetString(&apn, ::onc::cellular_apn::kAttach);
 
+    mojom::ApnPropertiesPtr mojo_apn = GetApnProperties(apn.GetDict());
     if (ash::features::IsApnRevampEnabled()) {
-      mojo_apn->id = GetString(&apn, ::onc::cellular_apn::kId);
       mojo_apn->state = OncApnStateTypeToMojo(
           base::OptionalToPtr(GetString(&apn, ::onc::cellular_apn::kState)));
-      // TODO(b/162365553) Remove missing value checking after Shill implements
-      // the interface.
-      if (!GetString(&apn, ::onc::cellular_apn::kAuthenticationType)) {
-        mojo_apn->authentication_type =
-            mojom::ApnAuthenticationType::kAutomatic;
-      } else {
-        mojo_apn->authentication_type = OncApnAuthenticationTypeToMojo(
-            GetRequiredString(&apn, ::onc::cellular_apn::kAuthenticationType));
-      }
-      // TODO(b/162365553) Remove missing value checking after Shill implements
-      // the interface.
-      if (!GetString(&apn, ::onc::cellular_apn::kIpType)) {
-        mojo_apn->ip_type = mojom::ApnIpType::kAutomatic;
-      } else {
-        mojo_apn->ip_type = OncApnIpTypeToMojo(
-            GetRequiredString(&apn, ::onc::cellular_apn::kIpType));
-      }
-      // TODO(b/162365553) Remove missing value checking after Shill implements
-      // the interface.
-      if (!GetStringList(&apn, ::onc::cellular_apn::kApnTypes)) {
-        mojo_apn->apn_types = {mojom::ApnType::kDefault};
-      } else {
-        mojo_apn->apn_types = OncApnTypesToMojo(
-            GetRequiredStringList(&apn, ::onc::cellular_apn::kApnTypes));
-      }
     }
 
     mojo_custom_apns.push_back(std::move(mojo_apn));
diff --git a/chromeos/services/network_config/public/cpp/BUILD.gn b/chromeos/services/network_config/public/cpp/BUILD.gn
index f0ece19c..3ff1cb6 100644
--- a/chromeos/services/network_config/public/cpp/BUILD.gn
+++ b/chromeos/services/network_config/public/cpp/BUILD.gn
@@ -13,6 +13,7 @@
   ]
 
   deps = [
+    "//ash/constants",
     "//chromeos/services/network_config/public/mojom",
     "//components/onc",
     "//services/network/public/cpp:ip_address_mojom_support",
diff --git a/chromeos/services/network_config/public/cpp/cros_network_config_util.cc b/chromeos/services/network_config/public/cpp/cros_network_config_util.cc
index d4cc1eb..5c45be3 100644
--- a/chromeos/services/network_config/public/cpp/cros_network_config_util.cc
+++ b/chromeos/services/network_config/public/cpp/cros_network_config_util.cc
@@ -3,10 +3,101 @@
 // found in the LICENSE file.
 
 #include "chromeos/services/network_config/public/cpp/cros_network_config_util.h"
+#include "ash/constants/ash_features.h"
 #include "components/onc/onc_constants.h"
 
 namespace chromeos::network_config {
 
+namespace {
+absl::optional<std::string> GetString(const base::Value::Dict& onc_apn,
+                                      const char* key) {
+  const std::string* v = onc_apn.FindString(key);
+  return v ? absl::make_optional<std::string>(*v) : absl::nullopt;
+}
+
+std::string GetRequiredString(const base::Value::Dict& onc_apn,
+                              const char* key) {
+  const std::string* v = onc_apn.FindString(key);
+  if (!v) {
+    NOTREACHED() << "Required key missing: " << key;
+    return std::string();
+  }
+  return *v;
+}
+
+absl::optional<std::vector<std::string>> GetStringList(
+    const base::Value::Dict& dict,
+    const char* key) {
+  const base::Value::List* v = dict.FindList(key);
+  if (!v) {
+    return absl::nullopt;
+  }
+  std::vector<std::string> result;
+  for (const base::Value& e : *v) {
+    result.push_back(e.GetString());
+  }
+  return result;
+}
+
+std::vector<std::string> GetRequiredStringList(const base::Value::Dict& dict,
+                                               const char* key) {
+  const base::Value::List* v = dict.FindList(key);
+  if (!v) {
+    NOTREACHED() << "Required key missing: " << key;
+    return {};
+  }
+  std::vector<std::string> result;
+  result.reserve(v->size());
+  for (const base::Value& e : *v) {
+    if (!e.is_string()) {
+      NOTREACHED() << "Expected string, found: " << e;
+      break;
+    }
+    result.push_back(e.GetString());
+  }
+  return result;
+}
+
+mojom::ApnAuthenticationType OncApnAuthenticationTypeToMojo(
+    const std::string& authentication_type) {
+  DCHECK(ash::features::IsApnRevampEnabled());
+  if (authentication_type.empty() ||
+      authentication_type ==
+          ::onc::cellular_apn::kAuthenticationTypeAutomatic) {
+    return mojom::ApnAuthenticationType::kAutomatic;
+  }
+  if (authentication_type == ::onc::cellular_apn::kAuthenticationTypePap) {
+    return mojom::ApnAuthenticationType::kPap;
+  }
+  if (authentication_type == ::onc::cellular_apn::kAuthenticationTypeChap) {
+    return mojom::ApnAuthenticationType::kChap;
+  }
+
+  NOTREACHED() << "Unexpected ONC APN Authentication type: "
+               << authentication_type;
+  return mojom::ApnAuthenticationType::kAutomatic;
+}
+
+mojom::ApnIpType OncApnIpTypeToMojo(const std::string& ip_type) {
+  DCHECK(ash::features::IsApnRevampEnabled());
+  if (ip_type.empty() || ip_type == ::onc::cellular_apn::kIpTypeAutomatic) {
+    return mojom::ApnIpType::kAutomatic;
+  }
+  if (ip_type == ::onc::cellular_apn::kIpTypeIpv4) {
+    return mojom::ApnIpType::kIpv4;
+  }
+  if (ip_type == ::onc::cellular_apn::kIpTypeIpv6) {
+    return mojom::ApnIpType::kIpv6;
+  }
+  if (ip_type == ::onc::cellular_apn::kIpTypeIpv4Ipv6) {
+    return mojom::ApnIpType::kIpv4Ipv6;
+  }
+
+  NOTREACHED() << "Unexpected ONC APN IP type: " << ip_type;
+  return mojom::ApnIpType::kAutomatic;
+}
+}  // namespace
+
 // This matches logic in NetworkTypePattern and should be kept in sync.
 bool NetworkTypeMatchesType(mojom::NetworkType network_type,
                             mojom::NetworkType match_type) {
@@ -91,4 +182,70 @@
   return onc;
 }
 
+std::vector<mojom::ApnType> OncApnTypesToMojo(
+    const std::vector<std::string>& apn_types) {
+  DCHECK(ash::features::IsApnRevampEnabled());
+  DCHECK(!apn_types.empty());
+  std::vector<mojom::ApnType> apn_types_result;
+  apn_types_result.reserve(apn_types.size());
+  for (const std::string& apn_type : apn_types) {
+    if (apn_type == ::onc::cellular_apn::kApnTypeDefault) {
+      apn_types_result.push_back(mojom::ApnType::kDefault);
+      continue;
+    }
+    if (apn_type == ::onc::cellular_apn::kApnTypeAttach) {
+      apn_types_result.push_back(mojom::ApnType::kAttach);
+      continue;
+    }
+
+    NOTREACHED() << "Unexpected ONC APN Type: " << apn_type;
+  }
+
+  return apn_types_result;
+}
+
+mojom::ApnPropertiesPtr GetApnProperties(const base::Value::Dict& onc_apn) {
+  auto apn = mojom::ApnProperties::New();
+  apn->access_point_name =
+      GetRequiredString(onc_apn, ::onc::cellular_apn::kAccessPointName);
+  apn->authentication =
+      GetString(onc_apn, ::onc::cellular_apn::kAuthentication);
+  apn->language = GetString(onc_apn, ::onc::cellular_apn::kLanguage);
+  apn->localized_name = GetString(onc_apn, ::onc::cellular_apn::kLocalizedName);
+  apn->name = GetString(onc_apn, ::onc::cellular_apn::kName);
+  apn->password = GetString(onc_apn, ::onc::cellular_apn::kPassword);
+  apn->username = GetString(onc_apn, ::onc::cellular_apn::kUsername);
+  apn->attach = GetString(onc_apn, ::onc::cellular_apn::kAttach);
+
+  if (ash::features::IsApnRevampEnabled()) {
+    apn->id = GetString(onc_apn, ::onc::cellular_apn::kId);
+    // TODO(b/162365553) Remove missing value checking after Shill implements
+    // the interface.
+    if (!GetString(onc_apn, ::onc::cellular_apn::kAuthenticationType)) {
+      apn->authentication_type = mojom::ApnAuthenticationType::kAutomatic;
+    } else {
+      apn->authentication_type = OncApnAuthenticationTypeToMojo(
+          GetRequiredString(onc_apn, ::onc::cellular_apn::kAuthenticationType));
+    }
+    // TODO(b/162365553) Remove missing value checking after Shill implements
+    // the interface.
+    if (!GetString(onc_apn, ::onc::cellular_apn::kIpType)) {
+      apn->ip_type = mojom::ApnIpType::kAutomatic;
+    } else {
+      apn->ip_type = OncApnIpTypeToMojo(
+          GetRequiredString(onc_apn, ::onc::cellular_apn::kIpType));
+    }
+    // TODO(b/162365553) Remove missing value checking after Shill implements
+    // the interface.
+    if (!GetStringList(onc_apn, ::onc::cellular_apn::kApnTypes)) {
+      apn->apn_types = {mojom::ApnType::kDefault};
+    } else {
+      apn->apn_types = OncApnTypesToMojo(
+          GetRequiredStringList(onc_apn, ::onc::cellular_apn::kApnTypes));
+    }
+  }
+
+  return apn;
+}
+
 }  // namespace chromeos::network_config
diff --git a/chromeos/services/network_config/public/cpp/cros_network_config_util.h b/chromeos/services/network_config/public/cpp/cros_network_config_util.h
index 67d3f23..ac9bad34 100644
--- a/chromeos/services/network_config/public/cpp/cros_network_config_util.h
+++ b/chromeos/services/network_config/public/cpp/cros_network_config_util.h
@@ -34,6 +34,14 @@
 base::Value::Dict UserApnListToOnc(const std::string& network_guid,
                                    const base::Value::List* user_apn_list);
 
+// Converts a list of APN types in the ONC representation to the Mojo enum
+// representation.
+std::vector<mojom::ApnType> OncApnTypesToMojo(
+    const std::vector<std::string>& apn_types);
+
+// Creates a Mojo APN from a ONC dictionary.
+mojom::ApnPropertiesPtr GetApnProperties(const base::Value::Dict& onc_apn);
+
 }  // namespace network_config
 }  // namespace chromeos
 
diff --git a/components/autofill/core/browser/browser_autofill_manager.cc b/components/autofill/core/browser/browser_autofill_manager.cc
index 752c2d7f..6e0fc3d 100644
--- a/components/autofill/core/browser/browser_autofill_manager.cc
+++ b/components/autofill/core/browser/browser_autofill_manager.cc
@@ -1936,7 +1936,8 @@
     submitted_form->LogQualityMetrics(
         submitted_form->form_parsed_timestamp(), interaction_time,
         submission_time, form_interactions_ukm_logger(), did_show_suggestions_,
-        observed_submission, form_interaction_counts);
+        observed_submission, form_interaction_counts,
+        autofill_suggestion_method_);
 
     if (observed_submission) {
       // Ensure that callbacks for blur votes get sent as well here because
@@ -2023,6 +2024,7 @@
   last_unlocked_credit_card_cvc_.clear();
   credit_card_action_ = mojom::RendererFormDataAction::kPreview;
   initial_interaction_timestamp_ = TimeTicks();
+  autofill_suggestion_method_ = AutofillSuggestionMethod::kUnknown;
   external_delegate_->Reset();
   fast_checkout_delegate_->Reset();
   touch_to_fill_delegate_->Reset();
@@ -3333,6 +3335,11 @@
   OnSingleFieldSuggestionSelected(value, frontend_id);
 }
 
+void BrowserAutofillManager::SetSuggestionOriginMetricState(
+    AutofillSuggestionMethod state) {
+  autofill_suggestion_method_ = state;
+}
+
 void BrowserAutofillManager::ProcessFieldLogEventsInForm(
     const FormStructure& form_structure) {
   // TODO(crbug.com/1325851): Log metrics if at least one field in the form was
diff --git a/components/autofill/core/browser/browser_autofill_manager.h b/components/autofill/core/browser/browser_autofill_manager.h
index 58f6f75..f1ffcf2 100644
--- a/components/autofill/core/browser/browser_autofill_manager.h
+++ b/components/autofill/core/browser/browser_autofill_manager.h
@@ -29,6 +29,7 @@
 #include "components/autofill/core/browser/autofill_manager.h"
 #include "components/autofill/core/browser/fast_checkout_delegate.h"
 #include "components/autofill/core/browser/field_filler.h"
+#include "components/autofill/core/browser/form_structure.h"
 #include "components/autofill/core/browser/form_types.h"
 #include "components/autofill/core/browser/metrics/form_events/address_form_event_logger.h"
 #include "components/autofill/core/browser/metrics/form_events/credit_card_form_event_logger.h"
@@ -315,6 +316,10 @@
                                           const std::u16string& value,
                                           int frontend_id);
 
+  // Sets where the accepted autofill suggestion came from: touch to fill,
+  // keyboard accessory, etc.
+  virtual void SetSuggestionOriginMetricState(AutofillSuggestionMethod state);
+
   void SetExternalDelegateForTest(
       std::unique_ptr<AutofillExternalDelegate> external_delegate) {
     external_delegate_ = std::move(external_delegate);
@@ -810,6 +815,13 @@
   // value="7" label="Phone Collected, WebOTP Used, OTC Used"
   uint32_t phone_collection_metric_state_ = 0;
 
+  // Used to record metrics. It is supposed to be set right after the user
+  // selects an autofill suggestions and reflects 'and reflects the method how
+  // the accepted suggestion was offered to the user:: touch to fill, keyboard
+  // accessory, etc.
+  AutofillSuggestionMethod autofill_suggestion_method_ =
+      AutofillSuggestionMethod::kUnknown;
+
   // List of callbacks to be called for sending blur votes. Only one callback is
   // stored per FormSignature. We rely on FormSignatures rather than
   // FormGlobalId to send votes for the various signatures of a form while it
diff --git a/components/autofill/core/browser/form_structure.cc b/components/autofill/core/browser/form_structure.cc
index 923ad42..28a8ad73 100644
--- a/components/autofill/core/browser/form_structure.cc
+++ b/components/autofill/core/browser/form_structure.cc
@@ -969,7 +969,8 @@
     AutofillMetrics::FormInteractionsUkmLogger* form_interactions_ukm_logger,
     bool did_show_suggestions,
     bool observed_submission,
-    const FormInteractionCounts& form_interaction_counts) const {
+    const FormInteractionCounts& form_interaction_counts,
+    AutofillSuggestionMethod autofill_suggestion_method) const {
   // Use the same timestamp on UKM Metrics generated within this method's scope.
   AutofillMetrics::UkmTimestampPin timestamp_pin(form_interactions_ukm_logger);
 
@@ -1285,6 +1286,11 @@
         AutofillMetrics::LogAutofillPerfectFilling(/*is_address=*/false,
                                                    perfect_filling);
       }
+      if (autofill_suggestion_method ==
+          AutofillSuggestionMethod::KTouchToFillCreditCard) {
+        AutofillMetrics::LogTouchToFillCreditCardPerfectFilling(
+            perfect_filling);
+      }
     }
 
     // Log the field filling statistics if autofill was used.
diff --git a/components/autofill/core/browser/form_structure.h b/components/autofill/core/browser/form_structure.h
index d738c82..d0bed079 100644
--- a/components/autofill/core/browser/form_structure.h
+++ b/components/autofill/core/browser/form_structure.h
@@ -50,6 +50,19 @@
   kPasswordAttributesCount
 };
 
+// Enum which reflects where the user-selected suggestions comes from:
+// touch to fill bottom sheet, keyboard accessory, etc.
+// Add other use cases here as needed.
+// TODO(crbug.com/1405145): Add other origins for autofill and set them where
+// appropriate.
+enum class AutofillSuggestionMethod {
+  // The suggestion origin is not set.
+  kUnknown = 0,
+  // The suggestion comes from the payments bottom sheet.
+  KTouchToFillCreditCard = 1,
+  kMaxValue = KTouchToFillCreditCard
+};
+
 // The structure of forms and fields, represented by their signatures, on a
 // page. These are sequence containers to reflect their order in the DOM.
 using FormAndFieldSignatures =
@@ -261,7 +274,8 @@
       AutofillMetrics::FormInteractionsUkmLogger* form_interactions_ukm_logger,
       bool did_show_suggestions,
       bool observed_submission,
-      const FormInteractionCounts& form_interaction_counts) const;
+      const FormInteractionCounts& form_interaction_counts,
+      autofill::AutofillSuggestionMethod suggestion_origin) const;
 
   // Log the quality of the heuristics and server predictions for this form
   // structure, if autocomplete attributes are present on the fields (they are
diff --git a/components/autofill/core/browser/metrics/autofill_metrics.cc b/components/autofill/core/browser/metrics/autofill_metrics.cc
index 0bca984..433b76d7 100644
--- a/components/autofill/core/browser/metrics/autofill_metrics.cc
+++ b/components/autofill/core/browser/metrics/autofill_metrics.cc
@@ -1954,6 +1954,12 @@
   }
 }
 
+void AutofillMetrics::LogTouchToFillCreditCardPerfectFilling(
+    bool perfect_filling) {
+  base::UmaHistogramBoolean("Autofill.TouchToFill.CreditCard.PerfectFilling",
+                            perfect_filling);
+}
+
 AutofillMetrics::CreditCardSeamlessness::CreditCardSeamlessness(
     const ServerFieldTypeSet& filled_types)
     : name_(filled_types.contains(CREDIT_CARD_NAME_FULL) ||
diff --git a/components/autofill/core/browser/metrics/autofill_metrics.h b/components/autofill/core/browser/metrics/autofill_metrics.h
index c814269..c4ec883 100644
--- a/components/autofill/core/browser/metrics/autofill_metrics.h
+++ b/components/autofill/core/browser/metrics/autofill_metrics.h
@@ -1210,6 +1210,10 @@
   // If |is_address| an address was filled, otherwise it was a credit card.
   static void LogAutofillPerfectFilling(bool is_address, bool perfect_filling);
 
+  // Logs if every non-empty field in a submitted credit card form was filled by
+  // Touch To Fill bottom sheet suggestion selected by user.
+  static void LogTouchToFillCreditCardPerfectFilling(bool perfect_filling);
+
   struct LogCreditCardSeamlessnessParam {
     const raw_ref<const FormEventLoggerBase> event_logger;
     const raw_ref<const FormStructure> form;
diff --git a/components/autofill/core/browser/metrics/autofill_metrics_unittest.cc b/components/autofill/core/browser/metrics/autofill_metrics_unittest.cc
index f6e633a..6ef9c02f 100644
--- a/components/autofill/core/browser/metrics/autofill_metrics_unittest.cc
+++ b/components/autofill/core/browser/metrics/autofill_metrics_unittest.cc
@@ -746,6 +746,81 @@
       BucketsAre(test_case.credit_card_buckets));
 }
 
+struct SuggestionOriginPerfectFillingTestCase {
+  std::string description;
+  std::vector<Field> fields;
+  bool expected_metric_value;
+};
+
+class SuggestionOriginPerfectFillingMetricsTest
+    : public AutofillMetricsTest,
+      public ::testing::WithParamInterface<
+          SuggestionOriginPerfectFillingTestCase> {
+ public:
+  std::vector<test::FieldDescription> GetFields(std::vector<Field> fields) {
+    std::vector<test::FieldDescription> fields_to_return;
+    for (const auto& field : fields) {
+      test::FieldDescription f;
+      if (field.value) {
+        f.value = field.value;
+      } else if (field.field_type == CREDIT_CARD_NAME_FULL) {
+        f.value = u"Elvis Aaron Presley";
+      } else if (field.field_type == CREDIT_CARD_NUMBER) {
+        f.value = u"01230123012399";
+      } else {
+        NOTREACHED();
+      }
+      f.role = field.field_type;
+      f.is_autofilled = field.is_autofilled;
+      fields_to_return.push_back(f);
+    }
+    return fields_to_return;
+  }
+};
+
+TEST_P(SuggestionOriginPerfectFillingMetricsTest,
+       PerfectFilling_TouchToFill_CreditCards) {
+  SuggestionOriginPerfectFillingTestCase test_case = GetParam();
+  std::vector<Field> fields{{CREDIT_CARD_NAME_FULL}, {CREDIT_CARD_NUMBER}};
+  FormData form =
+      test::GetFormData({.description_for_logging = test_case.description,
+                         .fields = GetFields(test_case.fields),
+                         .unique_renderer_id = test::MakeFormRendererId(),
+                         .main_frame_origin = url::Origin::Create(
+                             autofill_client_->form_origin())});
+
+  std::vector<ServerFieldType> field_types;
+  for (const auto& f : test_case.fields) {
+    field_types.push_back(f.field_type);
+  }
+
+  autofill_manager().AddSeenForm(form, field_types);
+
+  base::HistogramTester histogram_tester;
+  autofill_manager().SetSuggestionOriginMetricState(
+      AutofillSuggestionMethod::KTouchToFillCreditCard);
+  SubmitForm(form);
+  EXPECT_EQ(histogram_tester.GetBucketCount(
+                "Autofill.TouchToFill.CreditCard.PerfectFilling",
+                test_case.expected_metric_value),
+            1);
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    AutofillMetricsTest,
+    SuggestionOriginPerfectFillingMetricsTest,
+    testing::Values(
+        // Test that we log the perfect filling metric correctly for an address
+        // form in which every field is autofilled.
+        SuggestionOriginPerfectFillingTestCase{
+            "PerfectFillingForCreditCardForm_AutofilledFromTTF",
+            {{CREDIT_CARD_NAME_FULL}, {CREDIT_CARD_NUMBER}},
+            true},
+        SuggestionOriginPerfectFillingTestCase{
+            "PerfectFillingForCreditCardForm_NotAllAutofilledFromTTF",
+            {{CREDIT_CARD_NAME_FULL}, {CREDIT_CARD_NUMBER, false}},
+            false}));
+
 // Test the emission of collisions between NUMERIC_QUANTITY and server
 // predictions as well as the potential false positives.
 TEST_F(AutofillMetricsTest, NumericQuantityCollision) {
diff --git a/components/autofill/core/browser/touch_to_fill_delegate_impl.cc b/components/autofill/core/browser/touch_to_fill_delegate_impl.cc
index c776ec8e..c8b1fc39 100644
--- a/components/autofill/core/browser/touch_to_fill_delegate_impl.cc
+++ b/components/autofill/core/browser/touch_to_fill_delegate_impl.cc
@@ -143,6 +143,8 @@
   CreditCard* card = pdm->GetCreditCardByGUID(unique_id);
   manager_->FillOrPreviewCreditCardForm(mojom::RendererFormDataAction::kFill,
                                         query_form_, query_field_, card);
+  manager_->SetSuggestionOriginMetricState(
+      AutofillSuggestionMethod::KTouchToFillCreditCard);
 }
 
 void TouchToFillDelegateImpl::OnDismissed() {
diff --git a/components/autofill/core/browser/touch_to_fill_delegate_impl_unittest.cc b/components/autofill/core/browser/touch_to_fill_delegate_impl_unittest.cc
index c7f8dc3..1dcf2e15b 100644
--- a/components/autofill/core/browser/touch_to_fill_delegate_impl_unittest.cc
+++ b/components/autofill/core/browser/touch_to_fill_delegate_impl_unittest.cc
@@ -106,6 +106,10 @@
                const FormData& form,
                const FormFieldData& field,
                const CreditCard* credit_card));
+  MOCK_METHOD(void,
+              SetSuggestionOriginMetricState,
+              (AutofillSuggestionMethod state),
+              (override));
 };
 
 }  // namespace
@@ -529,6 +533,7 @@
   TryToShowTouchToFill(/*expected_success=*/true);
 
   EXPECT_CALL(*browser_autofill_manager_, FillOrPreviewCreditCardForm);
+  EXPECT_CALL(*browser_autofill_manager_, SetSuggestionOriginMetricState);
   touch_to_fill_delegate_->SuggestionSelected(credit_card.server_id());
 }
 
diff --git a/components/autofill/core/common/autofill_payments_features.cc b/components/autofill/core/common/autofill_payments_features.cc
index 3c6c6c1..537f52f 100644
--- a/components/autofill/core/common/autofill_payments_features.cc
+++ b/components/autofill/core/common/autofill_payments_features.cc
@@ -222,6 +222,13 @@
              "AutofillShowUnmaskedCachedCardInManualFillingView",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
+// When enabled, Autofill suggestions that consist of a local and server
+// version of the same card will attempt to fill the server card upon selection
+// instead of the local card.
+BASE_FEATURE(kAutofillSuggestServerCardInsteadOfLocalCard,
+             "AutofillSuggestServerCardInsteadOfLocalCard",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
 // Controls offering credit card upload to Google Payments. Cannot ever be
 // ENABLED_BY_DEFAULT because the feature state depends on the user's country.
 // The set of launched countries is listed in autofill_experiments.cc, and this
diff --git a/components/autofill/core/common/autofill_payments_features.h b/components/autofill/core/common/autofill_payments_features.h
index bb6ff02..6e7a9b2 100644
--- a/components/autofill/core/common/autofill_payments_features.h
+++ b/components/autofill/core/common/autofill_payments_features.h
@@ -44,6 +44,7 @@
 extern const base::FeatureParam<int>
     kAutofillSaveCardUiExperimentSelectorInNumber;
 BASE_DECLARE_FEATURE(kAutofillShowUnmaskedCachedCardInManualFillingView);
+BASE_DECLARE_FEATURE(kAutofillSuggestServerCardInsteadOfLocalCard);
 BASE_DECLARE_FEATURE(kAutofillUpstream);
 BASE_DECLARE_FEATURE(kAutofillUpstreamAllowAdditionalEmailDomains);
 BASE_DECLARE_FEATURE(kAutofillUpstreamAllowAllEmailDomains);
diff --git a/components/download/internal/common/download_stats.cc b/components/download/internal/common/download_stats.cc
index 4cda5e6..6ca50dc 100644
--- a/components/download/internal/common/download_stats.cc
+++ b/components/download/internal/common/download_stats.cc
@@ -694,6 +694,24 @@
   base::UmaHistogramEnumeration("Download.Later.Events", event);
 }
 
+void RecordInputStreamReadError(MojoResult mojo_result) {
+  InputStreamReadError error = InputStreamReadError::kUnknown;
+  switch (mojo_result) {
+    case MOJO_RESULT_INVALID_ARGUMENT:
+      error = InputStreamReadError::kInvalidArgument;
+      break;
+    case MOJO_RESULT_OUT_OF_RANGE:
+      error = InputStreamReadError::kOutOfRange;
+      break;
+    case MOJO_RESULT_BUSY:
+      error = InputStreamReadError::kBusy;
+      break;
+    default:
+      NOTREACHED();
+  }
+  base::UmaHistogramEnumeration("Download.InputStreamReadError", error);
+}
+
 #if BUILDFLAG(IS_ANDROID)
 void RecordBackgroundTargetDeterminationResult(
     BackgroudTargetDeterminationResultTypes type) {
diff --git a/components/download/internal/common/stream_handle_input_stream.cc b/components/download/internal/common/stream_handle_input_stream.cc
index 049fc54..87df54a 100644
--- a/components/download/internal/common/stream_handle_input_stream.cc
+++ b/components/download/internal/common/stream_handle_input_stream.cc
@@ -6,6 +6,7 @@
 
 #include "base/bind.h"
 #include "components/download/public/common/download_interrupt_reasons_utils.h"
+#include "components/download/public/common/download_stats.h"
 #include "components/download/public/common/download_utils.h"
 #include "mojo/public/c/system/types.h"
 
@@ -67,10 +68,11 @@
     return InputStream::EMPTY;
 
   static int bytes_to_read = GetDownloadFileBufferSize();
-  *length = bytes_to_read;
   *data = base::MakeRefCounted<net::IOBuffer>(bytes_to_read);
+  uint32_t u32_len = static_cast<uint32_t>(bytes_to_read);
   MojoResult mojo_result = stream_handle_->stream->ReadData(
-      (*data)->data(), (uint32_t*)length, MOJO_READ_DATA_FLAG_NONE);
+      (*data)->data(), &u32_len, MOJO_READ_DATA_FLAG_NONE);
+  *length = u32_len;
   // TODO(qinmin): figure out when COMPLETE should be returned.
   switch (mojo_result) {
     case MOJO_RESULT_OK:
@@ -86,7 +88,7 @@
     case MOJO_RESULT_INVALID_ARGUMENT:
     case MOJO_RESULT_OUT_OF_RANGE:
     case MOJO_RESULT_BUSY:
-      NOTREACHED();
+      RecordInputStreamReadError(mojo_result);
       return InputStream::COMPLETE;
   }
   return InputStream::EMPTY;
diff --git a/components/download/public/common/BUILD.gn b/components/download/public/common/BUILD.gn
index 7c7850ba..06b7220a 100644
--- a/components/download/public/common/BUILD.gn
+++ b/components/download/public/common/BUILD.gn
@@ -88,6 +88,7 @@
     "//components/download/internal/common:internal",
     "//components/services/quarantine:quarantine",
     "//crypto",
+    "//mojo/public/c/system",
     "//net",
     "//services/metrics/public/cpp:metrics_cpp",
     "//services/network/public/cpp",
diff --git a/components/download/public/common/DEPS b/components/download/public/common/DEPS
index 3ccf12bd..5d027bd 100644
--- a/components/download/public/common/DEPS
+++ b/components/download/public/common/DEPS
@@ -4,6 +4,7 @@
   "+components/keyed_service/core",
   "+components/services/quarantine/public/mojom/quarantine.mojom.h",
   "+crypto",
+  "+mojo/public/c/system",
   "+mojo/public/cpp/bindings",
   "+mojo/public/cpp/system",
   "+net/base/io_buffer.h",
diff --git a/components/download/public/common/download_stats.h b/components/download/public/common/download_stats.h
index e39818b..c7354c3a 100644
--- a/components/download/public/common/download_stats.h
+++ b/components/download/public/common/download_stats.h
@@ -20,6 +20,7 @@
 #include "components/download/public/common/download_export.h"
 #include "components/download/public/common/download_interrupt_reasons.h"
 #include "components/download/public/common/download_source.h"
+#include "mojo/public/c/system/types.h"
 #include "net/base/network_change_notifier.h"
 #include "net/http/http_response_info.h"
 #include "url/gurl.h"
@@ -327,6 +328,20 @@
   kMixContentDownloadBlocking,
 };
 
+enum class InputStreamReadError {
+  // Reading the input stream cause a mojo input argument error.
+  kInvalidArgument = 0,
+
+  // Reading the input stream cause a mojo out of range error.
+  kOutOfRange = 1,
+
+  // Reading the input stream cause a mojo busy error.
+  kBusy = 2,
+
+  kUnknown = 3,
+  kMaxValue = kUnknown,
+};
+
 COMPONENTS_DOWNLOAD_EXPORT DownloadConnectionSecurity
 CheckDownloadConnectionSecurity(const GURL& download_url,
                                 const std::vector<GURL>& url_chain);
@@ -374,6 +389,10 @@
 COMPONENTS_DOWNLOAD_EXPORT void RecordDownloadLaterEvent(
     DownloadLaterEvent event);
 
+// Record download later events.
+COMPONENTS_DOWNLOAD_EXPORT void RecordInputStreamReadError(
+    MojoResult mojo_result);
+
 #if BUILDFLAG(IS_ANDROID)
 enum class BackgroudTargetDeterminationResultTypes {
   // Target determination succeeded.
diff --git a/components/exo/BUILD.gn b/components/exo/BUILD.gn
index ab7dae1..88d72b7f 100644
--- a/components/exo/BUILD.gn
+++ b/components/exo/BUILD.gn
@@ -208,8 +208,6 @@
     "mock_vsync_timing_observer.h",
     "test/exo_test_base.cc",
     "test/exo_test_base.h",
-    "test/exo_test_base_views.cc",
-    "test/exo_test_base_views.h",
     "test/exo_test_data_exchange_delegate.cc",
     "test/exo_test_data_exchange_delegate.h",
     "test/exo_test_helper.cc",
diff --git a/components/exo/test/exo_test_base_views.cc b/components/exo/test/exo_test_base_views.cc
deleted file mode 100644
index 48ccf8af..0000000
--- a/components/exo/test/exo_test_base_views.cc
+++ /dev/null
@@ -1,132 +0,0 @@
-// Copyright 2018 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/exo/test/exo_test_base_views.h"
-
-#include "base/callback_helpers.h"
-#include "base/notreached.h"
-#include "components/exo/vsync_timing_manager.h"
-#include "components/exo/wm_helper.h"
-#include "mojo/public/cpp/bindings/pending_remote.h"
-#include "ui/aura/client/drag_drop_delegate.h"
-#include "ui/base/dragdrop/mojom/drag_drop_types.mojom.h"
-#include "ui/base/ime/init/input_method_factory.h"
-#include "ui/display/manager/managed_display_info.h"
-#include "ui/wm/core/wm_core_switches.h"
-
-namespace exo {
-namespace test {
-
-namespace {
-
-class WMHelperTester : public WMHelper, public VSyncTimingManager::Delegate {
- public:
-  WMHelperTester(aura::Window* root_window)
-      : root_window_(root_window), vsync_timing_manager_(this) {}
-
-  WMHelperTester(const WMHelperTester&) = delete;
-  WMHelperTester& operator=(const WMHelperTester&) = delete;
-
-  ~WMHelperTester() override {}
-
-  // Overridden from WMHelper
-  void AddActivationObserver(wm::ActivationChangeObserver* observer) override {}
-  void RemoveActivationObserver(
-      wm::ActivationChangeObserver* observer) override {}
-  void AddTooltipObserver(wm::TooltipObserver* observer) override {}
-  void RemoveTooltipObserver(wm::TooltipObserver* observer) override {}
-  void AddFocusObserver(aura::client::FocusChangeObserver* observer) override {}
-  void RemoveFocusObserver(
-      aura::client::FocusChangeObserver* observer) override {}
-  void AddDragDropObserver(DragDropObserver* observer) override {}
-  void RemoveDragDropObserver(DragDropObserver* observer) override {}
-  void SetDragDropDelegate(aura::Window*) override {}
-  void ResetDragDropDelegate(aura::Window*) override {}
-  VSyncTimingManager& GetVSyncTimingManager() override {
-    return vsync_timing_manager_;
-  }
-
-  const display::ManagedDisplayInfo& GetDisplayInfo(
-      int64_t display_id) const override {
-    static display::ManagedDisplayInfo md;
-    return md;
-  }
-  const std::vector<uint8_t>& GetDisplayIdentificationData(
-      int64_t display_id) const override {
-    static std::vector<uint8_t> no_data;
-    return no_data;
-  }
-  bool GetActiveModeForDisplayId(
-      int64_t display_id,
-      display::ManagedDisplayMode* mode) const override {
-    return false;
-  }
-
-  aura::Window* GetPrimaryDisplayContainer(int container_id) override {
-    return root_window_;
-  }
-  aura::Window* GetActiveWindow() const override { return nullptr; }
-  aura::Window* GetFocusedWindow() const override { return nullptr; }
-  aura::Window* GetRootWindowForNewWindows() const override {
-    return root_window_;
-  }
-  aura::client::CursorClient* GetCursorClient() override { return nullptr; }
-  aura::client::DragDropClient* GetDragDropClient() override { return nullptr; }
-  void AddPreTargetHandler(ui::EventHandler* handler) override {}
-  void PrependPreTargetHandler(ui::EventHandler* handler) override {}
-  void RemovePreTargetHandler(ui::EventHandler* handler) override {}
-  void AddPostTargetHandler(ui::EventHandler* handler) override {}
-  void RemovePostTargetHandler(ui::EventHandler* handler) override {}
-  bool InTabletMode() const override { return false; }
-  double GetDefaultDeviceScaleFactor() const override { return 1.0; }
-  double GetDeviceScaleFactorForWindow(aura::Window* window) const override {
-    return 1.0;
-  }
-  void SetDefaultScaleCancellation(bool default_scale_cancellation) override {}
-
-  LifetimeManager* GetLifetimeManager() override { return &lifetime_manager_; }
-  aura::client::CaptureClient* GetCaptureClient() override { return nullptr; }
-
-  // Overridden from aura::client::DragDropDelegate:
-  void OnDragEntered(const ui::DropTargetEvent& event) override {}
-  aura::client::DragUpdateInfo OnDragUpdated(
-      const ui::DropTargetEvent& event) override {
-    return aura::client::DragUpdateInfo();
-  }
-  void OnDragExited() override {}
-  WMHelper::DropCallback GetDropCallback(
-      const ui::DropTargetEvent& event) override {
-    return base::DoNothing();
-  }
-
-  // Overridden from VSyncTimingManager::Delegate:
-  void AddVSyncParameterObserver(
-      mojo::PendingRemote<viz::mojom::VSyncParameterObserver> observer)
-      override {}
-
- private:
-  aura::Window* root_window_;
-  LifetimeManager lifetime_manager_;
-  VSyncTimingManager vsync_timing_manager_;
-};
-
-}  // namespace
-
-ExoTestBaseViews::ExoTestBaseViews() {}
-ExoTestBaseViews::~ExoTestBaseViews() {}
-
-void ExoTestBaseViews::SetUp() {
-  views::ViewsTestBase::SetUp();
-
-  wm_helper_ = std::make_unique<WMHelperTester>(root_window());
-}
-
-void ExoTestBaseViews::TearDown() {
-  wm_helper_.reset();
-
-  views::ViewsTestBase::TearDown();
-}
-
-}  // namespace test
-}  // namespace exo
diff --git a/components/exo/test/exo_test_base_views.h b/components/exo/test/exo_test_base_views.h
deleted file mode 100644
index 165616d3..0000000
--- a/components/exo/test/exo_test_base_views.h
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright 2018 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_EXO_TEST_EXO_TEST_BASE_VIEWS_H_
-#define COMPONENTS_EXO_TEST_EXO_TEST_BASE_VIEWS_H_
-
-#include "testing/gtest/include/gtest/gtest.h"
-#include "ui/base/ime/init/input_method_factory.h"
-#include "ui/views/test/views_test_base.h"
-
-namespace exo {
-
-class WMHelper;
-
-namespace test {
-
-// Implementation of a test::Base built on Views without ChromeOS Ash
-// dependencies.
-class ExoTestBaseViews : public views::ViewsTestBase {
- public:
-  ExoTestBaseViews();
-  ~ExoTestBaseViews() override;
-
-  // Overridden from test::Test.
-  void SetUp() override;
-  void TearDown() override;
-
- private:
-  std::unique_ptr<WMHelper> wm_helper_;
-  ui::ScopedTestInputMethodFactory scoped_test_input_method_factory_;
-};
-
-}  // namespace test
-}  // namespace exo
-
-#endif  // COMPONENTS_EXO_TEST_EXO_TEST_BASE_VIEWS_H_
diff --git a/components/exo/wayland/fuzzer/harness_unittest.cc b/components/exo/wayland/fuzzer/harness_unittest.cc
index a18d1134..1efc2bd 100644
--- a/components/exo/wayland/fuzzer/harness_unittest.cc
+++ b/components/exo/wayland/fuzzer/harness_unittest.cc
@@ -9,7 +9,6 @@
 #include "base/time/time.h"
 #include "components/exo/display.h"
 #include "components/exo/test/exo_test_base.h"
-#include "components/exo/test/exo_test_base_views.h"
 #include "components/exo/wayland/fuzzer/actions.pb.h"
 #include "components/exo/wayland/server.h"
 
diff --git a/components/exo/wayland/test/wayland_server_test_base.cc b/components/exo/wayland/test/wayland_server_test_base.cc
index 3951093..ca4fe1f 100644
--- a/components/exo/wayland/test/wayland_server_test_base.cc
+++ b/components/exo/wayland/test/wayland_server_test_base.cc
@@ -15,7 +15,6 @@
 #include "base/strings/stringprintf.h"
 #include "components/exo/display.h"
 #include "components/exo/security_delegate.h"
-#include "components/exo/test/exo_test_base_views.h"
 #include "components/exo/test/test_security_delegate.h"
 #include "components/exo/wayland/server.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/components/feed/core/v2/api_test/feed_api_test.cc b/components/feed/core/v2/api_test/feed_api_test.cc
index 7404c985..356c3b6 100644
--- a/components/feed/core/v2/api_test/feed_api_test.cc
+++ b/components/feed/core/v2/api_test/feed_api_test.cc
@@ -834,10 +834,6 @@
   return for_you;
 }
 
-void TestMetricsReporter::OnClearAll(base::TimeDelta since_last_clear) {
-  time_since_last_clear = since_last_clear;
-  MetricsReporter::OnClearAll(time_since_last_clear.value());
-}
 void TestMetricsReporter::OnUploadActions(UploadActionsStatus status) {
   upload_action_status = status;
   MetricsReporter::OnUploadActions(status);
diff --git a/components/feed/core/v2/api_test/feed_api_test.h b/components/feed/core/v2/api_test/feed_api_test.h
index bc18562..00915d1 100644
--- a/components/feed/core/v2/api_test/feed_api_test.h
+++ b/components/feed/core/v2/api_test/feed_api_test.h
@@ -427,7 +427,6 @@
                   const ContentStats& content_stats) override;
   void OnBackgroundRefresh(const StreamType& stream_type,
                            LoadStreamStatus final_status) override;
-  void OnClearAll(base::TimeDelta since_last_clear) override;
   void OnUploadActions(UploadActionsStatus status) override;
 
   struct StreamMetrics {
@@ -447,7 +446,6 @@
   absl::optional<SurfaceId> load_more_surface_id;
   absl::optional<LoadStreamStatus> load_more_status;
   absl::optional<LoadStreamStatus> background_refresh_status;
-  absl::optional<base::TimeDelta> time_since_last_clear;
   absl::optional<UploadActionsStatus> upload_action_status;
 
   StreamMetrics web_feed;
diff --git a/components/feed/core/v2/feed_stream.cc b/components/feed/core/v2/feed_stream.cc
index ac6bc84..86773840 100644
--- a/components/feed/core/v2/feed_stream.cc
+++ b/components/feed/core/v2/feed_stream.cc
@@ -1201,8 +1201,6 @@
 }
 
 void FeedStream::ClearAll() {
-  metrics_reporter_->OnClearAll(
-      base::Time::Now() - GetLastFetchTime(StreamType(StreamKind::kForYou)));
   clear_all_in_progress_ = true;
   task_queue_.AddTask(FROM_HERE, std::make_unique<ClearAllTask>(this));
 }
diff --git a/components/feed/core/v2/metrics_reporter.cc b/components/feed/core/v2/metrics_reporter.cc
index 7b67a15..781b9d7 100644
--- a/components/feed/core/v2/metrics_reporter.cc
+++ b/components/feed/core/v2/metrics_reporter.cc
@@ -1102,13 +1102,6 @@
   ReportGetMoreIfNeeded(surface_id, true);
 }
 
-void MetricsReporter::OnClearAll(base::TimeDelta time_since_last_clear) {
-  base::UmaHistogramCustomTimes(
-      "ContentSuggestions.Feed.Scheduler.TimeSinceLastFetchOnClear",
-      time_since_last_clear, base::Seconds(1), base::Days(7),
-      /*buckets=*/50);
-}
-
 void MetricsReporter::ReportPersistentDataIfDayIsDone() {
   // Reset the persistent data if 24 hours have elapsed, or if it has never
   // been initialized.
diff --git a/components/feed/core/v2/metrics_reporter.h b/components/feed/core/v2/metrics_reporter.h
index a99abea..1c4081c3 100644
--- a/components/feed/core/v2/metrics_reporter.h
+++ b/components/feed/core/v2/metrics_reporter.h
@@ -133,7 +133,6 @@
   virtual void OnLoadMore(const StreamType& stream_type,
                           LoadStreamStatus final_status,
                           const ContentStats& content_stats);
-  virtual void OnClearAll(base::TimeDelta time_since_last_clear);
   // Called each time the surface receives new content.
   void SurfaceReceivedContent(SurfaceId surface_id);
   // Called when Chrome is entering the background.
diff --git a/components/history/core/browser/BUILD.gn b/components/history/core/browser/BUILD.gn
index e08d1c3c..427566c 100644
--- a/components/history/core/browser/BUILD.gn
+++ b/components/history/core/browser/BUILD.gn
@@ -206,6 +206,7 @@
     "//components/test/data/history/history.58.sql",
     "//components/test/data/history/history.59.sql",
     "//components/test/data/history/history.60.sql",
+    "//components/test/data/history/history.61.sql",
     "//components/test/data/history/thumbnail_wild/Favicons.corrupt_meta.disable",
     "//components/test/data/history/thumbnail_wild/Favicons.v2.init.sql",
     "//components/test/data/history/thumbnail_wild/Favicons.v3.init.sql",
diff --git a/components/history/core/browser/history_backend_db_unittest.cc b/components/history/core/browser/history_backend_db_unittest.cc
index 75d80f98..4e1080c 100644
--- a/components/history/core/browser/history_backend_db_unittest.cc
+++ b/components/history/core/browser/history_backend_db_unittest.cc
@@ -2658,6 +2658,45 @@
   EXPECT_TRUE(cluster.triggerability_calculated);
 }
 
+TEST_F(HistoryBackendDBTest,
+       MigrateClustersAutoincrementIdAndAddOriginatorColumns) {
+  ASSERT_NO_FATAL_FAILURE(CreateDBVersion(60));
+
+  int64_t cluster_id = 1;
+
+  // Open the db for manual manipulation.
+  sql::Database db;
+  ASSERT_TRUE(db.Open(history_dir_.Append(kHistoryFilename)));
+
+  const char kInsertClustersStatement[] =
+      "INSERT INTO clusters"
+      "(cluster_id,should_show_on_prominent_ui_surfaces,label,raw_label,"
+      "triggerability_calculated)"
+      "VALUES(?,?,?,?,?)";
+
+  // Add a row to `clusters` table.
+  {
+    sql::Statement s(db.GetUniqueStatement(kInsertClustersStatement));
+    s.BindInt64(0, cluster_id);
+    s.BindBool(1, true);
+    s.BindString16(2, u"");
+    s.BindString16(3, u"");
+    s.BindBool(4, true);
+    ASSERT_TRUE(s.Run());
+  }
+
+  // Re-open the db, triggering migration.
+  CreateBackendAndDatabase();
+
+  // After the migration, the originator columns should return default values.
+  {
+    // Check contents.
+    Cluster cluster = db_->GetCluster(cluster_id);
+    EXPECT_EQ(cluster.originator_cache_guid, "");
+    EXPECT_EQ(cluster.originator_cluster_id, 0);
+  }
+}
+
 // ^^^ NEW MIGRATION TESTS GO HERE ^^^
 
 // Preparation for the next DB migration: This test verifies that the test DB
diff --git a/components/history/core/browser/history_database.cc b/components/history/core/browser/history_database.cc
index a6415cf..4fb936d 100644
--- a/components/history/core/browser/history_database.cc
+++ b/components/history/core/browser/history_database.cc
@@ -38,7 +38,7 @@
 // Current version number. We write databases at the "current" version number,
 // but any previous version that can read the "compatible" one can make do with
 // our database without *too* many bad effects.
-const int kCurrentVersionNumber = 60;
+const int kCurrentVersionNumber = 61;
 const int kCompatibleVersionNumber = 16;
 
 const char kEarlyExpirationThresholdKey[] = "early_expiration_threshold";
@@ -834,6 +834,14 @@
     meta_table_.SetVersionNumber(cur_version);
   }
 
+  if (cur_version == 60) {
+    if (!MigrateClustersAutoincrementIdAndAddOriginatorColumns()) {
+      return LogMigrationFailure(60);
+    }
+    cur_version++;
+    meta_table_.SetVersionNumber(cur_version);
+  }
+
   // =========================       ^^ new migration code goes here ^^
   // ADDING NEW MIGRATION CODE
   // =========================
diff --git a/components/history/core/browser/history_types.h b/components/history/core/browser/history_types.h
index f25e092..2e71fc91 100644
--- a/components/history/core/browser/history_types.h
+++ b/components/history/core/browser/history_types.h
@@ -1028,6 +1028,20 @@
   // Set to true if the triggerability of this cluster (e.g. keywords, should
   // show on prominent UI surfaces) has already been calculated.
   bool triggerability_calculated = false;
+
+  // These are set only for synced visits originating from a different machine.
+  // `originator_cache_guid` is the originator machine's unique client ID. It's
+  // called a "cache" just to match Chrome Sync's terminology.
+  // Note that even for synced clusters, this may be empty if from a legacy
+  // client that does not support the sending of this field or the local client
+  // does not support populating this field.
+  std::string originator_cache_guid;
+  // The cluster ID of this cluster on the originating device, which is *not*
+  // comparable to local cluster IDs (as in `cluster_id`.)
+  // Note that even for synced clusters, this may be 0 if from a legacy client
+  // that does not support the sending of this field or the local client does
+  // not support populating this field.
+  int64_t originator_cluster_id = 0;
 };
 
 // Navigation -----------------------------------------------------------------
diff --git a/components/history/core/browser/visit_annotations_database.cc b/components/history/core/browser/visit_annotations_database.cc
index bcb9ac7..fbf1d8d 100644
--- a/components/history/core/browser/visit_annotations_database.cc
+++ b/components/history/core/browser/visit_annotations_database.cc
@@ -14,6 +14,7 @@
 #include "components/history/core/browser/url_row.h"
 #include "sql/statement.h"
 #include "sql/statement_id.h"
+#include "sql/transaction.h"
 
 namespace history {
 
@@ -29,7 +30,7 @@
   "tab_id,task_id,root_task_id,parent_task_id,response_code "
 #define HISTORY_CLUSTER_ROW_FIELDS                                    \
   " cluster_id,should_show_on_prominent_ui_surfaces,label,raw_label," \
-  "triggerability_calculated "
+  "triggerability_calculated,originator_cache_guid,originator_cluster_id "
 #define HISTORY_CLUSTER_VISIT_ROW_FIELDS                              \
   " visit_id,score,engagement_score,url_for_deduping,normalized_url," \
   "url_for_display "
@@ -577,8 +578,9 @@
       GetDB().GetCachedStatement(SQL_FROM_HERE,
                                  "INSERT INTO clusters"
                                  "(should_show_on_prominent_ui_surfaces,label,"
-                                 "raw_label,triggerability_calculated)"
-                                 "VALUES(?,?,?,?)"));
+                                 "raw_label,triggerability_calculated,"
+                                 "originator_cache_guid,originator_cluster_id)"
+                                 "VALUES(?,?,?,?,?,?)"));
   sql::Statement clusters_and_visits_statement(GetDB().GetCachedStatement(
       SQL_FROM_HERE,
       "INSERT INTO clusters_and_visits"
@@ -609,6 +611,8 @@
     clusters_statement.BindString16(1, cluster.label.value_or(u""));
     clusters_statement.BindString16(2, cluster.raw_label.value_or(u""));
     clusters_statement.BindBool(3, cluster.triggerability_calculated);
+    clusters_statement.BindString(4, cluster.originator_cache_guid);
+    clusters_statement.BindInt64(5, cluster.originator_cluster_id);
     if (!clusters_statement.Run()) {
       DVLOG(0) << "Failed to execute 'clusters' insert statement";
       continue;
@@ -680,12 +684,16 @@
       GetDB().GetCachedStatement(SQL_FROM_HERE,
                                  "INSERT INTO clusters"
                                  "(should_show_on_prominent_ui_surfaces,label,"
-                                 "raw_label,triggerability_calculated)"
-                                 "VALUES(?,?,?,?)"));
+                                 "raw_label,triggerability_calculated,"
+                                 "originator_cache_guid,originator_cluster_id)"
+                                 "VALUES(?,?,?,?,?,?)"));
   clusters_statement.BindBool(0, false);
   clusters_statement.BindString16(1, u"");
   clusters_statement.BindString16(2, u"");
   clusters_statement.BindBool(3, false);
+  // TODO(b/264457591): Probably have this method take in the originator fields.
+  clusters_statement.BindString(4, "");
+  clusters_statement.BindInt64(5, 0);
   if (!clusters_statement.Run()) {
     DVLOG(0) << "Failed to execute 'clusters' insert statement";
   }
@@ -817,6 +825,8 @@
   if (cluster.raw_label->empty())
     cluster.raw_label = absl::nullopt;
   cluster.triggerability_calculated = statement.ColumnBool(4);
+  cluster.originator_cache_guid = statement.ColumnString(5);
+  cluster.originator_cluster_id = statement.ColumnInt64(6);
   return cluster;
 }
 
@@ -1235,14 +1245,98 @@
   return true;
 }
 
+bool VisitAnnotationsDatabase::MigrateClustersAddTriggerabilityCalculated() {
+  if (!GetDB().DoesTableExist("clusters")) {
+    NOTREACHED() << " Clusters table should exist before migration";
+    return false;
+  }
+
+  if (GetDB().DoesColumnExist("clusters", "triggerability_calculated")) {
+    return true;
+  }
+  // Set default to true, as clusters added to this table prior to this column
+  // getting added are the fully formed clusters rather than just the basic
+  // ones.
+  return GetDB().Execute(
+      "ALTER TABLE clusters "
+      "ADD COLUMN triggerability_calculated BOOL DEFAULT TRUE");
+}
+
+bool VisitAnnotationsDatabase::
+    MigrateClustersAutoincrementIdAndAddOriginatorColumns() {
+  if (!GetDB().DoesTableExist("clusters")) {
+    NOTREACHED() << " Clusters table should exist before migration";
+    return false;
+  }
+
+  if (GetDB().DoesColumnExist("clusters", "originator_cache_guid") &&
+      GetDB().DoesColumnExist("clusters", "originator_cluster_id") &&
+      ClustersTableContainsAutoincrement()) {
+    return true;
+  }
+
+  sql::Transaction transaction(&GetDB());
+  return transaction.Begin() &&
+         GetDB().Execute(
+             "CREATE TABLE clusters_tmp("
+             "cluster_id INTEGER PRIMARY KEY AUTOINCREMENT,"
+             "should_show_on_prominent_ui_surfaces BOOLEAN NOT NULL,"
+             "label VARCHAR NOT NULL,"
+             "raw_label VARCHAR NOT NULL,"
+             "triggerability_calculated BOOLEAN NOT NULL,"
+             "originator_cache_guid TEXT DEFAULT \"\" NOT NULL,"
+             "originator_cluster_id INTEGER DEFAULT 0 NOT NULL)") &&
+         GetDB().Execute(
+             "INSERT INTO clusters_tmp("
+             "cluster_id,should_show_on_prominent_ui_surfaces,label,raw_label,"
+             "triggerability_calculated)"
+             "SELECT "
+             "cluster_id,should_show_on_prominent_ui_surfaces,label,raw_label,"
+             "triggerability_calculated FROM clusters") &&
+         GetDB().Execute("DROP TABLE clusters") &&
+         GetDB().Execute("ALTER TABLE clusters_tmp RENAME TO clusters") &&
+         transaction.Commit();
+}
+
+bool VisitAnnotationsDatabase::ClustersTableContainsAutoincrement() {
+  // sqlite_schema has columns:
+  //   type - "index" or "table".
+  //   name - name of created element.
+  //   tbl_name - name of element, or target table in case of index.
+  //   rootpage - root page of the element in database file.
+  //   sql - SQL to create the element.
+  sql::Statement statement(
+      GetDB().GetUniqueStatement("SELECT sql FROM sqlite_schema WHERE type = "
+                                 "'table' AND name = 'clusters'"));
+
+  // clusters table does not exist.
+  if (!statement.Step()) {
+    return false;
+  }
+
+  std::string clusters_schema = statement.ColumnString(0);
+  // We check if the whole schema contains "AUTOINCREMENT", since
+  // "AUTOINCREMENT" only can be used for "INTEGER PRIMARY KEY", so we assume no
+  // other columns could contain "AUTOINCREMENT".
+  return clusters_schema.find("AUTOINCREMENT") != std::string::npos;
+}
+
 bool VisitAnnotationsDatabase::CreateClustersTable() {
+  // The `id` uses AUTOINCREMENT to support Sync. Chrome Sync uses the
+  // `id` in conjunction with the Client ID as a unique identifier.
+  // If this was not AUTOINCREMENT, deleting a row and creating a new
+  // one could reuse the same `id` for an entirely new cluster, which
+  // would confuse Sync, as Sync would be unable to distinguish
+  // an update from a deletion plus a creation.
   return GetDB().Execute(
       "CREATE TABLE IF NOT EXISTS clusters("
-      "cluster_id INTEGER PRIMARY KEY,"
+      "cluster_id INTEGER PRIMARY KEY AUTOINCREMENT,"
       "should_show_on_prominent_ui_surfaces BOOLEAN NOT NULL,"
       "label VARCHAR NOT NULL,"
       "raw_label VARCHAR NOT NULL,"
-      "triggerability_calculated BOOLEAN NOT NULL)");
+      "triggerability_calculated BOOLEAN NOT NULL,"
+      "originator_cache_guid TEXT NOT NULL,"
+      "originator_cluster_id INTEGER NOT NULL)");
 }
 
 bool VisitAnnotationsDatabase::CreateClustersAndVisitsTableAndIndex() {
@@ -1262,21 +1356,4 @@
              "clusters_and_visits(visit_id)");
 }
 
-bool VisitAnnotationsDatabase::MigrateClustersAddTriggerabilityCalculated() {
-  if (!GetDB().DoesTableExist("clusters")) {
-    NOTREACHED() << " Clusters table should exist before migration";
-    return false;
-  }
-
-  if (GetDB().DoesColumnExist("clusters", "triggerability_calculated")) {
-    return true;
-  }
-  // Set default to true, as clusters added to this table prior to this column
-  // getting added are the fully formed clusters rather than just the basic
-  // ones.
-  return GetDB().Execute(
-      "ALTER TABLE clusters "
-      "ADD COLUMN triggerability_calculated BOOL DEFAULT TRUE");
-}
-
 }  // namespace history
diff --git a/components/history/core/browser/visit_annotations_database.h b/components/history/core/browser/visit_annotations_database.h
index 6b57549..00a43c95d 100644
--- a/components/history/core/browser/visit_annotations_database.h
+++ b/components/history/core/browser/visit_annotations_database.h
@@ -192,7 +192,17 @@
   // a triggerability calculated column.
   bool MigrateClustersAddTriggerabilityCalculated();
 
+  // Called by the derived classes to migrate the older clusters table which
+  // aren't ready to accommodate Sync. It sets `id` to AUTOINCREMENT, and
+  // ensures the existence of the `originator_cache_guid` and
+  // `originator_cluster_id` columns.
+  bool MigrateClustersAutoincrementIdAndAddOriginatorColumns();
+
  private:
+  // Return true if the clusters table's schema contains "AUTOINCREMENT".
+  // false if table do not contain AUTOINCREMENT, or the table is not created.
+  bool ClustersTableContainsAutoincrement();
+
   // Helper to create the 'clusters' table and avoid duplicating the code.
   bool CreateClustersTable();
 
diff --git a/components/image_service/image_service.cc b/components/image_service/image_service.cc
index e1d858a..aca6452 100644
--- a/components/image_service/image_service.cc
+++ b/components/image_service/image_service.cc
@@ -177,9 +177,10 @@
     for (const auto& result : results.suggest_results) {
       // TODO(tommycli): `entity_id_` is not used yet, because it's always
       // empty right now.
-      if (result.image_url().is_valid() &&
+      GURL url(result.entity_info().image_url());
+      if (url.is_valid() &&
           base::i18n::ToLower(result.match_contents()) == search_query_) {
-        return std::move(callback_).Run(result.image_url());
+        return std::move(callback_).Run(std::move(url));
       }
     }
 
diff --git a/components/language/ios/browser/ios_language_detection_tab_helper.mm b/components/language/ios/browser/ios_language_detection_tab_helper.mm
index 8cfd806..d4f10151 100644
--- a/components/language/ios/browser/ios_language_detection_tab_helper.mm
+++ b/components/language/ios/browser/ios_language_detection_tab_helper.mm
@@ -194,8 +194,12 @@
 }
 
 void IOSLanguageDetectionTabHelper::StartLanguageDetection() {
-  // Translate setting should not cancel language detection.
+  // Translate setting should not cancel language detection, except if it is
+  // disabled by policy.
   // Keep the existing behavior if IsForceTranslateEnabled is disabled.
+  if (!translate_enabled_.GetValue() && translate_enabled_.IsManaged()) {
+    return;
+  }
   if (!translate_enabled_.GetValue() && !translate::IsForceTranslateEnabled()) {
     return;  // Translate disabled in preferences.
   }
diff --git a/components/ntp_snippets/remote/remote_suggestions_scheduler_impl.cc b/components/ntp_snippets/remote/remote_suggestions_scheduler_impl.cc
index 99f778a..ee11faf 100644
--- a/components/ntp_snippets/remote/remote_suggestions_scheduler_impl.cc
+++ b/components/ntp_snippets/remote/remote_suggestions_scheduler_impl.cc
@@ -838,15 +838,6 @@
 }
 
 void RemoteSuggestionsSchedulerImpl::ClearLastFetchAttemptTime() {
-  // Added during Feed rollout to help investigate https://crbug.com/908963.
-  base::TimeDelta attempt_age =
-      clock_->Now() -
-      profile_prefs_->GetTime(prefs::kSnippetLastFetchAttemptTime);
-  UMA_HISTOGRAM_CUSTOM_TIMES(
-      "ContentSuggestions.Feed.Scheduler.TimeSinceLastFetchOnClear",
-      attempt_age, base::Seconds(1), base::Days(7),
-      /*bucket_count=*/50);
-
   profile_prefs_->ClearPref(prefs::kSnippetLastFetchAttemptTime);
   // To mark the last fetch as stale, we need to keep the time in prefs, only
   // making sure it is long ago.
diff --git a/components/omnibox/browser/base_search_provider.cc b/components/omnibox/browser/base_search_provider.cc
index e9388f9..3f27113 100644
--- a/components/omnibox/browser/base_search_provider.cc
+++ b/components/omnibox/browser/base_search_provider.cc
@@ -176,9 +176,9 @@
   if (!template_url)
     return match;
   match.keyword = template_url->keyword();
-  match.image_dominant_color = suggestion.image_dominant_color();
-  match.image_url = suggestion.image_url();
-  match.entity_id = suggestion.entity_id();
+  match.image_dominant_color = suggestion.entity_info().dominant_color();
+  match.image_url = GURL(suggestion.entity_info().image_url());
+  match.entity_id = suggestion.entity_info().entity_id();
   match.contents = suggestion.match_contents();
   match.contents_class = suggestion.match_contents_class();
   match.suggestion_group_id = suggestion.suggestion_group_id();
@@ -240,7 +240,7 @@
   match.search_terms_args->original_query = original_query;
   match.search_terms_args->accepted_suggestion = accepted_suggestion;
   match.search_terms_args->additional_query_params =
-      suggestion.additional_query_params();
+      suggestion.entity_info().suggest_search_parameters();
   match.search_terms_args->append_extra_query_params_from_command_line =
       append_extra_query_params_from_command_line;
   // Must be set for deduplication and navigation. AutocompleteController will
diff --git a/components/omnibox/browser/search_provider.cc b/components/omnibox/browser/search_provider.cc
index 25ed228..4eb98de9 100644
--- a/components/omnibox/browser/search_provider.cc
+++ b/components/omnibox/browser/search_provider.cc
@@ -1620,9 +1620,9 @@
        ++i) {
     auto suggestion = results->suggest_results[i];
 
-    const auto& image_url = suggestion.image_url();
+    GURL image_url = GURL(suggestion.entity_info().image_url());
     if (!image_url.is_empty())
-      prefetch_image_urls.push_back(image_url);
+      prefetch_image_urls.push_back(std::move(image_url));
 
     if (suggestion.answer())
       suggestion.answer()->AddImageURLsTo(&prefetch_image_urls);
diff --git a/components/omnibox/browser/search_suggestion_parser.cc b/components/omnibox/browser/search_suggestion_parser.cc
index 19e0773..62d5907 100644
--- a/components/omnibox/browser/search_suggestion_parser.cc
+++ b/components/omnibox/browser/search_suggestion_parser.cc
@@ -255,7 +255,7 @@
     const std::u16string& match_contents,
     const std::u16string& match_contents_prefix,
     const std::u16string& annotation,
-    const omnibox::EntityInfo& entity_info,
+    omnibox::EntityInfo entity_info,
     const std::string& deletion_url,
     bool from_keyword,
     int relevance,
@@ -271,15 +271,14 @@
              deletion_url),
       suggestion_(suggestion),
       match_contents_prefix_(match_contents_prefix),
-      image_url_(GURL(entity_info.image_url())),
-      entity_info_(entity_info),
+      entity_info_(std::move(entity_info)),
       should_prefetch_(should_prefetch),
       should_prerender_(should_prerender) {
-  annotation_ = !entity_info.annotation().empty()
-                    ? base::UTF8ToUTF16(entity_info.annotation())
+  annotation_ = !entity_info_.annotation().empty()
+                    ? base::UTF8ToUTF16(entity_info_.annotation())
                     : annotation;
-  match_contents_ = !entity_info.name().empty()
-                        ? base::UTF8ToUTF16(entity_info.name())
+  match_contents_ = !entity_info_.name().empty()
+                        ? base::UTF8ToUTF16(entity_info_.name())
                         : match_contents;
   DCHECK(!match_contents_.empty());
   ClassifyMatchContents(true, input_text);
@@ -851,9 +850,9 @@
       results->suggest_results.push_back(SuggestResult(
           suggestion, match_type, subtypes[index],
           base::CollapseWhitespace(match_contents, false),
-          match_contents_prefix, annotation, entity_info, deletion_url,
-          is_keyword_result, relevance, relevances != nullptr, should_prefetch,
-          should_prerender, trimmed_input));
+          match_contents_prefix, annotation, std::move(entity_info),
+          deletion_url, is_keyword_result, relevance, relevances != nullptr,
+          should_prefetch, should_prerender, trimmed_input));
 
       if (answer_parsed_successfully) {
         results->suggest_results.back().SetAnswer(answer);
diff --git a/components/omnibox/browser/search_suggestion_parser.h b/components/omnibox/browser/search_suggestion_parser.h
index 9fe4c1f..9eace2d 100644
--- a/components/omnibox/browser/search_suggestion_parser.h
+++ b/components/omnibox/browser/search_suggestion_parser.h
@@ -144,7 +144,7 @@
                   const std::u16string& match_contents,
                   const std::u16string& match_contents_prefix,
                   const std::u16string& annotation,
-                  const omnibox::EntityInfo& entity_info,
+                  omnibox::EntityInfo entity_info,
                   const std::string& deletion_url,
                   bool from_keyword,
                   int relevance,
@@ -162,13 +162,6 @@
       return match_contents_prefix_;
     }
     const std::u16string& annotation() const { return annotation_; }
-    // Optional additional parameters to be added to the search URL.
-    const std::string& additional_query_params() const {
-      return entity_info_.suggest_search_parameters();
-    }
-    // Optional entity id for entity suggestions. Empty string means no entity
-    // ID.
-    const std::string& entity_id() const { return entity_info_.entity_id(); }
 
     void set_suggestion_group_id(
         absl::optional<omnibox::GroupId> suggestion_group_id) {
@@ -181,13 +174,7 @@
     void SetAnswer(const SuggestionAnswer& answer);
     const absl::optional<SuggestionAnswer>& answer() const { return answer_; }
 
-    // Optional image information. Used for entity suggestions. The dominant
-    // color can be used to paint the image placeholder while fetching the
-    // image.
-    const std::string& image_dominant_color() const {
-      return entity_info_.dominant_color();
-    }
-    const GURL& image_url() const { return image_url_; }
+    const omnibox::EntityInfo& entity_info() const { return entity_info_; }
 
     bool should_prefetch() const { return should_prefetch_; }
     bool should_prerender() const { return should_prerender_; }
@@ -225,8 +212,6 @@
     // Optional short answer to the input that produced this suggestion.
     absl::optional<SuggestionAnswer> answer_;
 
-    GURL image_url_;
-
     // Proto containing various pieces of data related to entity suggestions.
     omnibox::EntityInfo entity_info_;
 
diff --git a/components/omnibox/browser/search_suggestion_parser_unittest.cc b/components/omnibox/browser/search_suggestion_parser_unittest.cc
index 5aae6c5fb..e767e89 100644
--- a/components/omnibox/browser/search_suggestion_parser_unittest.cc
+++ b/components/omnibox/browser/search_suggestion_parser_unittest.cc
@@ -35,6 +35,17 @@
   return encoded_groups_info;
 }
 
+// (Rudimentary) mechanism comparing two protobuf MessageLite objects.
+// This mechanism should be sufficient as long as compared objects don't host
+// any maps.
+// TODO(ender): Improve the mechanism to be smarter about checking individual
+// fields and their values.
+bool ProtosAreEqual(const google::protobuf::MessageLite& actual,
+                    const google::protobuf::MessageLite& expected) {
+  return (actual.GetTypeName() == expected.GetTypeName()) &&
+         (actual.SerializeAsString() == expected.SerializeAsString());
+}
+
 }  // namespace
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -174,18 +185,18 @@
     const auto& suggestion_result = results.suggest_results[0];
     ASSERT_EQ(u"christmas", suggestion_result.suggestion());
     ASSERT_EQ(u"", suggestion_result.annotation());
-    ASSERT_EQ("", suggestion_result.entity_id());
-    // This entry has no image.
-    ASSERT_EQ("", suggestion_result.image_dominant_color());
-    ASSERT_EQ(GURL(), suggestion_result.image_url());
+    // This entry has no entity data
+    ASSERT_TRUE(ProtosAreEqual(suggestion_result.entity_info(),
+                               omnibox::EntityInfo::default_instance()));
   }
   {
     const auto& suggestion_result = results.suggest_results[1];
     ASSERT_EQ(u"christopher doe", suggestion_result.suggestion());
     ASSERT_EQ(u"American author", suggestion_result.annotation());
-    ASSERT_EQ("/m/065xxm", suggestion_result.entity_id());
-    ASSERT_EQ("#424242", suggestion_result.image_dominant_color());
-    ASSERT_EQ(GURL("http://example.com/a.png"), suggestion_result.image_url());
+    ASSERT_EQ("/m/065xxm", suggestion_result.entity_info().entity_id());
+    ASSERT_EQ("#424242", suggestion_result.entity_info().dominant_color());
+    ASSERT_EQ("http://example.com/a.png",
+              suggestion_result.entity_info().image_url());
   }
   ASSERT_EQ(3U, results.experiment_stats_v2s.size());
   {
@@ -808,38 +819,41 @@
 
   // For each suggestion, verify that the JSON fields were correctly parsed.
   ASSERT_EQ(u"the menu", results.suggest_results[0].suggestion());
+  ASSERT_TRUE(ProtosAreEqual(results.suggest_results[0].entity_info(),
+                             omnibox::EntityInfo::default_instance()));
   ASSERT_EQ(u"", results.suggest_results[0].annotation());
-  ASSERT_EQ("", results.suggest_results[0].image_dominant_color());
-  ASSERT_EQ("", results.suggest_results[0].image_url().spec());
-  ASSERT_EQ("", results.suggest_results[0].additional_query_params());
   // Empty "t" value from server results in suggestion being used instead.
   ASSERT_EQ(u"the menu", results.suggest_results[0].match_contents());
-  ASSERT_EQ("", results.suggest_results[0].entity_id());
 
   ASSERT_EQ(u"the menu", results.suggest_results[1].suggestion());
   ASSERT_EQ(u"2022 film", results.suggest_results[1].annotation());
-  ASSERT_EQ("#424242", results.suggest_results[1].image_dominant_color());
+  ASSERT_EQ("#424242",
+            results.suggest_results[1].entity_info().dominant_color());
   ASSERT_EQ(
       "https://encrypted-tbn0.gstatic.com/"
       "images?q=the+menu",
-      results.suggest_results[1].image_url().spec());
-  ASSERT_EQ("gs_ssp=eJzj4tVP1zc0LCwoKssryyg3YPTiKMlIVchNzSsFAGrSCGQ",
-            results.suggest_results[1].additional_query_params());
+      results.suggest_results[1].entity_info().image_url());
+  ASSERT_EQ(
+      "gs_ssp=eJzj4tVP1zc0LCwoKssryyg3YPTiKMlIVchNzSsFAGrSCGQ",
+      results.suggest_results[1].entity_info().suggest_search_parameters());
   ASSERT_EQ(u"The Menu", results.suggest_results[1].match_contents());
-  ASSERT_EQ("/g/11qprvnvhw", results.suggest_results[1].entity_id());
+  ASSERT_EQ("/g/11qprvnvhw",
+            results.suggest_results[1].entity_info().entity_id());
 
   ASSERT_EQ(u"the midnight club", results.suggest_results[2].suggestion());
   ASSERT_EQ(u"Thriller series", results.suggest_results[2].annotation());
-  ASSERT_EQ("#283e75", results.suggest_results[2].image_dominant_color());
+  ASSERT_EQ("#283e75",
+            results.suggest_results[2].entity_info().dominant_color());
   ASSERT_EQ(
       "https://encrypted-tbn0.gstatic.com/"
       "images?q=the+midnight+club",
-      results.suggest_results[2].image_url().spec());
+      results.suggest_results[2].entity_info().image_url());
   ASSERT_EQ(
       "gs_ssp=eJzj4tVP1zc0zMqrNCvJNkwyYPQSLMlIVcjNTMnLTM8oUUjOKU0CALmyCz8",
-      results.suggest_results[2].additional_query_params());
+      results.suggest_results[2].entity_info().suggest_search_parameters());
   ASSERT_EQ(u"The Midnight Club", results.suggest_results[2].match_contents());
-  ASSERT_EQ("/g/11jny6tk1b", results.suggest_results[2].entity_id());
+  ASSERT_EQ("/g/11jny6tk1b",
+            results.suggest_results[2].entity_info().entity_id());
 }
 
 TEST(SearchSuggestionParserTest, ParseSuggestionGroupInfo_FromProto) {
@@ -1296,40 +1310,19 @@
     // For each suggestion, verify that the JSON fields were correctly parsed.
     ASSERT_EQ(u"the menu", results.suggest_results[0].suggestion());
     ASSERT_EQ(u"", results.suggest_results[0].annotation());
-    ASSERT_EQ("", results.suggest_results[0].image_dominant_color());
-    ASSERT_EQ("", results.suggest_results[0].image_url().spec());
-    ASSERT_EQ("", results.suggest_results[0].additional_query_params());
+    ASSERT_TRUE(ProtosAreEqual(results.suggest_results[0].entity_info(),
+                               omnibox::EntityInfo::default_instance()));
+    ASSERT_TRUE(results.suggest_results[0].entity_info().image_url().empty());
     // Empty "t" value from server results in suggestion being used instead.
     ASSERT_EQ(u"the menu", results.suggest_results[0].match_contents());
-    ASSERT_EQ("", results.suggest_results[0].entity_id());
 
     ASSERT_EQ(u"the menu", results.suggest_results[1].suggestion());
-    ASSERT_EQ(base::UTF8ToUTF16(first_entity_info.annotation()),
-              results.suggest_results[1].annotation());
-    ASSERT_EQ(first_entity_info.dominant_color(),
-              results.suggest_results[1].image_dominant_color());
-    ASSERT_EQ(first_entity_info.image_url(),
-              results.suggest_results[1].image_url().spec());
-    ASSERT_EQ(first_entity_info.suggest_search_parameters(),
-              results.suggest_results[1].additional_query_params());
-    ASSERT_EQ(base::UTF8ToUTF16(first_entity_info.name()),
-              results.suggest_results[1].match_contents());
-    ASSERT_EQ(first_entity_info.entity_id(),
-              results.suggest_results[1].entity_id());
+    ASSERT_TRUE(ProtosAreEqual(results.suggest_results[1].entity_info(),
+                               first_entity_info));
 
     ASSERT_EQ(u"the midnight club", results.suggest_results[2].suggestion());
-    ASSERT_EQ(base::UTF8ToUTF16(second_entity_info.annotation()),
-              results.suggest_results[2].annotation());
-    ASSERT_EQ(second_entity_info.dominant_color(),
-              results.suggest_results[2].image_dominant_color());
-    ASSERT_EQ(second_entity_info.image_url(),
-              results.suggest_results[2].image_url().spec());
-    ASSERT_EQ(second_entity_info.suggest_search_parameters(),
-              results.suggest_results[2].additional_query_params());
-    ASSERT_EQ(base::UTF8ToUTF16(second_entity_info.name()),
-              results.suggest_results[2].match_contents());
-    ASSERT_EQ(second_entity_info.entity_id(),
-              results.suggest_results[2].entity_id());
+    ASSERT_TRUE(ProtosAreEqual(results.suggest_results[2].entity_info(),
+                               second_entity_info));
   }
 
   // If possible, fall back to individual JSON fields when attempting to parse
@@ -1388,39 +1381,42 @@
 
     // For each suggestion, verify that the JSON fields were correctly parsed.
     ASSERT_EQ(u"the menu", results.suggest_results[0].suggestion());
+    ASSERT_TRUE(ProtosAreEqual(results.suggest_results[0].entity_info(),
+                               omnibox::EntityInfo::default_instance()));
     ASSERT_EQ(u"", results.suggest_results[0].annotation());
-    ASSERT_EQ("", results.suggest_results[0].image_dominant_color());
-    ASSERT_EQ("", results.suggest_results[0].image_url().spec());
-    ASSERT_EQ("", results.suggest_results[0].additional_query_params());
     // Empty "t" value from server results in suggestion being used instead.
     ASSERT_EQ(u"the menu", results.suggest_results[0].match_contents());
-    ASSERT_EQ("", results.suggest_results[0].entity_id());
 
     ASSERT_EQ(u"the menu", results.suggest_results[1].suggestion());
     ASSERT_EQ(u"2022 film", results.suggest_results[1].annotation());
-    ASSERT_EQ("#424242", results.suggest_results[1].image_dominant_color());
+    ASSERT_EQ("#424242",
+              results.suggest_results[1].entity_info().dominant_color());
     ASSERT_EQ(
         "https://encrypted-tbn0.gstatic.com/"
         "images?q=the+menu",
-        results.suggest_results[1].image_url().spec());
-    ASSERT_EQ("gs_ssp=eJzj4tVP1zc0LCwoKssryyg3YPTiKMlIVchNzSsFAGrSCGQ",
-              results.suggest_results[1].additional_query_params());
+        results.suggest_results[1].entity_info().image_url());
+    ASSERT_EQ(
+        "gs_ssp=eJzj4tVP1zc0LCwoKssryyg3YPTiKMlIVchNzSsFAGrSCGQ",
+        results.suggest_results[1].entity_info().suggest_search_parameters());
     ASSERT_EQ(u"The Menu", results.suggest_results[1].match_contents());
-    ASSERT_EQ("/g/11qprvnvhw", results.suggest_results[1].entity_id());
+    ASSERT_EQ("/g/11qprvnvhw",
+              results.suggest_results[1].entity_info().entity_id());
 
     ASSERT_EQ(u"the midnight club", results.suggest_results[2].suggestion());
     ASSERT_EQ(u"Thriller series", results.suggest_results[2].annotation());
-    ASSERT_EQ("#283e75", results.suggest_results[2].image_dominant_color());
+    ASSERT_EQ("#283e75",
+              results.suggest_results[2].entity_info().dominant_color());
     ASSERT_EQ(
         "https://encrypted-tbn0.gstatic.com/"
         "images?q=the+midnight+club",
-        results.suggest_results[2].image_url().spec());
+        results.suggest_results[2].entity_info().image_url());
     ASSERT_EQ(
         "gs_ssp=eJzj4tVP1zc0zMqrNCvJNkwyYPQSLMlIVcjNTMnLTM8oUUjOKU0CALmyCz8",
-        results.suggest_results[2].additional_query_params());
+        results.suggest_results[2].entity_info().suggest_search_parameters());
     ASSERT_EQ(u"The Midnight Club",
               results.suggest_results[2].match_contents());
-    ASSERT_EQ("/g/11jny6tk1b", results.suggest_results[2].entity_id());
+    ASSERT_EQ("/g/11jny6tk1b",
+              results.suggest_results[2].entity_info().entity_id());
   }
 }
 
@@ -1721,41 +1717,38 @@
 
   // Most fields for a verbatim suggestion should be empty.
   ASSERT_EQ(u"1 + 1", results.suggest_results[0].suggestion());
+  ASSERT_TRUE(ProtosAreEqual(results.suggest_results[0].entity_info(),
+                             omnibox::EntityInfo::default_instance()));
   ASSERT_EQ(u"", results.suggest_results[0].annotation());
-  ASSERT_EQ("", results.suggest_results[0].image_dominant_color());
-  ASSERT_EQ("", results.suggest_results[0].image_url().spec());
-  ASSERT_EQ("", results.suggest_results[0].additional_query_params());
   ASSERT_EQ(u"1 + 1", results.suggest_results[0].match_contents());
-  ASSERT_EQ("", results.suggest_results[0].entity_id());
 
   // Calculator suggestions should have specific values for the |suggestion|,
   // |match_contents|, and |annotation| fields.
 #if !BUILDFLAG(IS_IOS) && !BUILDFLAG(IS_ANDROID)
   ASSERT_EQ(u"2", results.suggest_results[1].suggestion());
   ASSERT_EQ(u"2", results.suggest_results[1].annotation());
-  ASSERT_EQ("", results.suggest_results[1].image_dominant_color());
-  ASSERT_EQ("", results.suggest_results[1].image_url().spec());
-  ASSERT_EQ("", results.suggest_results[1].additional_query_params());
+  ASSERT_TRUE(ProtosAreEqual(results.suggest_results[1].entity_info(),
+                             omnibox::EntityInfo::default_instance()));
   ASSERT_EQ(u"1 + 1", results.suggest_results[1].match_contents());
-  ASSERT_EQ("", results.suggest_results[1].entity_id());
 #else
   ASSERT_EQ(u"2", results.suggest_results[1].suggestion());
   ASSERT_EQ(u"", results.suggest_results[1].annotation());
-  ASSERT_EQ("", results.suggest_results[1].image_dominant_color());
-  ASSERT_EQ("", results.suggest_results[1].image_url().spec());
-  ASSERT_EQ("", results.suggest_results[1].additional_query_params());
+  ASSERT_TRUE(ProtosAreEqual(results.suggest_results[1].entity_info(),
+                             omnibox::EntityInfo::default_instance()));
   ASSERT_EQ(u"= 2", results.suggest_results[1].match_contents());
-  ASSERT_EQ("", results.suggest_results[1].entity_id());
 #endif
 
   // Entity data should be correctly sourced as usual.
   ASSERT_EQ(u"1 + 1", results.suggest_results[2].suggestion());
   ASSERT_EQ(u"Song", results.suggest_results[2].annotation());
-  ASSERT_EQ("#424242", results.suggest_results[2].image_dominant_color());
+  ASSERT_EQ("#424242",
+            results.suggest_results[2].entity_info().dominant_color());
   ASSERT_EQ("https://encrypted-tbn0.gstatic.com/images?q=song",
-            results.suggest_results[2].image_url().spec());
-  ASSERT_EQ("gs_ssp=eJzj4tFP1zcsNjAzMykwKDZg9GI1VNBWMAQAOlEEsA",
-            results.suggest_results[2].additional_query_params());
+            results.suggest_results[2].entity_info().image_url());
+  ASSERT_EQ(
+      "gs_ssp=eJzj4tFP1zcsNjAzMykwKDZg9GI1VNBWMAQAOlEEsA",
+      results.suggest_results[2].entity_info().suggest_search_parameters());
   ASSERT_EQ(u"1+1", results.suggest_results[2].match_contents());
-  ASSERT_EQ("/g/1s0664p0s", results.suggest_results[2].entity_id());
+  ASSERT_EQ("/g/1s0664p0s",
+            results.suggest_results[2].entity_info().entity_id());
 }
diff --git a/components/page_load_metrics/browser/page_load_tracker.cc b/components/page_load_metrics/browser/page_load_tracker.cc
index 4243062..bef36c5 100644
--- a/components/page_load_metrics/browser/page_load_tracker.cc
+++ b/components/page_load_metrics/browser/page_load_tracker.cc
@@ -665,8 +665,8 @@
 
 void PageLoadTracker::StopTracking() {
   did_stop_tracking_ = true;
-  observers_.clear();
   observers_map_.clear();
+  observers_.clear();
 }
 
 void PageLoadTracker::AddObserver(
diff --git a/components/page_load_metrics/browser/page_load_tracker.h b/components/page_load_metrics/browser/page_load_tracker.h
index f29af1b9..5487c12 100644
--- a/components/page_load_metrics/browser/page_load_tracker.h
+++ b/components/page_load_metrics/browser/page_load_tracker.h
@@ -532,9 +532,7 @@
 
   // Observer's name pointer to instance map. Can be raw_ptr as the instance is
   // owned `observers` above, and is removed from the map on destruction.
-  base::flat_map<
-      const char*,
-      base::raw_ptr<PageLoadMetricsObserverInterface, DanglingUntriaged>>
+  base::flat_map<const char*, base::raw_ptr<PageLoadMetricsObserverInterface>>
       observers_map_;
 
   PageLoadMetricsUpdateDispatcher metrics_update_dispatcher_;
diff --git a/components/password_manager/core/browser/sync_credentials_filter_unittest.cc b/components/password_manager/core/browser/sync_credentials_filter_unittest.cc
index 4f32cd4..768adf77 100644
--- a/components/password_manager/core/browser/sync_credentials_filter_unittest.cc
+++ b/components/password_manager/core/browser/sync_credentials_filter_unittest.cc
@@ -16,6 +16,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/metrics/user_action_tester.h"
 #include "base/test/scoped_feature_list.h"
+#include "components/autofill/core/browser/ui/suggestion.h"
 #include "components/password_manager/core/browser/fake_form_fetcher.h"
 #include "components/password_manager/core/browser/mock_password_store_interface.h"
 #include "components/password_manager/core/browser/mock_webauthn_credentials_delegate.h"
@@ -49,6 +50,8 @@
             features::kPasswordReuseDetectionEnabled)) {
       return;
     }
+    ON_CALL(webauthn_credentials_delegate_, GetWebAuthnSuggestions)
+        .WillByDefault(testing::ReturnRef(webauthn_suggestions_));
 
     // Initializes and configures prefs.
     prefs_ = std::make_unique<TestingPrefServiceSimple>();
@@ -97,6 +100,7 @@
   scoped_refptr<testing::NiceMock<MockPasswordStoreInterface>> password_store_ =
       new testing::NiceMock<MockPasswordStoreInterface>;
   MockWebAuthnCredentialsDelegate webauthn_credentials_delegate_;
+  absl::optional<std::vector<autofill::Suggestion>> webauthn_suggestions_;
   bool is_incognito_ = false;
   raw_ptr<signin::IdentityManager> identity_manager_;
   std::unique_ptr<TestingPrefServiceSimple> prefs_;
@@ -164,16 +168,7 @@
   std::unique_ptr<SyncCredentialsFilter> filter_;
 };
 
-// TODO(crbug.com/1404890): This test fails on iOS devices.
-#if BUILDFLAG(IS_IOS) && !TARGET_OS_SIMULATOR
-#define MAYBE_ReportFormLoginSuccess_ExistingSyncCredentials \
-  DISABLED_ReportFormLoginSuccess_ExistingSyncCredentials
-#else
-#define MAYBE_ReportFormLoginSuccess_ExistingSyncCredentials \
-  ReportFormLoginSuccess_ExistingSyncCredentials
-#endif
-TEST_P(CredentialsFilterTest,
-       MAYBE_ReportFormLoginSuccess_ExistingSyncCredentials) {
+TEST_P(CredentialsFilterTest, ReportFormLoginSuccess_ExistingSyncCredentials) {
   FakeSigninAs("user@gmail.com");
   SetSyncingPasswords(true);
 
@@ -183,15 +178,7 @@
   EXPECT_EQ(1, tester.GetActionCount(kFilledAndLoginActionName));
 }
 
-// TODO(crbug.com/1404890): This test fails on iOS devices.
-#if BUILDFLAG(IS_IOS) && !TARGET_OS_SIMULATOR
-#define MAYBE_ReportFormLoginSuccess_NewSyncCredentials \
-  DISABLED_ReportFormLoginSuccess_NewSyncCredentials
-#else
-#define MAYBE_ReportFormLoginSuccess_NewSyncCredentials \
-  ReportFormLoginSuccess_NewSyncCredentials
-#endif
-TEST_P(CredentialsFilterTest, MAYBE_ReportFormLoginSuccess_NewSyncCredentials) {
+TEST_P(CredentialsFilterTest, ReportFormLoginSuccess_NewSyncCredentials) {
   FakeSigninAs("user@gmail.com");
   SetSyncingPasswords(true);
 
@@ -201,16 +188,7 @@
   EXPECT_EQ(0, tester.GetActionCount(kFilledAndLoginActionName));
 }
 
-// TODO(crbug.com/1404890): This test fails on iOS devices.
-#if BUILDFLAG(IS_IOS) && !TARGET_OS_SIMULATOR
-#define MAYBE_ReportFormLoginSuccess_GAIANotSyncCredentials \
-  DISABLED_ReportFormLoginSuccess_GAIANotSyncCredentials
-#else
-#define MAYBE_ReportFormLoginSuccess_GAIANotSyncCredentials \
-  ReportFormLoginSuccess_GAIANotSyncCredentials
-#endif
-TEST_P(CredentialsFilterTest,
-       MAYBE_ReportFormLoginSuccess_GAIANotSyncCredentials) {
+TEST_P(CredentialsFilterTest, ReportFormLoginSuccess_GAIANotSyncCredentials) {
   const char kOtherUsername[] = "other_user@gmail.com";
   const char16_t kOtherUsername16[] = u"other_user@gmail.com";
   FakeSigninAs(kOtherUsername);
@@ -223,15 +201,7 @@
   EXPECT_EQ(0, tester.GetActionCount(kFilledAndLoginActionName));
 }
 
-// TODO(crbug.com/1404890): This test fails on iOS devices.
-#if BUILDFLAG(IS_IOS) && !TARGET_OS_SIMULATOR
-#define MAYBE_ReportFormLoginSuccess_NotGAIACredentials \
-  DISABLED_ReportFormLoginSuccess_NotGAIACredentials
-#else
-#define MAYBE_ReportFormLoginSuccess_NotGAIACredentials \
-  ReportFormLoginSuccess_NotGAIACredentials
-#endif
-TEST_P(CredentialsFilterTest, MAYBE_ReportFormLoginSuccess_NotGAIACredentials) {
+TEST_P(CredentialsFilterTest, ReportFormLoginSuccess_NotGAIACredentials) {
   pending_ = SimpleNonGaiaForm("user@gmail.com");
   FakeSigninAs("user@gmail.com");
   SetSyncingPasswords(true);
@@ -242,15 +212,7 @@
   EXPECT_EQ(0, tester.GetActionCount(kFilledAndLoginActionName));
 }
 
-// TODO(crbug.com/1404890): This test fails on iOS devices.
-#if BUILDFLAG(IS_IOS) && !TARGET_OS_SIMULATOR
-#define MAYBE_ReportFormLoginSuccess_NotSyncing \
-  DISABLED_ReportFormLoginSuccess_NotSyncing
-#else
-#define MAYBE_ReportFormLoginSuccess_NotSyncing \
-  ReportFormLoginSuccess_NotSyncing
-#endif
-TEST_P(CredentialsFilterTest, MAYBE_ReportFormLoginSuccess_NotSyncing) {
+TEST_P(CredentialsFilterTest, ReportFormLoginSuccess_NotSyncing) {
   FakeSigninAs("user@gmail.com");
   SetSyncingPasswords(false);
 
diff --git a/components/privacy_sandbox/privacy_sandbox_settings.cc b/components/privacy_sandbox/privacy_sandbox_settings.cc
index b53b642..b1dc5e8 100644
--- a/components/privacy_sandbox/privacy_sandbox_settings.cc
+++ b/components/privacy_sandbox/privacy_sandbox_settings.cc
@@ -60,6 +60,11 @@
 
 }  // namespace
 
+// static
+bool PrivacySandboxSettings::IsAllowed(Status status) {
+  return status == Status::kAllowed;
+}
+
 PrivacySandboxSettings::PrivacySandboxSettings(
     std::unique_ptr<Delegate> delegate,
     HostContentSettingsMap* host_content_settings_map,
@@ -97,7 +102,10 @@
 bool PrivacySandboxSettings::IsTopicsAllowed() const {
   // M1 specific
   if (base::FeatureList::IsEnabled(privacy_sandbox::kPrivacySandboxSettings4)) {
-    return IsM1PrivacySandboxApiEnabled(prefs::kPrivacySandboxM1TopicsEnabled);
+    // TODO(crbug.com/1395437): Record metrics here when privacy sandbox is not
+    // allowed.
+    return IsAllowed(GetM1PrivacySandboxApiEnabledStatus(
+        prefs::kPrivacySandboxM1TopicsEnabled));
   }
 
   // Topics API calculation should be prevented if the user has blocked 3PC
@@ -120,7 +128,10 @@
     const GURL& url) const {
   // M1 specific
   if (base::FeatureList::IsEnabled(privacy_sandbox::kPrivacySandboxSettings4)) {
-    return IsTopicsAllowed() && IsAccessAllowed(top_frame_origin, url);
+    // TODO(crbug.com/1395437): Record metrics here when privacy sandbox is not
+    // allowed due to site access blocked.
+    return IsTopicsAllowed() &&
+           IsAllowed(GetSiteAccessAllowedStatus(top_frame_origin, url));
   }
 
   // If the Topics API is disabled completely, it is not available in any
@@ -203,9 +214,12 @@
     const url::Origin& reporting_origin) const {
   // M1 specific
   if (base::FeatureList::IsEnabled(privacy_sandbox::kPrivacySandboxSettings4)) {
-    return IsM1PrivacySandboxApiEnabled(
-               prefs::kPrivacySandboxM1AdMeasurementEnabled) &&
-           IsAccessAllowed(top_frame_origin, reporting_origin.GetURL());
+    // TODO(crbug.com/1395437): Record metrics here when privacy sandbox is not
+    // allowed.
+    return IsAllowed(GetM1PrivacySandboxApiEnabledStatus(
+               prefs::kPrivacySandboxM1AdMeasurementEnabled)) &&
+           IsAllowed(GetSiteAccessAllowedStatus(top_frame_origin,
+                                                reporting_origin.GetURL()));
   }
 
   return IsPrivacySandboxEnabledForContext(top_frame_origin,
@@ -330,9 +344,12 @@
     const url::Origin& top_frame_origin,
     const url::Origin& auction_party) const {
   if (base::FeatureList::IsEnabled(privacy_sandbox::kPrivacySandboxSettings4)) {
-    return IsM1PrivacySandboxApiEnabled(
-               prefs::kPrivacySandboxM1FledgeEnabled) &&
-           IsAccessAllowed(top_frame_origin, auction_party.GetURL());
+    // TODO(crbug.com/1395437): Record metrics here when privacy sandbox is not
+    // allowed.
+    return IsAllowed(GetM1PrivacySandboxApiEnabledStatus(
+               prefs::kPrivacySandboxM1FledgeEnabled)) &&
+           IsAllowed(GetSiteAccessAllowedStatus(top_frame_origin,
+                                                auction_party.GetURL()));
   }
 
   return IsPrivacySandboxEnabledForContext(top_frame_origin,
@@ -342,6 +359,14 @@
 bool PrivacySandboxSettings::IsSharedStorageAllowed(
     const url::Origin& top_frame_origin,
     const url::Origin& accessing_origin) const {
+  if (base::FeatureList::IsEnabled(privacy_sandbox::kPrivacySandboxSettings4)) {
+    // TODO(crbug.com/1395437): Record metrics here when privacy sandbox is not
+    // allowed.
+    return IsAllowed(GetPrivacySandboxAllowedStatus()) &&
+           IsAllowed(GetSiteAccessAllowedStatus(top_frame_origin,
+                                                accessing_origin.GetURL()));
+  }
+
   // Ensures that Shared Storage is only allowed if both Privacy Sandbox is
   // enabled and full cookie access is enabled for this context.
   return IsPrivacySandboxEnabledForContext(top_frame_origin,
@@ -351,25 +376,31 @@
 bool PrivacySandboxSettings::IsSharedStorageSelectURLAllowed(
     const url::Origin& top_frame_origin,
     const url::Origin& accessing_origin) const {
-  // TODO(crbug.com/1378703): Respect appropriate M1 pref and site data settings
-  // when release 4 is enabled.
+  if (base::FeatureList::IsEnabled(privacy_sandbox::kPrivacySandboxSettings4)) {
+    // TODO(crbug.com/1395437): Record metrics here when privacy sandbox is not
+    // allowed.
+    return IsFledgeAllowed(top_frame_origin, accessing_origin);
+  }
+
   return IsSharedStorageAllowed(top_frame_origin, accessing_origin);
 }
 
 bool PrivacySandboxSettings::IsPrivateAggregationAllowed(
     const url::Origin& top_frame_origin,
     const url::Origin& reporting_origin) const {
+  if (base::FeatureList::IsEnabled(privacy_sandbox::kPrivacySandboxSettings4)) {
+    // TODO(crbug.com/1395437): Record metrics here when privacy sandbox is not
+    // allowed.
+    return IsAttributionReportingAllowed(top_frame_origin, reporting_origin);
+  }
+
   return IsPrivacySandboxEnabledForContext(top_frame_origin,
                                            reporting_origin.GetURL());
 }
 
 bool PrivacySandboxSettings::IsPrivacySandboxEnabled() const {
-  // If the delegate is restricting access the Privacy Sandbox is disabled.
-  if (delegate_->IsPrivacySandboxRestricted()) {
-    return false;
-  }
-
-  if (delegate_->IsIncognitoProfile()) {
+  PrivacySandboxSettings::Status status = GetPrivacySandboxAllowedStatus();
+  if (!IsAllowed(status)) {
     return false;
   }
 
@@ -477,7 +508,8 @@
   }
 }
 
-bool PrivacySandboxSettings::IsAccessAllowed(
+PrivacySandboxSettings::Status
+PrivacySandboxSettings::GetSiteAccessAllowedStatus(
     const url::Origin& top_frame_origin,
     const GURL& url) const {
   // Relying on |host_content_settings_map_| instead of |cookie_settings_|
@@ -485,22 +517,35 @@
   // access Site data (aka ContentSettingsType::COOKIES) without considering any
   // 3P cookie blocking setting.
   return content_settings::CookieSettingsBase::IsAllowed(
-      host_content_settings_map_->GetContentSetting(
-          url, top_frame_origin.GetURL(), ContentSettingsType::COOKIES));
+             host_content_settings_map_->GetContentSetting(
+                 url, top_frame_origin.GetURL(), ContentSettingsType::COOKIES))
+             ? Status::kAllowed
+             : Status::kSiteDataAccesssDisallowed;
 }
 
-bool PrivacySandboxSettings::IsM1PrivacySandboxApiEnabled(
+PrivacySandboxSettings::Status
+PrivacySandboxSettings::GetPrivacySandboxAllowedStatus() const {
+  if (delegate_->IsIncognitoProfile()) {
+    return Status::kIncognitoProfile;
+  }
+
+  if (IsPrivacySandboxRestricted()) {
+    return Status::kRestricted;
+  }
+
+  return Status::kAllowed;
+}
+
+PrivacySandboxSettings::Status
+PrivacySandboxSettings::GetM1PrivacySandboxApiEnabledStatus(
     const std::string& pref_name) const {
   DCHECK(pref_name == prefs::kPrivacySandboxM1TopicsEnabled ||
          pref_name == prefs::kPrivacySandboxM1FledgeEnabled ||
          pref_name == prefs::kPrivacySandboxM1AdMeasurementEnabled);
 
-  if (delegate_->IsIncognitoProfile()) {
-    return false;
-  }
-
-  if (IsPrivacySandboxRestricted()) {
-    return false;
+  PrivacySandboxSettings::Status status = GetPrivacySandboxAllowedStatus();
+  if (!IsAllowed(status)) {
+    return status;
   }
 
   // For Measurement and Relevance APIs, we explicitly do not require the
@@ -508,10 +553,12 @@
   // allow for local testing.
   if (base::FeatureList::IsEnabled(
           privacy_sandbox::kOverridePrivacySandboxSettingsLocalTesting)) {
-    return true;
+    return Status::kAllowed;
   }
 
-  return pref_service_->GetBoolean(pref_name);
+  status = (pref_service_->GetBoolean(pref_name)) ? Status::kAllowed
+                                                  : Status::kApisDisabled;
+  return status;
 }
 
 }  // namespace privacy_sandbox
diff --git a/components/privacy_sandbox/privacy_sandbox_settings.h b/components/privacy_sandbox/privacy_sandbox_settings.h
index 8e24aaa..c20836f 100644
--- a/components/privacy_sandbox/privacy_sandbox_settings.h
+++ b/components/privacy_sandbox/privacy_sandbox_settings.h
@@ -236,13 +236,31 @@
   void SetTopicsDataAccessibleFromNow() const;
 
  private:
+  enum class Status {
+    kAllowed,
+    kRestricted,
+    kIncognitoProfile,
+    kApisDisabled,
+    kSiteDataAccesssDisallowed,
+    kMaxValue = kSiteDataAccesssDisallowed,
+  };
+
+  static bool IsAllowed(Status status);
+
   // Whether the site associated with the URL is allowed to access privacy
   // sandbox APIs within the context of |top_frame_origin|.
-  bool IsAccessAllowed(const url::Origin& top_frame_origin,
-                       const GURL& url) const;
+  Status GetSiteAccessAllowedStatus(const url::Origin& top_frame_origin,
+                                    const GURL& url) const;
+
+  // Whether the privacy sandbox APIs can be allowed given the current
+  // environment. For example, the privacy sandbox is always disabled in
+  // Incognito and for restricted accounts.
+  Status GetPrivacySandboxAllowedStatus() const;
+
   // Whether the privacy sandbox associated with  the |pref_name| is enabled.
-  // For individual sites, check as well with IsSiteDataAllowed.
-  bool IsM1PrivacySandboxApiEnabled(const std::string& pref_name) const;
+  // For individual sites, check as well with GetSiteAccessAllowedStatus.
+  Status GetM1PrivacySandboxApiEnabledStatus(
+      const std::string& pref_name) const;
 
   base::ObserverList<Observer>::Unchecked observers_;
 
diff --git a/components/privacy_sandbox/privacy_sandbox_settings_unittest.cc b/components/privacy_sandbox/privacy_sandbox_settings_unittest.cc
index 260fc4bb..3eb49de 100644
--- a/components/privacy_sandbox/privacy_sandbox_settings_unittest.cc
+++ b/components/privacy_sandbox/privacy_sandbox_settings_unittest.cc
@@ -60,6 +60,7 @@
     InputKey::kAdMeasurementSourceOrigin;
 constexpr auto kAdMeasurementDestinationOrigin =
     InputKey::kAdMeasurementDestinationOrigin;
+constexpr auto kAccessingOrigin = InputKey::kAccessingOrigin;
 
 // using enum privacy_sandbox_test_util::TestOutput;
 using privacy_sandbox_test_util::OutputKey;
@@ -70,6 +71,11 @@
 constexpr auto kIsAttributionReportingAllowed =
     OutputKey::kIsAttributionReportingAllowed;
 constexpr auto kMaySendAttributionReport = OutputKey::kMaySendAttributionReport;
+constexpr auto kIsSharedStorageAllowed = OutputKey::kIsSharedStorageAllowed;
+constexpr auto kIsSharedStorageSelectURLAllowed =
+    OutputKey::kIsSharedStorageSelectURLAllowed;
+constexpr auto kIsPrivateAggregationAllowed =
+    OutputKey::kIsPrivateAggregationAllowed;
 
 // using enum ContentSetting;
 constexpr auto CONTENT_SETTING_ALLOW = ContentSetting::CONTENT_SETTING_ALLOW;
@@ -1021,16 +1027,18 @@
           {kTopFrameOrigin, url::Origin::Create(GURL("https://top-frame.com"))},
           {kTopicsURL, GURL("https://embedded.com")},
           {MultipleInputKeys{kFledgeAuctionPartyOrigin,
-                             kAdMeasurementReportingOrigin},
+                             kAdMeasurementReportingOrigin, kAccessingOrigin},
            url::Origin::Create(GURL("https://embedded.com"))},
           {kAdMeasurementSourceOrigin,
            url::Origin::Create(GURL("https://source-origin.com"))},
           {kAdMeasurementDestinationOrigin,
            url::Origin::Create(GURL("https://dest-origin.com"))}},
       TestOutput{
-          {MultipleOutputKeys{kIsTopicsAllowed, kIsTopicsAllowedForContext,
-                              kIsFledgeAllowed, kIsAttributionReportingAllowed,
-                              kMaySendAttributionReport},
+          {MultipleOutputKeys{
+               kIsTopicsAllowed, kIsTopicsAllowedForContext, kIsFledgeAllowed,
+               kIsAttributionReportingAllowed, kMaySendAttributionReport,
+               kIsSharedStorageAllowed, kIsSharedStorageSelectURLAllowed,
+               kIsPrivateAggregationAllowed},
            true}});
 }
 
@@ -1045,18 +1053,19 @@
           {kTopFrameOrigin, url::Origin::Create(GURL("https://top-frame.com"))},
           {kTopicsURL, GURL("https://embedded.com")},
           {MultipleInputKeys{kFledgeAuctionPartyOrigin,
-                             kAdMeasurementReportingOrigin},
+                             kAdMeasurementReportingOrigin, kAccessingOrigin},
            url::Origin::Create(GURL("https://embedded.com"))},
           {kAdMeasurementSourceOrigin,
            url::Origin::Create(GURL("https://source-origin.com"))},
           {kAdMeasurementDestinationOrigin,
-           url::Origin::Create(GURL("https://dest-origin.com"))},
-      },
+           url::Origin::Create(GURL("https://dest-origin.com"))}},
       TestOutput{
-          {MultipleOutputKeys{kIsTopicsAllowed, kIsTopicsAllowedForContext,
-                              kIsFledgeAllowed, kIsAttributionReportingAllowed,
-                              kMaySendAttributionReport},
-           false}});
+          {MultipleOutputKeys{
+               kIsTopicsAllowed, kIsTopicsAllowedForContext, kIsFledgeAllowed,
+               kIsAttributionReportingAllowed, kMaySendAttributionReport,
+               kIsSharedStorageSelectURLAllowed, kIsPrivateAggregationAllowed},
+           false},
+          {kIsSharedStorageAllowed, true}});
 }
 
 TEST_F(PrivacySandboxSettingsM1Test, CookieControlsModeHasNoEffect) {
@@ -1071,16 +1080,18 @@
           {kTopFrameOrigin, url::Origin::Create(GURL("https://top-frame.com"))},
           {kTopicsURL, GURL("https://embedded.com")},
           {MultipleInputKeys{kFledgeAuctionPartyOrigin,
-                             kAdMeasurementReportingOrigin},
+                             kAdMeasurementReportingOrigin, kAccessingOrigin},
            url::Origin::Create(GURL("https://embedded.com"))},
           {kAdMeasurementSourceOrigin,
            url::Origin::Create(GURL("https://source-origin.com"))},
           {kAdMeasurementDestinationOrigin,
            url::Origin::Create(GURL("https://dest-origin.com"))}},
       TestOutput{
-          {MultipleOutputKeys{kIsTopicsAllowed, kIsTopicsAllowedForContext,
-                              kIsFledgeAllowed, kIsAttributionReportingAllowed,
-                              kMaySendAttributionReport},
+          {MultipleOutputKeys{
+               kIsTopicsAllowed, kIsTopicsAllowedForContext, kIsFledgeAllowed,
+               kIsAttributionReportingAllowed, kMaySendAttributionReport,
+               kIsSharedStorageAllowed, kIsSharedStorageSelectURLAllowed,
+               kIsPrivateAggregationAllowed},
            true}});
 }
 
@@ -1099,18 +1110,19 @@
           {kTopFrameOrigin, url::Origin::Create(GURL("https://top-frame.com"))},
           {kTopicsURL, GURL("https://embedded.com")},
           {MultipleInputKeys{kFledgeAuctionPartyOrigin,
-                             kAdMeasurementReportingOrigin},
+                             kAdMeasurementReportingOrigin, kAccessingOrigin},
            url::Origin::Create(GURL("https://embedded.com"))},
           {kAdMeasurementSourceOrigin,
            url::Origin::Create(GURL("https://source-origin.com"))},
           {kAdMeasurementDestinationOrigin,
            url::Origin::Create(GURL("https://dest-origin.com"))}},
-      TestOutput{
-          {kIsTopicsAllowed, true},
-          {MultipleOutputKeys{kIsTopicsAllowedForContext, kIsFledgeAllowed,
-                              kIsAttributionReportingAllowed,
-                              kMaySendAttributionReport},
-           false}});
+      TestOutput{{kIsTopicsAllowed, true},
+                 {MultipleOutputKeys{
+                      kIsTopicsAllowedForContext, kIsFledgeAllowed,
+                      kIsAttributionReportingAllowed, kMaySendAttributionReport,
+                      kIsSharedStorageAllowed, kIsSharedStorageSelectURLAllowed,
+                      kIsPrivateAggregationAllowed},
+                  false}});
 }
 
 TEST_F(PrivacySandboxSettingsM1Test, SiteDataAllowDoesntOverridePref) {
@@ -1130,17 +1142,18 @@
           {kTopFrameOrigin, url::Origin::Create(GURL("https://top-frame.com"))},
           {kTopicsURL, GURL("https://embedded.com")},
           {MultipleInputKeys{kFledgeAuctionPartyOrigin,
-                             kAdMeasurementReportingOrigin},
+                             kAdMeasurementReportingOrigin, kAccessingOrigin},
            url::Origin::Create(GURL("https://embedded.com"))},
           {kAdMeasurementSourceOrigin,
            url::Origin::Create(GURL("https://source-origin.com"))},
           {kAdMeasurementDestinationOrigin,
            url::Origin::Create(GURL("https://dest-origin.com"))}},
       TestOutput{
-          {kIsTopicsAllowed, false},
-          {MultipleOutputKeys{kIsTopicsAllowedForContext, kIsFledgeAllowed,
-                              kIsAttributionReportingAllowed,
-                              kMaySendAttributionReport},
+          {kIsSharedStorageAllowed, true},
+          {MultipleOutputKeys{
+               kIsTopicsAllowed, kIsTopicsAllowedForContext, kIsFledgeAllowed,
+               kIsAttributionReportingAllowed, kMaySendAttributionReport,
+               kIsSharedStorageSelectURLAllowed, kIsPrivateAggregationAllowed},
            false}});
 }
 
@@ -1159,16 +1172,18 @@
           {kTopFrameOrigin, url::Origin::Create(GURL("https://top-frame.com"))},
           {kTopicsURL, GURL("https://embedded.com")},
           {MultipleInputKeys{kFledgeAuctionPartyOrigin,
-                             kAdMeasurementReportingOrigin},
+                             kAdMeasurementReportingOrigin, kAccessingOrigin},
            url::Origin::Create(GURL("https://embedded.com"))},
           {kAdMeasurementSourceOrigin,
            url::Origin::Create(GURL("https://source-origin.com"))},
           {kAdMeasurementDestinationOrigin,
            url::Origin::Create(GURL("https://dest-origin.com"))}},
       TestOutput{
-          {MultipleOutputKeys{kIsTopicsAllowed, kIsTopicsAllowedForContext,
-                              kIsFledgeAllowed, kIsAttributionReportingAllowed,
-                              kMaySendAttributionReport},
+          {MultipleOutputKeys{
+               kIsTopicsAllowed, kIsTopicsAllowedForContext, kIsFledgeAllowed,
+               kIsAttributionReportingAllowed, kMaySendAttributionReport,
+               kIsSharedStorageAllowed, kIsSharedStorageSelectURLAllowed,
+               kIsPrivateAggregationAllowed},
            true}});
 }
 
@@ -1187,16 +1202,18 @@
           {kTopFrameOrigin, url::Origin::Create(GURL("https://top-frame.com"))},
           {kTopicsURL, GURL("https://embedded.com")},
           {MultipleInputKeys{kFledgeAuctionPartyOrigin,
-                             kAdMeasurementReportingOrigin},
+                             kAdMeasurementReportingOrigin, kAccessingOrigin},
            url::Origin::Create(GURL("https://embedded.com"))},
           {kAdMeasurementSourceOrigin,
            url::Origin::Create(GURL("https://source-origin.com"))},
           {kAdMeasurementDestinationOrigin,
            url::Origin::Create(GURL("https://dest-origin.com"))}},
       TestOutput{
-          {MultipleOutputKeys{kIsTopicsAllowed, kIsTopicsAllowedForContext,
-                              kIsFledgeAllowed, kIsAttributionReportingAllowed,
-                              kMaySendAttributionReport},
+          {MultipleOutputKeys{
+               kIsTopicsAllowed, kIsTopicsAllowedForContext, kIsFledgeAllowed,
+               kIsAttributionReportingAllowed, kMaySendAttributionReport,
+               kIsSharedStorageAllowed, kIsSharedStorageSelectURLAllowed,
+               kIsPrivateAggregationAllowed},
            true}});
 }
 
@@ -1215,18 +1232,19 @@
           {kTopFrameOrigin, url::Origin::Create(GURL("https://top-frame.com"))},
           {kTopicsURL, GURL("https://embedded.com")},
           {MultipleInputKeys{kFledgeAuctionPartyOrigin,
-                             kAdMeasurementReportingOrigin},
+                             kAdMeasurementReportingOrigin, kAccessingOrigin},
            url::Origin::Create(GURL("https://embedded.com"))},
           {kAdMeasurementSourceOrigin,
            url::Origin::Create(GURL("https://source-origin.com"))},
           {kAdMeasurementDestinationOrigin,
            url::Origin::Create(GURL("https://dest-origin.com"))}},
-      TestOutput{
-          {kIsTopicsAllowed, true},
-          {MultipleOutputKeys{kIsTopicsAllowedForContext, kIsFledgeAllowed,
-                              kIsAttributionReportingAllowed,
-                              kMaySendAttributionReport},
-           false}});
+      TestOutput{{kIsTopicsAllowed, true},
+                 {MultipleOutputKeys{
+                      kIsTopicsAllowedForContext, kIsFledgeAllowed,
+                      kIsAttributionReportingAllowed, kMaySendAttributionReport,
+                      kIsSharedStorageAllowed, kIsSharedStorageSelectURLAllowed,
+                      kIsPrivateAggregationAllowed},
+                  false}});
 }
 
 TEST_F(PrivacySandboxSettingsM1Test, ApisAreOffInIncognito) {
@@ -1240,16 +1258,18 @@
           {kTopFrameOrigin, url::Origin::Create(GURL("https://top-frame.com"))},
           {kTopicsURL, GURL("https://embedded.com")},
           {MultipleInputKeys{kFledgeAuctionPartyOrigin,
-                             kAdMeasurementReportingOrigin},
+                             kAdMeasurementReportingOrigin, kAccessingOrigin},
            url::Origin::Create(GURL("https://embedded.com"))},
           {kAdMeasurementSourceOrigin,
            url::Origin::Create(GURL("https://source-origin.com"))},
           {kAdMeasurementDestinationOrigin,
            url::Origin::Create(GURL("https://dest-origin.com"))}},
       TestOutput{
-          {MultipleOutputKeys{kIsTopicsAllowed, kIsTopicsAllowedForContext,
-                              kIsFledgeAllowed, kIsAttributionReportingAllowed,
-                              kMaySendAttributionReport},
+          {MultipleOutputKeys{
+               kIsTopicsAllowed, kIsTopicsAllowedForContext, kIsFledgeAllowed,
+               kIsAttributionReportingAllowed, kMaySendAttributionReport,
+               kIsSharedStorageAllowed, kIsSharedStorageSelectURLAllowed,
+               kIsPrivateAggregationAllowed},
            false}});
 }
 
@@ -1264,16 +1284,59 @@
           {kTopFrameOrigin, url::Origin::Create(GURL("https://top-frame.com"))},
           {kTopicsURL, GURL("https://embedded.com")},
           {MultipleInputKeys{kFledgeAuctionPartyOrigin,
-                             kAdMeasurementReportingOrigin},
+                             kAdMeasurementReportingOrigin, kAccessingOrigin},
            url::Origin::Create(GURL("https://embedded.com"))},
           {kAdMeasurementSourceOrigin,
            url::Origin::Create(GURL("https://source-origin.com"))},
           {kAdMeasurementDestinationOrigin,
            url::Origin::Create(GURL("https://dest-origin.com"))}},
       TestOutput{
-          {MultipleOutputKeys{kIsTopicsAllowed, kIsTopicsAllowedForContext,
-                              kIsFledgeAllowed, kIsAttributionReportingAllowed,
-                              kMaySendAttributionReport},
+          {MultipleOutputKeys{
+               kIsTopicsAllowed, kIsTopicsAllowedForContext, kIsFledgeAllowed,
+               kIsAttributionReportingAllowed, kMaySendAttributionReport,
+               kIsSharedStorageAllowed, kIsSharedStorageSelectURLAllowed,
+               kIsPrivateAggregationAllowed},
            false}});
 }
+
+TEST_F(PrivacySandboxSettingsM1Test,
+       CheckFledgeDependentApi_FledgeOn_OtherApiOn) {
+  RunTestCase(TestState{{kM1FledgeEnabledUserPrefValue, true}},
+              TestInput{{kTopFrameOrigin,
+                         url::Origin::Create(GURL("https://top-frame.com"))},
+                        {kAccessingOrigin,
+                         url::Origin::Create(GURL("https://embedded.com"))}},
+              TestOutput{{kIsSharedStorageSelectURLAllowed, true}});
+}
+
+TEST_F(PrivacySandboxSettingsM1Test,
+       CheckFledgeDependentApi_FledgeOff_OtherApiOff) {
+  RunTestCase(TestState{{kM1FledgeEnabledUserPrefValue, false}},
+              TestInput{{kTopFrameOrigin,
+                         url::Origin::Create(GURL("https://top-frame.com"))},
+                        {kAccessingOrigin,
+                         url::Origin::Create(GURL("https://embedded.com"))}},
+              TestOutput{{kIsSharedStorageSelectURLAllowed, false}});
+}
+
+TEST_F(PrivacySandboxSettingsM1Test,
+       CheckAdMeasurementDependentApi_AdMeasurementOn_OtherApiOn) {
+  RunTestCase(TestState{{kM1AdMeasurementEnabledUserPrefValue, true}},
+              TestInput{{kTopFrameOrigin,
+                         url::Origin::Create(GURL("https://top-frame.com"))},
+                        {kAdMeasurementReportingOrigin,
+                         url::Origin::Create(GURL("https://embedded.com"))}},
+              TestOutput{{kIsPrivateAggregationAllowed, true}});
+}
+
+TEST_F(PrivacySandboxSettingsM1Test,
+       CheckAdMeasurementDependentApi_AdMeasurementOff_OtherApiOff) {
+  RunTestCase(TestState{{kM1AdMeasurementEnabledUserPrefValue, false}},
+              TestInput{{kTopFrameOrigin,
+                         url::Origin::Create(GURL("https://top-frame.com"))},
+                        {kAdMeasurementReportingOrigin,
+                         url::Origin::Create(GURL("https://embedded.com"))}},
+              TestOutput{{kIsPrivateAggregationAllowed, false}});
+}
+
 }  // namespace privacy_sandbox
diff --git a/components/privacy_sandbox/privacy_sandbox_test_util.cc b/components/privacy_sandbox/privacy_sandbox_test_util.cc
index 43297f39..32491a7 100644
--- a/components/privacy_sandbox/privacy_sandbox_test_util.cc
+++ b/components/privacy_sandbox/privacy_sandbox_test_util.cc
@@ -197,6 +197,44 @@
                     source_origin, destination_origin, reporting_origin));
       return;
     }
+
+    case (OutputKey::kIsSharedStorageAllowed): {
+      SCOPED_TRACE("Check Output: kIsSharedStorageAllowed()");
+      auto top_frame_origin =
+          GetItemValueForKey<url::Origin>(InputKey::kTopFrameOrigin, input);
+      auto accessing_origin =
+          GetItemValueForKey<url::Origin>(InputKey::kAccessingOrigin, input);
+      auto return_value = GetItemValue<bool>(output_value);
+      ASSERT_EQ(return_value, privacy_sandbox_settings->IsSharedStorageAllowed(
+                                  top_frame_origin, accessing_origin));
+      return;
+    }
+
+    case (OutputKey::kIsSharedStorageSelectURLAllowed): {
+      SCOPED_TRACE("Check Output: IsSharedStorageSelectURLAllowed()");
+      auto top_frame_origin =
+          GetItemValueForKey<url::Origin>(InputKey::kTopFrameOrigin, input);
+      auto accessing_origin =
+          GetItemValueForKey<url::Origin>(InputKey::kAccessingOrigin, input);
+      auto return_value = GetItemValue<bool>(output_value);
+      ASSERT_EQ(return_value,
+                privacy_sandbox_settings->IsSharedStorageSelectURLAllowed(
+                    top_frame_origin, accessing_origin));
+      return;
+    }
+
+    case (OutputKey::kIsPrivateAggregationAllowed): {
+      SCOPED_TRACE("Check Output: IsPrivateAggregationAllowed()");
+      auto top_frame_origin =
+          GetItemValueForKey<url::Origin>(InputKey::kTopFrameOrigin, input);
+      auto reporting_origin = GetItemValueForKey<url::Origin>(
+          InputKey::kAdMeasurementReportingOrigin, input);
+      auto return_value = GetItemValue<bool>(output_value);
+      ASSERT_EQ(return_value,
+                privacy_sandbox_settings->IsPrivateAggregationAllowed(
+                    top_frame_origin, reporting_origin));
+      return;
+    }
   }
 }
 
diff --git a/components/privacy_sandbox/privacy_sandbox_test_util.h b/components/privacy_sandbox/privacy_sandbox_test_util.h
index 832c28f..aeb6423a 100644
--- a/components/privacy_sandbox/privacy_sandbox_test_util.h
+++ b/components/privacy_sandbox/privacy_sandbox_test_util.h
@@ -77,6 +77,7 @@
   kAdMeasurementReportingOrigin = 4,
   kAdMeasurementSourceOrigin = 5,
   kAdMeasurementDestinationOrigin = 6,
+  kAccessingOrigin = 7,
 };
 
 // Defines the expected output of the functions under test, when the profile is
@@ -87,6 +88,9 @@
   kIsFledgeAllowed = 3,
   kIsAttributionReportingAllowed = 4,
   kMaySendAttributionReport = 5,
+  kIsSharedStorageAllowed = 6,
+  kIsSharedStorageSelectURLAllowed = 7,
+  kIsPrivateAggregationAllowed = 8,
 };
 
 // To allow multiple input keys to map to the same value, without having to
diff --git a/components/remote_cocoa/app_shim/BUILD.gn b/components/remote_cocoa/app_shim/BUILD.gn
index 43713a7..1adfddd 100644
--- a/components/remote_cocoa/app_shim/BUILD.gn
+++ b/components/remote_cocoa/app_shim/BUILD.gn
@@ -84,4 +84,7 @@
     "QuartzCore.framework",
     "SecurityInterface.framework",
   ]
+  weak_frameworks = [
+    "UniformTypeIdentifiers.framework",  # macOS 11
+  ]
 }
diff --git a/components/remote_cocoa/app_shim/select_file_dialog_bridge.mm b/components/remote_cocoa/app_shim/select_file_dialog_bridge.mm
index c939ff0..8accf4e 100644
--- a/components/remote_cocoa/app_shim/select_file_dialog_bridge.mm
+++ b/components/remote_cocoa/app_shim/select_file_dialog_bridge.mm
@@ -5,8 +5,9 @@
 #include "components/remote_cocoa/app_shim/select_file_dialog_bridge.h"
 
 #include <AppKit/AppKit.h>
-#include <CoreServices/CoreServices.h>
+#include <CoreServices/CoreServices.h>  // pre-macOS 11
 #include <Foundation/Foundation.h>
+#import <UniformTypeIdentifiers/UniformTypeIdentifiers.h>  // macOS 11
 #include <stddef.h>
 
 #include "base/files/file_util.h"
@@ -25,6 +26,7 @@
 
 const int kFileTypePopupTag = 1234;
 
+// TODO(macOS 11): Remove this.
 CFStringRef CreateUTIFromExtension(const base::FilePath::StringType& ext) {
   base::ScopedCFTypeRef<CFStringRef> ext_cf(base::SysUTF8ToCFStringRef(ext));
   return UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension,
@@ -32,12 +34,23 @@
 }
 
 NSString* GetDescriptionFromExtension(const base::FilePath::StringType& ext) {
-  base::ScopedCFTypeRef<CFStringRef> uti(CreateUTIFromExtension(ext));
-  base::ScopedCFTypeRef<CFStringRef> description(
-      UTTypeCopyDescription(uti.get()));
+  if (@available(macOS 11, *)) {
+    UTType* type =
+        [UTType typeWithFilenameExtension:base::SysUTF8ToNSString(ext)];
+    NSString* description = type.localizedDescription;
 
-  if (description && CFStringGetLength(description))
-    return [[base::mac::CFToNSCast(description.get()) retain] autorelease];
+    if (description.length) {
+      return description;
+    }
+  } else {
+    base::ScopedCFTypeRef<CFStringRef> uti(CreateUTIFromExtension(ext));
+    base::ScopedCFTypeRef<CFStringRef> description(
+        UTTypeCopyDescription(uti.get()));
+
+    if (description && CFStringGetLength(description)) {
+      return [[base::mac::CFToNSCast(description.get()) retain] autorelease];
+    }
+  }
 
   // In case no description is found, create a description based on the
   // unknown extension type (i.e. if the extension is .qqq, the we create
@@ -167,13 +180,25 @@
   // since the dialog_ will stay alive longer than this object.
   NSSavePanel* _dialog;
 
-  // An array whose each item corresponds to an array of different extensions in
-  // an extension group.
-  base::scoped_nsobject<NSArray> _fileTypeLists;
+  // Two ivars serving the same purpose. While `_fileTypeLists` is for pre-macOS
+  // 11, and contains NSStrings with UTType identifiers, `_fileUTTypeLists` is
+  // for macOS 11 and later, and contains UTTypes. TODO(macOS 11): Clean this
+  // up.
+
+  // An array where each item is an array of different extensions in an
+  // extension group.
+  base::scoped_nsobject<NSArray<NSArray<NSString*>*>> _fileTypeLists;
+  base::scoped_nsobject<NSArray<NSArray<UTType*>*>> _fileUTTypeLists
+      API_AVAILABLE(macos(11.0));
 }
 
+// TODO(macOS 11): Remove this.
 - (instancetype)initWithDialog:(NSSavePanel*)dialog
-                 fileTypeLists:(NSArray*)fileTypeLists;
+                 fileTypeLists:(NSArray<NSArray<NSString*>*>*)fileTypeLists;
+
+- (instancetype)initWithDialog:(NSSavePanel*)dialog
+               fileUTTypeLists:(NSArray<NSArray<UTType*>*>*)fileUTTypeLists
+    API_AVAILABLE(macos(11.0));
 
 - (void)popupAction:(id)sender;
 @end
@@ -184,8 +209,8 @@
   // Refuse to accept users closing the dialog with a key repeat, since the key
   // may have been first pressed while the user was looking at insecure content.
   // See https://crbug.com/637098.
-  if ([[NSApp currentEvent] type] == NSEventTypeKeyDown &&
-      [[NSApp currentEvent] isARepeat]) {
+  if (NSApp.currentEvent.type == NSEventTypeKeyDown &&
+      NSApp.currentEvent.ARepeat) {
     return NO;
   }
 
@@ -196,8 +221,9 @@
 
 @implementation ExtensionDropdownHandler
 
+// TODO(macOS 11): Remove this.
 - (instancetype)initWithDialog:(NSSavePanel*)dialog
-                 fileTypeLists:(NSArray*)fileTypeLists {
+                 fileTypeLists:(NSArray<NSArray<NSString*>*>*)fileTypeLists {
   if ((self = [super init])) {
     _dialog = dialog;
     _fileTypeLists.reset([fileTypeLists retain]);
@@ -205,15 +231,38 @@
   return self;
 }
 
+- (instancetype)initWithDialog:(NSSavePanel*)dialog
+               fileUTTypeLists:(NSArray<NSArray<UTType*>*>*)fileUTTypeLists
+    API_AVAILABLE(macos(11.0)) {
+  if ((self = [super init])) {
+    _dialog = dialog;
+    _fileUTTypeLists.reset([fileUTTypeLists retain]);
+  }
+  return self;
+}
+
 - (void)popupAction:(id)sender {
   NSUInteger index = [sender indexOfSelectedItem];
-  if (index < [_fileTypeLists count]) {
-    // For save dialogs, this causes the first item in the allowedFileTypes
-    // array to be used as the extension for the save panel.
-    [_dialog setAllowedFileTypes:[_fileTypeLists objectAtIndex:index]];
+  if (@available(macOS 11, *)) {
+    if (index < [_fileUTTypeLists count]) {
+      // For save dialogs, this causes the first item in the allowedContentTypes
+      // array to be used as the extension for the save panel.
+      _dialog.allowedContentTypes = [_fileUTTypeLists objectAtIndex:index];
+    } else {
+      // The user selected "All files" option. (Note that an empty array is "all
+      // types" and nil is an error.)
+      _dialog.allowedContentTypes = @[];
+    }
   } else {
-    // The user selected "All files" option.
-    [_dialog setAllowedFileTypes:nil];
+    if (index < [_fileTypeLists count]) {
+      // For save dialogs, this causes the first item in the allowedFileTypes
+      // array to be used as the extension for the save panel.
+      _dialog.allowedFileTypes = [_fileTypeLists objectAtIndex:index];
+    } else {
+      // The user selected "All files" option. (Note that nil is "all types" and
+      // an empty array is an error.)
+      _dialog.allowedFileTypes = nil;
+    }
   }
 }
 
@@ -301,7 +350,7 @@
   if (type_ == SelectFileDialogType::kSaveAsFile) {
     // When file extensions are hidden and removing the extension from
     // the default filename gives one which still has an extension
-    // that OS X recognizes, it will get confused and think the user
+    // that macOS recognizes, it will get confused and think the user
     // is trying to override the default extension. This happens with
     // filenames like "foo.tar.gz" or "ball.of.tar.png". Work around
     // this by never hiding extensions in that case.
@@ -374,8 +423,11 @@
   DCHECK(popup);
 
   // Create an array with each item corresponding to an array of different
-  // extensions in an extension group.
-  NSMutableArray* file_type_lists = [NSMutableArray array];
+  // extensions in an extension group. TODO(macOS 11): Remove the first,
+  // uncomment the second.
+  NSMutableArray<NSArray<NSString*>*>* file_type_lists = [NSMutableArray array];
+  NSMutableArray /*<NSArray<UTType*>*>*/* file_uttype_lists =
+      [NSMutableArray array];
   int default_extension_index = -1;
   for (size_t i = 0; i < file_types->extensions.size(); ++i) {
     const std::vector<base::FilePath::StringType>& ext_list =
@@ -396,34 +448,51 @@
     DCHECK_NE(0u, [type_description length]);
     [popup addItemWithTitle:type_description];
 
-    // Populate file_type_lists.
-    // Set to store different extensions in the current extension group.
-    NSMutableArray* file_type_array = [NSMutableArray array];
+    // Store different extensions in the current extension group. TODO(macOS
+    // 11): Remove the first, uncomment the second.
+    NSMutableArray<NSString*>* file_type_array = [NSMutableArray array];
+    NSMutableArray /*<UTType*>*/* file_uttype_array = [NSMutableArray array];
     for (const base::FilePath::StringType& ext : ext_list) {
-      if (ext == default_extension)
+      if (ext == default_extension) {
         default_extension_index = i;
-
-      // Crash reports suggest that CreateUTIFromExtension may return nil. Hence
-      // we nil check before adding to |file_type_set|. See
-      // https://crbug.com/630101 and rdar://27490414.
-      base::ScopedCFTypeRef<CFStringRef> uti(CreateUTIFromExtension(ext));
-      if (uti) {
-        NSString* uti_ns = base::mac::CFToNSCast(uti.get());
-        if (![file_type_array containsObject:uti_ns])
-          [file_type_array addObject:uti_ns];
       }
 
-      // Always allow the extension itself, in case the UTI doesn't map
-      // back to the original extension correctly. This occurs with dynamic
-      // UTIs on 10.7 and 10.8.
-      // See https://crbug.com/148840, https://openradar.appspot.com/12316273
-      base::ScopedCFTypeRef<CFStringRef> ext_cf(
-          base::SysUTF8ToCFStringRef(ext));
-      NSString* ext_ns = base::mac::CFToNSCast(ext_cf.get());
-      if (![file_type_array containsObject:ext_ns])
-        [file_type_array addObject:ext_ns];
+      if (@available(macOS 11, *)) {
+        UTType* type =
+            [UTType typeWithFilenameExtension:base::SysUTF8ToNSString(ext)];
+        if (![file_uttype_array containsObject:type]) {
+          [file_uttype_array addObject:type];
+        }
+      } else {
+        // Crash reports suggest that CreateUTIFromExtension may return nil.
+        // Hence we nil check before adding to |file_type_set|. See
+        // crbug.com/630101 and rdar://27490414.
+        base::ScopedCFTypeRef<CFStringRef> uti(CreateUTIFromExtension(ext));
+        if (uti) {
+          NSString* uti_ns = base::mac::CFToNSCast(uti.get());
+          if (![file_type_array containsObject:uti_ns]) {
+            [file_type_array addObject:uti_ns];
+          }
+        }
+
+        // Always allow the extension itself, in case the UTI doesn't map
+        // back to the original extension correctly. This occurs with dynamic
+        // UTIs on 10.7 and 10.8.
+        // See https://crbug.com/148840, https://openradar.appspot.com/12316273
+        base::ScopedCFTypeRef<CFStringRef> ext_cf(
+            base::SysUTF8ToCFStringRef(ext));
+        NSString* ext_ns = base::mac::CFToNSCast(ext_cf.get());
+        if (![file_type_array containsObject:ext_ns]) {
+          [file_type_array addObject:ext_ns];
+        }
+      }
     }
-    [file_type_lists addObject:file_type_array];
+
+    if (@available(macOS 11, *)) {
+      [file_uttype_lists addObject:file_uttype_array];
+    } else {
+      [file_type_lists addObject:file_type_array];
+    }
   }
 
   if (file_types->include_all_files || file_types->extensions.empty()) {
@@ -436,35 +505,39 @@
     }
   }
 
-  extension_dropdown_handler_.reset([[ExtensionDropdownHandler alloc]
-      initWithDialog:dialog
-       fileTypeLists:file_type_lists]);
+  if (@available(macOS 11, *)) {
+    extension_dropdown_handler_.reset([[ExtensionDropdownHandler alloc]
+         initWithDialog:dialog
+        fileUTTypeLists:file_uttype_lists]);
+  } else {
+    extension_dropdown_handler_.reset([[ExtensionDropdownHandler alloc]
+        initWithDialog:dialog
+         fileTypeLists:file_type_lists]);
+  }
 
   // This establishes a weak reference to handler. Hence we persist it as part
-  // of dialog_data_list_.
-  [popup setTarget:extension_dropdown_handler_];
-  [popup setAction:@selector(popupAction:)];
+  // of `dialog_data_list_`.
+  popup.target = extension_dropdown_handler_;
+  popup.action = @selector(popupAction:);
 
-  // file_type_index uses 1 based indexing.
+  // Note that `file_type_index` uses 1-based indexing.
   if (file_type_index) {
     DCHECK_LE(static_cast<size_t>(file_type_index),
               file_types->extensions.size());
     DCHECK_GE(file_type_index, 1);
     [popup selectItemAtIndex:file_type_index - 1];
-    [extension_dropdown_handler_ popupAction:popup];
   } else if (!default_extension.empty() && default_extension_index != -1) {
     [popup selectItemAtIndex:default_extension_index];
-    [dialog
-        setAllowedFileTypes:@[ base::SysUTF8ToNSString(default_extension) ]];
   } else {
     // Select the first item.
     [popup selectItemAtIndex:0];
-    [extension_dropdown_handler_ popupAction:popup];
   }
+  [extension_dropdown_handler_ popupAction:popup];
 
   // There's no need for a popup unless there are at least two choices.
-  if (popup.numberOfItems >= 2)
+  if (popup.numberOfItems >= 2) {
     dialog.accessoryView = accessory_view.get();
+  }
 }
 
 void SelectFileDialogBridge::OnPanelEnded(bool did_cancel) {
diff --git a/components/safe_browsing/content/browser/web_ui/safe_browsing_ui.cc b/components/safe_browsing/content/browser/web_ui/safe_browsing_ui.cc
index 1765e69..c02939eb 100644
--- a/components/safe_browsing/content/browser/web_ui/safe_browsing_ui.cc
+++ b/components/safe_browsing/content/browser/web_ui/safe_browsing_ui.cc
@@ -2323,13 +2323,12 @@
 
 SafeBrowsingUI::SafeBrowsingUI(content::WebUI* web_ui)
     : content::WebUIController(web_ui) {
-  // Set up the chrome://safe-browsing source.
-
-  content::WebUIDataSource* html_source = content::WebUIDataSource::Create(
-      safe_browsing::kChromeUISafeBrowsingHost);
-
   content::BrowserContext* browser_context =
       web_ui->GetWebContents()->GetBrowserContext();
+  // Set up the chrome://safe-browsing source.
+  content::WebUIDataSource* html_source =
+      content::WebUIDataSource::CreateAndAdd(
+          browser_context, safe_browsing::kChromeUISafeBrowsingHost);
 
   // Register callback handler.
   // Handles messages from JavaScript to C++ via chrome.send().
@@ -2345,8 +2344,6 @@
   html_source->OverrideContentSecurityPolicy(
       network::mojom::CSPDirectiveName::TrustedTypes,
       "trusted-types static-types;");
-
-  content::WebUIDataSource::Add(browser_context, html_source);
 }
 
 SafeBrowsingUI::~SafeBrowsingUI() {}
diff --git a/components/security_interstitials/content/connection_help_ui.cc b/components/security_interstitials/content/connection_help_ui.cc
index f3dc8a4..f375239 100644
--- a/components/security_interstitials/content/connection_help_ui.cc
+++ b/components/security_interstitials/content/connection_help_ui.cc
@@ -20,7 +20,9 @@
 ConnectionHelpUI::ConnectionHelpUI(content::WebUI* web_ui)
     : content::WebUIController(web_ui) {
   content::WebUIDataSource* html_source =
-      content::WebUIDataSource::Create(kChromeUIConnectionHelpHost);
+      content::WebUIDataSource::CreateAndAdd(
+          web_ui->GetWebContents()->GetBrowserContext(),
+          kChromeUIConnectionHelpHost);
 
   // JS code needs these constants to decide which section to expand.
   html_source->AddInteger("certCommonNameInvalid",
@@ -87,10 +89,6 @@
                                IDR_SECURITY_INTERSTITIAL_CONNECTION_HELP_JS);
   html_source->SetDefaultResource(
       IDR_SECURITY_INTERSTITIAL_CONNECTION_HELP_HTML);
-
-  content::BrowserContext* browser_context =
-      web_ui->GetWebContents()->GetBrowserContext();
-  content::WebUIDataSource::Add(browser_context, html_source);
 }
 
 ConnectionHelpUI::~ConnectionHelpUI() {}
diff --git a/components/security_interstitials/content/known_interception_disclosure_ui.cc b/components/security_interstitials/content/known_interception_disclosure_ui.cc
index fa353dbf..e4e71a3e 100644
--- a/components/security_interstitials/content/known_interception_disclosure_ui.cc
+++ b/components/security_interstitials/content/known_interception_disclosure_ui.cc
@@ -20,8 +20,10 @@
 KnownInterceptionDisclosureUI::KnownInterceptionDisclosureUI(
     content::WebUI* web_ui)
     : content::WebUIController(web_ui) {
-  content::WebUIDataSource* html_source = content::WebUIDataSource::Create(
-      kChromeUIConnectionMonitoringDetectedHost);
+  content::WebUIDataSource* html_source =
+      content::WebUIDataSource::CreateAndAdd(
+          web_ui->GetWebContents()->GetBrowserContext(),
+          kChromeUIConnectionMonitoringDetectedHost);
 
   html_source->AddLocalizedString("title", IDS_KNOWN_INTERCEPTION_TITLE);
   html_source->AddLocalizedString("pageHeader", IDS_KNOWN_INTERCEPTION_HEADER);
@@ -39,10 +41,6 @@
   html_source->AddResourcePath("images/2x/triangle_red.png",
                                IDR_KNOWN_INTERCEPTION_ICON_2X_PNG);
   html_source->SetDefaultResource(IDR_KNOWN_INTERCEPTION_HTML);
-
-  content::BrowserContext* browser_context =
-      web_ui->GetWebContents()->GetBrowserContext();
-  content::WebUIDataSource::Add(browser_context, html_source);
 }
 
 KnownInterceptionDisclosureUI::~KnownInterceptionDisclosureUI() = default;
diff --git a/components/test/data/history/history.61.sql b/components/test/data/history/history.61.sql
new file mode 100644
index 0000000..819ffa8
--- /dev/null
+++ b/components/test/data/history/history.61.sql
@@ -0,0 +1,38 @@
+PRAGMA foreign_keys=OFF;
+BEGIN TRANSACTION;
+CREATE TABLE meta(key LONGVARCHAR NOT NULL UNIQUE PRIMARY KEY, value LONGVARCHAR);
+INSERT INTO meta VALUES('mmap_status','-1');
+INSERT INTO meta VALUES('version','61');
+INSERT INTO meta VALUES('last_compatible_version','16');
+CREATE TABLE urls(id INTEGER PRIMARY KEY AUTOINCREMENT,url LONGVARCHAR,title LONGVARCHAR,visit_count INTEGER DEFAULT 0 NOT NULL,typed_count INTEGER DEFAULT 0 NOT NULL,last_visit_time INTEGER NOT NULL,hidden INTEGER DEFAULT 0 NOT NULL);
+CREATE TABLE visits(id INTEGER PRIMARY KEY AUTOINCREMENT,url INTEGER NOT NULL,visit_time INTEGER NOT NULL,from_visit INTEGER,transition INTEGER DEFAULT 0 NOT NULL,segment_id INTEGER,visit_duration INTEGER DEFAULT 0 NOT NULL,incremented_omnibox_typed_score BOOLEAN DEFAULT FALSE NOT NULL,opener_visit INTEGER,originator_cache_guid TEXT,originator_visit_id INTEGER,originator_from_visit INTEGER,originator_opener_visit INTEGER,is_known_to_sync BOOLEAN DEFAULT FALSE NOT NULL);
+CREATE TABLE visit_source(id INTEGER PRIMARY KEY,source INTEGER NOT NULL);
+CREATE TABLE keyword_search_terms (keyword_id INTEGER NOT NULL,url_id INTEGER NOT NULL,term LONGVARCHAR NOT NULL,normalized_term LONGVARCHAR NOT NULL);
+CREATE TABLE downloads (id INTEGER PRIMARY KEY,guid VARCHAR NOT NULL,current_path LONGVARCHAR NOT NULL,target_path LONGVARCHAR NOT NULL,start_time INTEGER NOT NULL,received_bytes INTEGER NOT NULL,total_bytes INTEGER NOT NULL,state INTEGER NOT NULL,danger_type INTEGER NOT NULL,interrupt_reason INTEGER NOT NULL,hash BLOB NOT NULL,end_time INTEGER NOT NULL,opened INTEGER NOT NULL,last_access_time INTEGER NOT NULL,transient INTEGER NOT NULL,referrer VARCHAR NOT NULL,site_url VARCHAR NOT NULL,embedder_download_data VARCHAR NOT NULL,tab_url VARCHAR NOT NULL,tab_referrer_url VARCHAR NOT NULL,http_method VARCHAR NOT NULL,by_ext_id VARCHAR NOT NULL,by_ext_name VARCHAR NOT NULL,etag VARCHAR NOT NULL,last_modified VARCHAR NOT NULL,mime_type VARCHAR(255) NOT NULL,original_mime_type VARCHAR(255) NOT NULL);
+CREATE TABLE downloads_url_chains (id INTEGER NOT NULL,chain_index INTEGER NOT NULL,url LONGVARCHAR NOT NULL, PRIMARY KEY (id, chain_index) );
+CREATE TABLE downloads_slices (download_id INTEGER NOT NULL,offset INTEGER NOT NULL,received_bytes INTEGER NOT NULL,finished INTEGER NOT NULL DEFAULT 0,PRIMARY KEY (download_id, offset) );
+CREATE TABLE segments (id INTEGER PRIMARY KEY,name VARCHAR,url_id INTEGER NON NULL);
+CREATE TABLE segment_usage (id INTEGER PRIMARY KEY,segment_id INTEGER NOT NULL,time_slot INTEGER NOT NULL,visit_count INTEGER DEFAULT 0 NOT NULL);
+CREATE TABLE typed_url_sync_metadata (storage_key INTEGER PRIMARY KEY NOT NULL,value BLOB);
+CREATE TABLE content_annotations(visit_id INTEGER PRIMARY KEY,visibility_score NUMERIC,floc_protected_score NUMERIC,categories VARCHAR,page_topics_model_version INTEGER,annotation_flags INTEGER NOT NULL,entities VARCHAR,related_searches VARCHAR,search_normalized_url VARCHAR,search_terms LONGVARCHAR,alternative_title VARCHAR,page_language VARCHAR,password_state INTEGER DEFAULT 0 NOT NULL);
+CREATE TABLE context_annotations(visit_id INTEGER PRIMARY KEY,context_annotation_flags INTEGER NOT NULL,duration_since_last_visit INTEGER,page_end_reason INTEGER,total_foreground_duration INTEGER,browser_type INTEGER DEFAULT 0 NOT NULL,window_id INTEGER DEFAULT -1 NOT NULL,tab_id INTEGER DEFAULT -1 NOT NULL,task_id INTEGER DEFAULT -1 NOT NULL,root_task_id INTEGER DEFAULT -1 NOT NULL,parent_task_id INTEGER DEFAULT -1 NOT NULL,response_code INTEGER DEFAULT 0 NOT NULL);
+CREATE TABLE clusters(cluster_id INTEGER PRIMARY KEY AUTOINCREMENT,should_show_on_prominent_ui_surfaces BOOLEAN NOT NULL,label VARCHAR NOT NULL,raw_label VARCHAR NOT NULL,triggerability_calculated BOOLEAN NOT NULL,originator_cache_guid TEXT,originator_cluster_id INTEGER);
+CREATE TABLE clusters_and_visits(cluster_id INTEGER NOT NULL,visit_id INTEGER NOT NULL,score NUMERIC NOT NULL,engagement_score NUMERIC NOT NULL,url_for_deduping LONGVARCHAR NOT NULL,normalized_url LONGVARCHAR NOT NULL,url_for_display LONGVARCHAR NOT NULL,PRIMARY KEY(cluster_id,visit_id))WITHOUT ROWID;
+CREATE TABLE cluster_keywords(cluster_id INTEGER NOT NULL,keyword VARCHAR NOT NULL,type INTEGER NOT NULL,score NUMERIC NOT NULL,collections VARCHAR NOT NULL);
+CREATE TABLE cluster_visit_duplicates(visit_id INTEGER NOT NULL,duplicate_visit_id INTEGER NOT NULL,PRIMARY KEY(visit_id,duplicate_visit_id))WITHOUT ROWID;
+DELETE FROM sqlite_sequence;
+CREATE INDEX visits_url_index ON visits (url);
+CREATE INDEX visits_from_index ON visits (from_visit);
+CREATE INDEX visits_time_index ON visits (visit_time);
+CREATE INDEX visits_originator_id_index ON visits (originator_visit_id);
+CREATE INDEX keyword_search_terms_index1 ON keyword_search_terms (keyword_id, normalized_term);
+CREATE INDEX keyword_search_terms_index2 ON keyword_search_terms (url_id);
+CREATE INDEX keyword_search_terms_index3 ON keyword_search_terms (term);
+CREATE INDEX segments_name ON segments(name);
+CREATE INDEX segments_url_id ON segments(url_id);
+CREATE INDEX segment_usage_time_slot_segment_id ON segment_usage(time_slot, segment_id);
+CREATE INDEX segments_usage_seg_id ON segment_usage(segment_id);
+CREATE INDEX clusters_for_visit ON clusters_and_visits(visit_id);
+CREATE INDEX cluster_keywords_cluster_id_index ON cluster_keywords(cluster_id);
+CREATE INDEX urls_url_index ON urls (url);
+COMMIT;
diff --git a/components/viz/common/display/renderer_settings.h b/components/viz/common/display/renderer_settings.h
index 97996a8..a398a5d 100644
--- a/components/viz/common/display/renderer_settings.h
+++ b/components/viz/common/display/renderer_settings.h
@@ -61,6 +61,11 @@
   // then overlays aren't supported.
   std::vector<OverlayStrategy> overlay_strategies;
 #endif
+#if BUILDFLAG(IS_MAC)
+  // CGDirectDisplayID for the screen on which the browser is currently
+  // displayed.
+  int64_t display_id;
+#endif
 };
 
 // This is a set of debug flags that can be changed at runtime, so that we can
diff --git a/components/viz/common/frame_sinks/begin_frame_source.h b/components/viz/common/frame_sinks/begin_frame_source.h
index 36b495f..6578fb6a 100644
--- a/components/viz/common/frame_sinks/begin_frame_source.h
+++ b/components/viz/common/frame_sinks/begin_frame_source.h
@@ -412,6 +412,10 @@
   // observers.
   virtual void SetPreferredInterval(base::TimeDelta interval) {}
 
+#if BUILDFLAG(IS_MAC)
+  virtual void SetVSyncDisplayID(int64_t display_id) {}
+#endif
+
  protected:
   // Called on AddObserver and gets missed BeginFrameArgs for the given
   // observer. The missed BeginFrame is sent only if the returned
diff --git a/components/viz/service/frame_sinks/gpu_vsync_begin_frame_source.cc b/components/viz/service/frame_sinks/gpu_vsync_begin_frame_source.cc
index b6fafd5..72a1bb6f 100644
--- a/components/viz/service/frame_sinks/gpu_vsync_begin_frame_source.cc
+++ b/components/viz/service/frame_sinks/gpu_vsync_begin_frame_source.cc
@@ -92,4 +92,10 @@
   output_surface_->SetGpuVSyncEnabled(needs_begin_frames);
 }
 
+#if BUILDFLAG(IS_MAC)
+void GpuVSyncBeginFrameSource::SetVSyncDisplayID(int64_t display_id) {
+  // TODO (crbug/1404797): Use this Display id for VCDisplayLink
+}
+#endif
+
 }  // namespace viz
diff --git a/components/viz/service/frame_sinks/gpu_vsync_begin_frame_source.h b/components/viz/service/frame_sinks/gpu_vsync_begin_frame_source.h
index 389cba2..a9aca55 100644
--- a/components/viz/service/frame_sinks/gpu_vsync_begin_frame_source.h
+++ b/components/viz/service/frame_sinks/gpu_vsync_begin_frame_source.h
@@ -33,6 +33,9 @@
   // ExternalBeginFrameSource overrides.
   BeginFrameArgs GetMissedBeginFrameArgs(BeginFrameObserver* obs) override;
   void SetPreferredInterval(base::TimeDelta interval) override;
+#if BUILDFLAG(IS_MAC)
+  void SetVSyncDisplayID(int64_t display_id) override;
+#endif
 
   // BeginFrameSource:
   void SetDynamicBeginFrameDeadlineOffsetSource(
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 4fabf662..f9dd844c 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
@@ -266,6 +266,14 @@
   display_->SetDisplayColorSpaces(display_color_spaces);
 }
 
+#if BUILDFLAG(IS_MAC)
+void RootCompositorFrameSinkImpl::SetVSyncDisplayID(int64_t display_id) {
+  if (external_begin_frame_source_) {
+    external_begin_frame_source_->SetVSyncDisplayID(display_id);
+  }
+}
+#endif
+
 void RootCompositorFrameSinkImpl::SetOutputIsSecure(bool secure) {
   display_->SetOutputIsSecure(secure);
 }
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 de5f5b6..1fb3d56 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
@@ -76,6 +76,9 @@
   void SetDisplayColorMatrix(const gfx::Transform& color_matrix) override;
   void SetDisplayColorSpaces(
       const gfx::DisplayColorSpaces& display_color_spaces) override;
+#if BUILDFLAG(IS_MAC)
+  void SetVSyncDisplayID(int64_t display_id) override;
+#endif
   void SetOutputIsSecure(bool secure) override;
   void SetDisplayVSyncParameters(base::TimeTicks timebase,
                                  base::TimeDelta interval) override;
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index 4e72bfb8..0074c42 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -1892,8 +1892,8 @@
     "scheduler/responsiveness/watcher.h",
     "scoped_active_url.cc",
     "scoped_active_url.h",
-    "screen_enumeration/screen_change_monitor.cc",
-    "screen_enumeration/screen_change_monitor.h",
+    "screen_details/screen_change_monitor.cc",
+    "screen_details/screen_change_monitor.h",
     "screen_orientation/screen_orientation_provider.cc",
     "screen_orientation/screen_orientation_provider.h",
     "screenlock_monitor/screenlock_monitor.cc",
diff --git a/content/browser/compositor/viz_process_transport_factory.cc b/content/browser/compositor/viz_process_transport_factory.cc
index 3b44531..3ee0cc95 100644
--- a/content/browser/compositor/viz_process_transport_factory.cc
+++ b/content/browser/compositor/viz_process_transport_factory.cc
@@ -416,7 +416,9 @@
 #endif
   root_params->gpu_compositing = gpu_compositing;
   root_params->renderer_settings = viz::CreateRendererSettings();
-
+#if BUILDFLAG(IS_MAC)
+  root_params->renderer_settings.display_id = compositor->display_id();
+#endif
   base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
   if (command_line->HasSwitch(switches::kDisableFrameRateLimit))
     root_params->disable_frame_rate_limit = true;
diff --git a/content/browser/screen_enumeration/DIR_METADATA b/content/browser/screen_details/DIR_METADATA
similarity index 100%
rename from content/browser/screen_enumeration/DIR_METADATA
rename to content/browser/screen_details/DIR_METADATA
diff --git a/content/browser/screen_enumeration/OWNERS b/content/browser/screen_details/OWNERS
similarity index 100%
rename from content/browser/screen_enumeration/OWNERS
rename to content/browser/screen_details/OWNERS
diff --git a/content/browser/screen_enumeration/screen_change_monitor.cc b/content/browser/screen_details/screen_change_monitor.cc
similarity index 94%
rename from content/browser/screen_enumeration/screen_change_monitor.cc
rename to content/browser/screen_details/screen_change_monitor.cc
index d7b196c4..aa3285f69 100644
--- a/content/browser/screen_enumeration/screen_change_monitor.cc
+++ b/content/browser/screen_details/screen_change_monitor.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "content/browser/screen_enumeration/screen_change_monitor.h"
+#include "content/browser/screen_details/screen_change_monitor.h"
 
 #include "ui/display/screen.h"
 
diff --git a/content/browser/screen_enumeration/screen_change_monitor.h b/content/browser/screen_details/screen_change_monitor.h
similarity index 87%
rename from content/browser/screen_enumeration/screen_change_monitor.h
rename to content/browser/screen_details/screen_change_monitor.h
index a82f942c..f4d465e 100644
--- a/content/browser/screen_enumeration/screen_change_monitor.h
+++ b/content/browser/screen_details/screen_change_monitor.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CONTENT_BROWSER_SCREEN_ENUMERATION_SCREEN_CHANGE_MONITOR_H_
-#define CONTENT_BROWSER_SCREEN_ENUMERATION_SCREEN_CHANGE_MONITOR_H_
+#ifndef CONTENT_BROWSER_SCREEN_DETAILS_SCREEN_CHANGE_MONITOR_H_
+#define CONTENT_BROWSER_SCREEN_DETAILS_SCREEN_CHANGE_MONITOR_H_
 
 #include <vector>
 
@@ -44,4 +44,4 @@
 
 }  // namespace content
 
-#endif  // CONTENT_BROWSER_SCREEN_ENUMERATION_SCREEN_CHANGE_MONITOR_H_
+#endif  // CONTENT_BROWSER_SCREEN_DETAILS_SCREEN_CHANGE_MONITOR_H_
diff --git a/content/browser/screen_enumeration/screen_details_browsertest.cc b/content/browser/screen_details/screen_details_browsertest.cc
similarity index 99%
rename from content/browser/screen_enumeration/screen_details_browsertest.cc
rename to content/browser/screen_details/screen_details_browsertest.cc
index 5c4f742..c6d431fc 100644
--- a/content/browser/screen_enumeration/screen_details_browsertest.cc
+++ b/content/browser/screen_details/screen_details_browsertest.cc
@@ -4,7 +4,7 @@
 
 #include "base/memory/raw_ptr.h"
 #include "build/build_config.h"
-#include "content/browser/screen_enumeration/screen_details_test_utils.h"
+#include "content/browser/screen_details/screen_details_test_utils.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/content_browser_test.h"
diff --git a/content/browser/screen_enumeration/screen_details_test_utils.cc b/content/browser/screen_details/screen_details_test_utils.cc
similarity index 96%
rename from content/browser/screen_enumeration/screen_details_test_utils.cc
rename to content/browser/screen_details/screen_details_test_utils.cc
index e8a2490..c24464e85 100644
--- a/content/browser/screen_enumeration/screen_details_test_utils.cc
+++ b/content/browser/screen_details/screen_details_test_utils.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "content/browser/screen_enumeration/screen_details_test_utils.h"
+#include "content/browser/screen_details/screen_details_test_utils.h"
 
 #include "base/ranges/algorithm.h"
 #include "ui/display/display.h"
diff --git a/content/browser/screen_enumeration/screen_details_test_utils.h b/content/browser/screen_details/screen_details_test_utils.h
similarity index 85%
rename from content/browser/screen_enumeration/screen_details_test_utils.h
rename to content/browser/screen_details/screen_details_test_utils.h
index 7978284..93adec3 100644
--- a/content/browser/screen_enumeration/screen_details_test_utils.h
+++ b/content/browser/screen_details/screen_details_test_utils.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CONTENT_BROWSER_SCREEN_ENUMERATION_SCREEN_DETAILS_TEST_UTILS_H_
-#define CONTENT_BROWSER_SCREEN_ENUMERATION_SCREEN_DETAILS_TEST_UTILS_H_
+#ifndef CONTENT_BROWSER_SCREEN_DETAILS_SCREEN_DETAILS_TEST_UTILS_H_
+#define CONTENT_BROWSER_SCREEN_DETAILS_SCREEN_DETAILS_TEST_UTILS_H_
 
 #include "base/values.h"
 
@@ -41,4 +41,4 @@
 
 }  // namespace content::test
 
-#endif  // CONTENT_BROWSER_SCREEN_ENUMERATION_SCREEN_DETAILS_TEST_UTILS_H_
+#endif  // CONTENT_BROWSER_SCREEN_DETAILS_SCREEN_DETAILS_TEST_UTILS_H_
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index c679be4..a7606a7 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -97,7 +97,7 @@
 #include "content/browser/renderer_host/render_widget_host_view_child_frame.h"
 #include "content/browser/renderer_host/text_input_manager.h"
 #include "content/browser/renderer_host/visible_time_request_trigger.h"
-#include "content/browser/screen_enumeration/screen_change_monitor.h"
+#include "content/browser/screen_details/screen_change_monitor.h"
 #include "content/browser/screen_orientation/screen_orientation_provider.h"
 #include "content/browser/shared_storage/shared_storage_budget_charger.h"
 #include "content/browser/site_instance_impl.h"
@@ -8245,11 +8245,8 @@
 void WebContentsImpl::NotifyMainFrameSwappedFromRenderManager(
     RenderFrameHostImpl* old_frame,
     RenderFrameHostImpl* new_frame) {
-  // Only fire RenderViewHostChanged if it is
-  // related to our FrameTree, as observers cannot deal with events coming
-  // from non-primary FrameTree.
-  // TODO(https://crbug.com/1168562): Update observers to deal with the events,
-  // and fire events for all frame trees.
+  // Only notify about RenderViewHost changes if it is related to our FrameTree,
+  // as observers cannot deal with events coming from non-primary FrameTree.
   if (!new_frame->IsInPrimaryMainFrame()) {
     return;
   }
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index 803805b50..8979b34e 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -95,8 +95,8 @@
     "../browser/renderer_host/input/mock_input_router.h",
     "../browser/renderer_host/mock_render_widget_host.cc",
     "../browser/renderer_host/mock_render_widget_host.h",
-    "../browser/screen_enumeration/screen_details_test_utils.cc",
-    "../browser/screen_enumeration/screen_details_test_utils.h",
+    "../browser/screen_details/screen_details_test_utils.cc",
+    "../browser/screen_details/screen_details_test_utils.h",
     "../browser/service_worker/embedded_worker_test_helper.cc",
     "../browser/service_worker/embedded_worker_test_helper.h",
     "../browser/service_worker/fake_embedded_worker_instance_client.cc",
@@ -1430,7 +1430,7 @@
     "../browser/renderer_host/sec_fetch_browsertest.cc",
     "../browser/renderer_host/unassigned_site_instance_browsertest.cc",
     "../browser/resource_loading_browsertest.cc",
-    "../browser/screen_enumeration/screen_details_browsertest.cc",
+    "../browser/screen_details/screen_details_browsertest.cc",
     "../browser/screen_orientation/screen_orientation_browsertest.cc",
     "../browser/security_exploit_browsertest.cc",
     "../browser/service_process_host_browsertest.cc",
diff --git a/docs/chrome_untrusted.md b/docs/chrome_untrusted.md
index 71680e7e..97e3fcf 100644
--- a/docs/chrome_untrusted.md
+++ b/docs/chrome_untrusted.md
@@ -114,12 +114,9 @@
 
     // Create a URLDataSource and add resources.
     auto* untrusted_source =
-      content::WebUIDataSource::Create(kUntrustedExampleURL);
+      content::WebUIDataSource::CreateAndAdd(
+          web_ui->GetWebContents()->GetBrowserContext(), kUntrustedExampleURL);
     untrusted_source->AddResourcePath(...);
-
-    // Register the URLDataSource
-    auto* browser_context = web_ui->GetWebContents()->GetBrowserContext();
-    content::WebUIDataSource::Add(browser_context, untrusted_source);
   }
 
   UntrustedExampleUI(const UntrustedExampleUI&) = delete;
diff --git a/docs/webui_explainer.md b/docs/webui_explainer.md
index a783f93..586b671 100644
--- a/docs/webui_explainer.md
+++ b/docs/webui_explainer.md
@@ -129,10 +129,11 @@
  public:
   DonutsUI(content::WebUI* web_ui) : content::WebUIController(web_ui) {
     content::WebUIDataSource* source =
-        content::WebUIDataSource::Create("donuts");  // "donuts" == hostname
+        content::WebUIDataSource::CreateAndAdd(
+            web_ui->GetWebContents()->GetBrowserContext(),
+            "donuts");  // "donuts" == hostname
     source->AddString("mmmDonuts", "Mmm, donuts!");  // Translations.
     source->AddResourcePath("", IDR_DONUTS_HTML);  // Home page.
-    content::WebUIDataSource::Add(source);
 
     // Handles messages from JavaScript to C++ via chrome.send().
     web_ui->AddMessageHandler(std::make_unique<OvenHandler>());
@@ -220,7 +221,8 @@
 page):
 
 ```c++
-content::WebUIDataSource* source = content::WebUIDataSource::Create("history");
+content::WebUIDataSource* source = content::WebUIDataSource::CreateAndAdd(
+    Profile::FromWebUI(web_ui), "history");
 
 source->AddResourcePath("sign_in_promo.svg", IDR_HISTORY_SIGN_IN_PROMO_SVG);
 source->AddResourcePath("synced_tabs.html", IDR_HISTORY_SYNCED_TABS_HTML);
@@ -234,8 +236,6 @@
 webui::SetupWebUIDataSource(
     source, base::make_span(kHistoryResources, kHistoryResourcesSize),
     kGeneratedPath, IDR_HISTORY_HISTORY_HTML);
-
-content::WebUIDataSource::Add(source);
 ```
 
 For more about each of the methods called on `WebUIDataSource` and the utility
@@ -279,18 +279,13 @@
 
 ## Data Sources
 
-### WebUIDataSource::Create()
+### WebUIDataSource::CreateAndAdd()
 
-This is a factory method required to create a WebUIDataSource instance. The
-argument to `Create()` is typically the host name of the page. Caller owns the
-result.
+This is a factory method required to create and add a WebUIDataSource. The first
+argument to `Create()` is the browser context. The second argument is typically
+the host name of the page. The caller does not own the result.
 
-### WebUIDataSource::Add()
-
-Once you've created and added some things to a data source, it'll need to be
-"added". This means transferring ownership. In practice, the data source is
-created in the browser process on the UI thread and transferred to the IO
-thread. Additionally, calling `Add()` will overwrite any existing data source
+Additionally, calling `CreateAndAdd()` will overwrite any existing data source
 with the same name.
 
 <div class="note">
diff --git a/docs/webui_in_components.md b/docs/webui_in_components.md
index 973c93f..57e3b53f60 100644
--- a/docs/webui_in_components.md
+++ b/docs/webui_in_components.md
@@ -167,7 +167,9 @@
     : content::WebUIController(web_ui) {
   // Set up the chrome://hello-world source.
   content::WebUIDataSource* html_source =
-      content::WebUIDataSource::Create(chrome::kChromeUIHelloWorldHost);
+      content::WebUIDataSource::CreateAndAdd(
+          web_ui->GetWebContents()->GetBrowserContext(),
+          chrome::kChromeUIHelloWorldHost);
 
   // Localized strings.
   static constexpr webui::LocalizedString kStrings[] = {
@@ -188,10 +190,6 @@
   };
   source->AddResourcePaths(kResources);
   html_source->SetDefaultResource(IDR_HELLO_WORLD_HTML);
-
-  content::BrowserContext* browser_context =
-      web_ui->GetWebContents()->GetBrowserContext();
-  content::WebUIDataSource::Add(browser_context, html_source);
 }
 
 HelloWorldUI::~HelloWorldUI() {
diff --git a/fuchsia_web/shell/web_engine_shell.cml b/fuchsia_web/shell/web_engine_shell.cml
index 26903688..7f20d5c4 100644
--- a/fuchsia_web/shell/web_engine_shell.cml
+++ b/fuchsia_web/shell/web_engine_shell.cml
@@ -72,6 +72,9 @@
     },
   ],
   facets: {
-    "fuchsia.test.deprecated-allowed-packages": [ "web_engine_with_webui" ]
+    "fuchsia.test.deprecated-allowed-packages": [
+      "web_engine",
+      "web_engine_with_webui",
+    ],
   },
 }
diff --git a/infra/config/generated/builders/ci/Linux ChromiumOS MSan Focal/properties.json b/infra/config/generated/builders/ci/Linux ChromiumOS MSan Focal/properties.json
deleted file mode 100644
index 9e90d261..0000000
--- a/infra/config/generated/builders/ci/Linux ChromiumOS MSan Focal/properties.json
+++ /dev/null
@@ -1,61 +0,0 @@
-{
-  "$build/chromium_tests_builder_config": {
-    "builder_config": {
-      "builder_db": {
-        "entries": [
-          {
-            "builder_id": {
-              "bucket": "ci",
-              "builder": "Linux ChromiumOS MSan Focal",
-              "project": "chromium"
-            },
-            "builder_spec": {
-              "builder_group": "chromium.fyi",
-              "execution_mode": "COMPILE_AND_TEST",
-              "legacy_chromium_config": {
-                "apply_configs": [
-                  "mb"
-                ],
-                "build_config": "Release",
-                "config": "chromium_msan"
-              },
-              "legacy_gclient_config": {
-                "apply_configs": [
-                  "chromeos"
-                ],
-                "config": "chromium"
-              }
-            }
-          }
-        ]
-      },
-      "builder_ids": [
-        {
-          "bucket": "ci",
-          "builder": "Linux ChromiumOS MSan Focal",
-          "project": "chromium"
-        }
-      ],
-      "mirroring_builder_group_and_names": [
-        {
-          "builder": "linux_chromium_chromeos_msan_focal",
-          "group": "tryserver.chromium.linux"
-        }
-      ]
-    }
-  },
-  "$build/reclient": {
-    "instance": "rbe-chromium-trusted",
-    "jobs": 500,
-    "metrics_project": "chromium-reclient-metrics"
-  },
-  "$recipe_engine/resultdb/test_presentation": {
-    "column_keys": [],
-    "grouping_keys": [
-      "status",
-      "v.test_suite"
-    ]
-  },
-  "builder_group": "chromium.fyi",
-  "recipe": "chromium"
-}
\ No newline at end of file
diff --git a/infra/config/generated/builders/try/linux_chromium_chromeos_msan_focal/properties.json b/infra/config/generated/builders/try/linux_chromium_chromeos_msan_focal/properties.json
deleted file mode 100644
index 5245d8b..0000000
--- a/infra/config/generated/builders/try/linux_chromium_chromeos_msan_focal/properties.json
+++ /dev/null
@@ -1,55 +0,0 @@
-{
-  "$build/chromium_tests_builder_config": {
-    "builder_config": {
-      "builder_db": {
-        "entries": [
-          {
-            "builder_id": {
-              "bucket": "ci",
-              "builder": "Linux ChromiumOS MSan Focal",
-              "project": "chromium"
-            },
-            "builder_spec": {
-              "builder_group": "chromium.fyi",
-              "execution_mode": "COMPILE_AND_TEST",
-              "legacy_chromium_config": {
-                "apply_configs": [
-                  "mb"
-                ],
-                "build_config": "Release",
-                "config": "chromium_msan"
-              },
-              "legacy_gclient_config": {
-                "apply_configs": [
-                  "chromeos"
-                ],
-                "config": "chromium"
-              }
-            }
-          }
-        ]
-      },
-      "builder_ids": [
-        {
-          "bucket": "ci",
-          "builder": "Linux ChromiumOS MSan Focal",
-          "project": "chromium"
-        }
-      ]
-    }
-  },
-  "$build/reclient": {
-    "instance": "rbe-chromium-untrusted",
-    "jobs": 300,
-    "metrics_project": "chromium-reclient-metrics"
-  },
-  "$recipe_engine/resultdb/test_presentation": {
-    "column_keys": [],
-    "grouping_keys": [
-      "status",
-      "v.test_suite"
-    ]
-  },
-  "builder_group": "tryserver.chromium.linux",
-  "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 4c325ae4..1629a41 100644
--- a/infra/config/generated/luci/commit-queue.cfg
+++ b/infra/config/generated/luci/commit-queue.cfg
@@ -2923,10 +2923,6 @@
         includable_only: true
       }
       builders {
-        name: "chromium/try/linux_chromium_chromeos_msan_focal"
-        includable_only: true
-      }
-      builders {
         name: "chromium/try/linux_chromium_chromeos_msan_rel_ng"
         includable_only: true
       }
diff --git a/infra/config/generated/luci/cr-buildbucket.cfg b/infra/config/generated/luci/cr-buildbucket.cfg
index 90f89aa..ae951d4a 100644
--- a/infra/config/generated/luci/cr-buildbucket.cfg
+++ b/infra/config/generated/luci/cr-buildbucket.cfg
@@ -13019,7 +13019,7 @@
       dimensions: "cores:16"
       dimensions: "cpu:x86-64"
       dimensions: "free_space:standard"
-      dimensions: "os:Ubuntu-18.04"
+      dimensions: "os:Ubuntu-20.04"
       dimensions: "pool:luci.chromium.ci"
       dimensions: "ssd:1"
       exe {
@@ -13104,102 +13104,13 @@
       }
     }
     builders {
-      name: "Linux ChromiumOS MSan Focal"
-      swarming_host: "chromium-swarm.appspot.com"
-      dimensions: "builderless:1"
-      dimensions: "cores:8"
-      dimensions: "cpu:x86-64"
-      dimensions: "free_space:standard"
-      dimensions: "os:Ubuntu-20.04"
-      dimensions: "pool:luci.chromium.ci"
-      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/ci/Linux ChromiumOS MSan Focal/properties.json",'
-        '    "top_level_project": {'
-        '      "ref": "refs/heads/main",'
-        '      "repo": {'
-        '        "host": "chromium.googlesource.com",'
-        '        "project": "chromium/src"'
-        '      }'
-        '    }'
-        '  },'
-        '  "builder_group": "chromium.fyi",'
-        '  "led_builder_is_bootstrapped": true,'
-        '  "recipe": "chromium"'
-        '}'
-      priority: 35
-      execution_timeout_secs: 57600
-      build_numbers: YES
-      service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
-      experiments {
-        key: "chromium_swarming.expose_merge_script_failures"
-        value: 100
-      }
-      experiments {
-        key: "luci.buildbucket.omit_python2"
-        value: 100
-      }
-      experiments {
-        key: "luci.recipes.use_python3"
-        value: 100
-      }
-      resultdb {
-        enable: true
-        bq_exports {
-          project: "chrome-luci-data"
-          dataset: "chromium"
-          table: "ci_test_results"
-          test_results {}
-        }
-        bq_exports {
-          project: "chrome-luci-data"
-          dataset: "chromium"
-          table: "gpu_ci_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_ci_test_results"
-          test_results {
-            predicate {
-              test_id_regexp: "(ninja://[^/]*blink_web_tests/.+)|(ninja://[^/]*blink_wpt_tests/.+)"
-            }
-          }
-        }
-        history_options {
-          use_invocation_timestamp: true
-        }
-      }
-    }
-    builders {
       name: "Linux ChromiumOS MSan Tests"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "free_space:standard"
-      dimensions: "os:Ubuntu-18.04"
+      dimensions: "os:Ubuntu-20.04"
       dimensions: "pool:luci.chromium.ci"
       dimensions: "ssd:0"
       exe {
@@ -53395,178 +53306,6 @@
       }
     }
     builders {
-      name: "Comparison Linux (reclient vs reclient remote links)(large)"
-      swarming_host: "chromium-swarm.appspot.com"
-      dimensions: "builderless:1"
-      dimensions: "cores:8"
-      dimensions: "cpu:x86-64"
-      dimensions: "free_space:standard"
-      dimensions: "os:Ubuntu-18.04"
-      dimensions: "pool:luci.chromium.ci"
-      dimensions: "ssd:0"
-      exe {
-        cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
-        cipd_version: "refs/heads/main"
-        cmd: "luciexe"
-      }
-      properties:
-        '{'
-        '  "$build/reclient": {'
-        '    "bootstrap_env": {'
-        '      "RBE_clang_depscan_archive": "true",'
-        '      "RBE_deps_cache_dir": "",'
-        '      "RBE_ip_reset_min_delay": "-1s"'
-        '    },'
-        '    "cache_silo": "Comparison Linux remote links - cache siloed",'
-        '    "instance": "rbe-chromium-trusted-test",'
-        '    "jobs": 250,'
-        '    "metrics_project": "chromium-reclient-metrics"'
-        '  },'
-        '  "$recipe_engine/resultdb/test_presentation": {'
-        '    "column_keys": [],'
-        '    "grouping_keys": ['
-        '      "status",'
-        '      "v.test_suite"'
-        '    ]'
-        '  },'
-        '  "builder_group": "chromium.reclient.fyi",'
-        '  "recipe": "reclient_reclient_comparison"'
-        '}'
-      execution_timeout_secs: 21600
-      build_numbers: YES
-      service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
-      experiments {
-        key: "chromium_swarming.expose_merge_script_failures"
-        value: 100
-      }
-      experiments {
-        key: "luci.buildbucket.omit_python2"
-        value: 100
-      }
-      experiments {
-        key: "luci.recipes.use_python3"
-        value: 100
-      }
-      resultdb {
-        enable: true
-        bq_exports {
-          project: "chrome-luci-data"
-          dataset: "chromium"
-          table: "ci_test_results"
-          test_results {}
-        }
-        bq_exports {
-          project: "chrome-luci-data"
-          dataset: "chromium"
-          table: "gpu_ci_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_ci_test_results"
-          test_results {
-            predicate {
-              test_id_regexp: "(ninja://[^/]*blink_web_tests/.+)|(ninja://[^/]*blink_wpt_tests/.+)"
-            }
-          }
-        }
-        history_options {
-          use_invocation_timestamp: true
-        }
-      }
-    }
-    builders {
-      name: "Comparison Linux (reclient vs reclient remote links)(medium)"
-      swarming_host: "chromium-swarm.appspot.com"
-      dimensions: "builderless:1"
-      dimensions: "cores:8"
-      dimensions: "cpu:x86-64"
-      dimensions: "free_space:standard"
-      dimensions: "os:Ubuntu-18.04"
-      dimensions: "pool:luci.chromium.ci"
-      dimensions: "ssd:0"
-      exe {
-        cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
-        cipd_version: "refs/heads/main"
-        cmd: "luciexe"
-      }
-      properties:
-        '{'
-        '  "$build/reclient": {'
-        '    "bootstrap_env": {'
-        '      "RBE_clang_depscan_archive": "true",'
-        '      "RBE_deps_cache_dir": "",'
-        '      "RBE_ip_reset_min_delay": "-1s"'
-        '    },'
-        '    "cache_silo": "Comparison Linux remote links - cache siloed",'
-        '    "instance": "rbe-chromium-trusted-test",'
-        '    "jobs": 250,'
-        '    "metrics_project": "chromium-reclient-metrics"'
-        '  },'
-        '  "$recipe_engine/resultdb/test_presentation": {'
-        '    "column_keys": [],'
-        '    "grouping_keys": ['
-        '      "status",'
-        '      "v.test_suite"'
-        '    ]'
-        '  },'
-        '  "builder_group": "chromium.reclient.fyi",'
-        '  "recipe": "reclient_reclient_comparison"'
-        '}'
-      execution_timeout_secs: 21600
-      build_numbers: YES
-      service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
-      experiments {
-        key: "chromium_swarming.expose_merge_script_failures"
-        value: 100
-      }
-      experiments {
-        key: "luci.buildbucket.omit_python2"
-        value: 100
-      }
-      experiments {
-        key: "luci.recipes.use_python3"
-        value: 100
-      }
-      resultdb {
-        enable: true
-        bq_exports {
-          project: "chrome-luci-data"
-          dataset: "chromium"
-          table: "ci_test_results"
-          test_results {}
-        }
-        bq_exports {
-          project: "chrome-luci-data"
-          dataset: "chromium"
-          table: "gpu_ci_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_ci_test_results"
-          test_results {
-            predicate {
-              test_id_regexp: "(ninja://[^/]*blink_web_tests/.+)|(ninja://[^/]*blink_wpt_tests/.+)"
-            }
-          }
-        }
-        history_options {
-          use_invocation_timestamp: true
-        }
-      }
-    }
-    builders {
       name: "Comparison Linux (reclient vs reclient remote links)(small)"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
@@ -53587,7 +53326,8 @@
         '    "bootstrap_env": {'
         '      "RBE_clang_depscan_archive": "true",'
         '      "RBE_deps_cache_dir": "",'
-        '      "RBE_ip_reset_min_delay": "-1s"'
+        '      "RBE_ip_reset_min_delay": "-1s",'
+        '      "RBE_use_unified_uploads": "false"'
         '    },'
         '    "cache_silo": "Comparison Linux remote links - cache siloed",'
         '    "instance": "rbe-chromium-trusted-test",'
@@ -84509,122 +84249,12 @@
       }
     }
     builders {
-      name: "linux_chromium_chromeos_msan_focal"
-      swarming_host: "chromium-swarm.appspot.com"
-      dimensions: "builderless:1"
-      dimensions: "cores:8"
-      dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-20.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/linux_chromium_chromeos_msan_focal/properties.json",'
-        '    "top_level_project": {'
-        '      "ref": "refs/heads/main",'
-        '      "repo": {'
-        '        "host": "chromium.googlesource.com",'
-        '        "project": "chromium/src"'
-        '      }'
-        '    }'
-        '  },'
-        '  "builder_group": "tryserver.chromium.linux",'
-        '  "led_builder_is_bootstrapped": true,'
-        '  "recipe": "chromium_trybot"'
-        '}'
-      execution_timeout_secs: 57600
-      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: "chromium_swarming.expose_merge_script_failures"
-        value: 100
-      }
-      experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
-        key: "luci.buildbucket.omit_python2"
-        value: 100
-      }
-      experiments {
-        key: "luci.recipes.use_python3"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        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/.+)|(ninja://[^/]*blink_wpt_tests/.+)"
-            }
-          }
-        }
-        history_options {
-          use_invocation_timestamp: true
-        }
-      }
-    }
-    builders {
       name: "linux_chromium_chromeos_msan_rel_ng"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
       dimensions: "cores:16"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-18.04"
+      dimensions: "os:Ubuntu-20.04"
       dimensions: "pool:luci.chromium.try"
       dimensions: "ssd:1"
       exe {
diff --git a/infra/config/generated/luci/luci-milo.cfg b/infra/config/generated/luci/luci-milo.cfg
index c0360310..4389e57 100644
--- a/infra/config/generated/luci/luci-milo.cfg
+++ b/infra/config/generated/luci/luci-milo.cfg
@@ -9161,11 +9161,6 @@
     category: "lacros x64"
   }
   builders {
-    name: "buildbucket/luci.chromium.ci/Linux ChromiumOS MSan Focal"
-    category: "msan"
-    short_name: "crs"
-  }
-  builders {
     name: "buildbucket/luci.chromium.ci/linux-upload-perfetto"
     category: "perfetto"
     short_name: "lnx"
@@ -13416,16 +13411,6 @@
     short_name: "cmp"
   }
   builders {
-    name: "buildbucket/luci.chromium.reclient/Comparison Linux (reclient vs reclient remote links)(large)"
-    category: "linux"
-    short_name: "cmp"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.reclient/Comparison Linux (reclient vs reclient remote links)(medium)"
-    category: "linux"
-    short_name: "cmp"
-  }
-  builders {
     name: "buildbucket/luci.chromium.reclient/Comparison Linux (reclient vs reclient remote links)(small)"
     category: "linux"
     short_name: "cmp"
@@ -17797,9 +17782,6 @@
     name: "buildbucket/luci.chromium.try/linux_chromium_chromeos_asan_rel_ng"
   }
   builders {
-    name: "buildbucket/luci.chromium.try/linux_chromium_chromeos_msan_focal"
-  }
-  builders {
     name: "buildbucket/luci.chromium.try/linux_chromium_chromeos_msan_rel_ng"
   }
   builders {
@@ -18863,9 +18845,6 @@
     name: "buildbucket/luci.chromium.try/linux_chromium_chromeos_asan_rel_ng"
   }
   builders {
-    name: "buildbucket/luci.chromium.try/linux_chromium_chromeos_msan_focal"
-  }
-  builders {
     name: "buildbucket/luci.chromium.try/linux_chromium_chromeos_msan_rel_ng"
   }
   builders {
diff --git a/infra/config/generated/luci/luci-scheduler.cfg b/infra/config/generated/luci/luci-scheduler.cfg
index 62e1a0c..1b22b16d 100644
--- a/infra/config/generated/luci/luci-scheduler.cfg
+++ b/infra/config/generated/luci/luci-scheduler.cfg
@@ -618,24 +618,6 @@
   }
 }
 job {
-  id: "Comparison Linux (reclient vs reclient remote links)(large)"
-  realm: "reclient"
-  buildbucket {
-    server: "cr-buildbucket.appspot.com"
-    bucket: "reclient"
-    builder: "Comparison Linux (reclient vs reclient remote links)(large)"
-  }
-}
-job {
-  id: "Comparison Linux (reclient vs reclient remote links)(medium)"
-  realm: "reclient"
-  buildbucket {
-    server: "cr-buildbucket.appspot.com"
-    bucket: "reclient"
-    builder: "Comparison Linux (reclient vs reclient remote links)(medium)"
-  }
-}
-job {
   id: "Comparison Linux (reclient vs reclient remote links)(small)"
   realm: "reclient"
   buildbucket {
@@ -1685,15 +1667,6 @@
   }
 }
 job {
-  id: "Linux ChromiumOS MSan Focal"
-  realm: "ci"
-  buildbucket {
-    server: "cr-buildbucket.appspot.com"
-    bucket: "ci"
-    builder: "Linux ChromiumOS MSan Focal"
-  }
-}
-job {
   id: "Linux ChromiumOS MSan Tests"
   realm: "ci"
   buildbucket {
@@ -6084,7 +6057,6 @@
   triggers: "Linux Chromium OS ASan LSan Builder"
   triggers: "Linux ChromiumOS Full"
   triggers: "Linux ChromiumOS MSan Builder"
-  triggers: "Linux ChromiumOS MSan Focal"
   triggers: "Linux FYI GPU TSAN Release"
   triggers: "Linux MSan Builder"
   triggers: "Linux TSan Builder"
@@ -6397,8 +6369,6 @@
   triggers: "mac-archive-rel-goma-rbe-canary"
   triggers: "mac-archive-rel-goma-rbe-latest"
   triggers: "Comparison Linux (reclient vs reclient remote links)"
-  triggers: "Comparison Linux (reclient vs reclient remote links)(large)"
-  triggers: "Comparison Linux (reclient vs reclient remote links)(medium)"
   triggers: "Comparison Linux (reclient vs reclient remote links)(small)"
   triggers: "Linux Builder reclient staging"
   triggers: "Linux Builder reclient staging untrusted"
diff --git a/infra/config/subprojects/chromium/ci/chromium.fyi.star b/infra/config/subprojects/chromium/ci/chromium.fyi.star
index 309acaab..0039eb36 100644
--- a/infra/config/subprojects/chromium/ci/chromium.fyi.star
+++ b/infra/config/subprojects/chromium/ci/chromium.fyi.star
@@ -2620,33 +2620,6 @@
     schedule = "0 2,6,10,14,18,22 * * *",
 )
 
-ci.builder(
-    # An FYI version of the following builders that runs on Focal:
-    # https://ci.chromium.org/p/chromium/builders/ci/Linux%20ChromiumOS%20MSan%20Builder
-    # https://ci.chromium.org/p/chromium/builders/ci/Linux%20ChromiumOS%20MSan%20Tests
-    # TODO(crbug.com/1260217): Remove this builder when the main MSAN builder
-    # has migrated to focal.
-    name = "Linux ChromiumOS MSan Focal",
-    builder_spec = builder_config.builder_spec(
-        gclient_config = builder_config.gclient_config(
-            config = "chromium",
-            apply_configs = ["chromeos"],
-        ),
-        chromium_config = builder_config.chromium_config(
-            config = "chromium_msan",
-            apply_configs = ["mb"],
-            build_config = builder_config.build_config.RELEASE,
-        ),
-    ),
-    os = os.LINUX_FOCAL,
-    console_view_entry = consoles.console_view_entry(
-        category = "msan",
-        short_name = "crs",
-    ),
-    execution_timeout = 16 * time.hour,
-    reclient_jobs = reclient.jobs.HIGH_JOBS_FOR_CI,
-)
-
 fyi_mac_builder(
     name = "Mac Builder Next",
     builder_spec = builder_config.builder_spec(
diff --git a/infra/config/subprojects/chromium/ci/chromium.memory.star b/infra/config/subprojects/chromium/ci/chromium.memory.star
index fd49965..610f648b 100644
--- a/infra/config/subprojects/chromium/ci/chromium.memory.star
+++ b/infra/config/subprojects/chromium/ci/chromium.memory.star
@@ -264,6 +264,7 @@
         short_name = "bld",
     ),
     execution_timeout = 4 * time.hour,
+    os = os.LINUX_FOCAL,
 )
 
 linux_memory_builder(
@@ -293,6 +294,7 @@
     ),
     execution_timeout = 4 * time.hour,
     reclient_instance = None,
+    os = os.LINUX_FOCAL,
 )
 
 linux_memory_builder(
diff --git a/infra/config/subprojects/chromium/try/tryserver.chromium.linux.star b/infra/config/subprojects/chromium/try/tryserver.chromium.linux.star
index cf3fcbdb..662c49d 100644
--- a/infra/config/subprojects/chromium/try/tryserver.chromium.linux.star
+++ b/infra/config/subprojects/chromium/try/tryserver.chromium.linux.star
@@ -458,17 +458,6 @@
 )
 
 try_.builder(
-    name = "linux_chromium_chromeos_msan_focal",
-    mirrors = [
-        "ci/Linux ChromiumOS MSan Focal",
-    ],
-    os = os.LINUX_FOCAL,
-    execution_timeout = 16 * time.hour,
-    goma_backend = None,
-    reclient_jobs = reclient.jobs.HIGH_JOBS_FOR_CQ,
-)
-
-try_.builder(
     name = "linux_chromium_chromeos_msan_rel_ng",
     mirrors = [
         "ci/Linux ChromiumOS MSan Builder",
@@ -478,6 +467,7 @@
     ssd = True,
     goma_backend = None,
     reclient_jobs = reclient.jobs.HIGH_JOBS_FOR_CQ,
+    os = os.LINUX_FOCAL,
 )
 
 try_.builder(
diff --git a/infra/config/subprojects/reclient/reclient.star b/infra/config/subprojects/reclient/reclient.star
index 546abd8..caa2259 100644
--- a/infra/config/subprojects/reclient/reclient.star
+++ b/infra/config/subprojects/reclient/reclient.star
@@ -431,44 +431,7 @@
         "RBE_ip_reset_min_delay": "-1s",
         "RBE_deps_cache_dir": "",
         "RBE_clang_depscan_archive": "true",
-    },
-    reclient_cache_silo = "Comparison Linux remote links - cache siloed",
-    reclient_instance = reclient.instance.TEST_TRUSTED,
-    reclient_jobs = reclient.jobs.DEFAULT,
-)
-
-ci.builder(
-    name = "Comparison Linux (reclient vs reclient remote links)(medium)",
-    executable = "recipe:reclient_reclient_comparison",
-    os = os.LINUX_DEFAULT,
-    console_view_entry = consoles.console_view_entry(
-        category = "linux",
-        short_name = "cmp",
-    ),
-    execution_timeout = 6 * time.hour,
-    reclient_bootstrap_env = {
-        "RBE_ip_reset_min_delay": "-1s",
-        "RBE_deps_cache_dir": "",
-        "RBE_clang_depscan_archive": "true",
-    },
-    reclient_cache_silo = "Comparison Linux remote links - cache siloed",
-    reclient_instance = reclient.instance.TEST_TRUSTED,
-    reclient_jobs = reclient.jobs.DEFAULT,
-)
-
-ci.builder(
-    name = "Comparison Linux (reclient vs reclient remote links)(large)",
-    executable = "recipe:reclient_reclient_comparison",
-    os = os.LINUX_DEFAULT,
-    console_view_entry = consoles.console_view_entry(
-        category = "linux",
-        short_name = "cmp",
-    ),
-    execution_timeout = 6 * time.hour,
-    reclient_bootstrap_env = {
-        "RBE_ip_reset_min_delay": "-1s",
-        "RBE_deps_cache_dir": "",
-        "RBE_clang_depscan_archive": "true",
+        "RBE_use_unified_uploads": "false",
     },
     reclient_cache_silo = "Comparison Linux remote links - cache siloed",
     reclient_instance = reclient.instance.TEST_TRUSTED,
diff --git a/infra/inclusive_language_presubmit_exempt_dirs.txt b/infra/inclusive_language_presubmit_exempt_dirs.txt
index 1018460d..f3338a6 100644
--- a/infra/inclusive_language_presubmit_exempt_dirs.txt
+++ b/infra/inclusive_language_presubmit_exempt_dirs.txt
@@ -337,7 +337,6 @@
 third_party/blink/renderer/modules/mediarecorder 9 1
 third_party/blink/renderer/modules/peerconnection 3 3
 third_party/blink/renderer/modules/scheduler 1 1
-third_party/blink/renderer/modules/screen_enumeration 3 2
 third_party/blink/renderer/modules/virtualkeyboard 6 3
 third_party/blink/renderer/modules/webcodecs 3 2
 third_party/blink/renderer/modules/window_controls_overlay 3 3
diff --git a/ios/chrome/app/application_delegate/BUILD.gn b/ios/chrome/app/application_delegate/BUILD.gn
index 33d11bf2..2881e191 100644
--- a/ios/chrome/app/application_delegate/BUILD.gn
+++ b/ios/chrome/app/application_delegate/BUILD.gn
@@ -63,7 +63,6 @@
     "//ios/chrome/browser/main:public",
     "//ios/chrome/browser/main:test_support",
     "//ios/chrome/browser/metrics",
-    "//ios/chrome/browser/ntp_snippets",
     "//ios/chrome/browser/tabs",
     "//ios/chrome/browser/ui:feature_flags",
     "//ios/chrome/browser/ui/commands",
diff --git a/ios/chrome/app/application_delegate/app_state_unittest.mm b/ios/chrome/app/application_delegate/app_state_unittest.mm
index 2c2773a..88f41560 100644
--- a/ios/chrome/app/application_delegate/app_state_unittest.mm
+++ b/ios/chrome/app/application_delegate/app_state_unittest.mm
@@ -31,7 +31,6 @@
 #import "ios/chrome/browser/flags/system_flags.h"
 #import "ios/chrome/browser/main/browser.h"
 #import "ios/chrome/browser/main/test_browser.h"
-#import "ios/chrome/browser/ntp_snippets/ios_chrome_content_suggestions_service_factory.h"
 #import "ios/chrome/browser/ui/commands/application_commands.h"
 #import "ios/chrome/browser/ui/commands/browser_commands.h"
 #import "ios/chrome/browser/ui/commands/command_dispatcher.h"
@@ -251,9 +250,6 @@
   void SetUp() override {
     BlockCleanupTest::SetUp();
     TestChromeBrowserState::Builder test_cbs_builder;
-    test_cbs_builder.AddTestingFactory(
-        IOSChromeContentSuggestionsServiceFactory::GetInstance(),
-        IOSChromeContentSuggestionsServiceFactory::GetDefaultFactory());
     browser_state_ = test_cbs_builder.Build();
   }
 
diff --git a/ios/chrome/app/strings/ios_strings.grd b/ios/chrome/app/strings/ios_strings.grd
index fd0acc7ab..9d2cdcc 100644
--- a/ios/chrome/app/strings/ios_strings.grd
+++ b/ios/chrome/app/strings/ios_strings.grd
@@ -2903,6 +2903,9 @@
       <message name="IDS_IOS_SHOW_PASSWORD_VIEW_SITE" desc="Label indicating that the text displayed below is the site on which the associated password was saved. The password is displayed in the same view, but under a different label. [Length: 20em] [iOS only]">
         Site
       </message>
+      <message name="IDS_IOS_SHOW_PASSWORD_VIEW_SITES" desc="Label indicating that the texts displayed next to it are the sites on which the associated password was saved. The password is displayed in the same view, but under a different label. [Length: 20em] [iOS only]">
+        Sites
+      </message>
       <message name="IDS_IOS_SHOW_PASSWORD_VIEW_USERNAME" desc="Label indicating that the text displayed below is the username to which a saved password corresponds. The password is displayed in the same view, but under a different label. [Length: 20em] [iOS only]">
         Username
       </message>
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_SHOW_PASSWORD_VIEW_SITES.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_SHOW_PASSWORD_VIEW_SITES.png.sha1
new file mode 100644
index 0000000..6a842a9
--- /dev/null
+++ b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_SHOW_PASSWORD_VIEW_SITES.png.sha1
@@ -0,0 +1 @@
+690bdff1da32425a443dabc2f7a3df1a31dc79d7
\ No newline at end of file
diff --git a/ios/chrome/browser/browser_state/BUILD.gn b/ios/chrome/browser/browser_state/BUILD.gn
index ee2d6b2..431138b 100644
--- a/ios/chrome/browser/browser_state/BUILD.gn
+++ b/ios/chrome/browser/browser_state/BUILD.gn
@@ -140,7 +140,6 @@
     "//ios/chrome/browser/mailto_handler:mailto_handler_factory",
     "//ios/chrome/browser/metrics",
     "//ios/chrome/browser/net",
-    "//ios/chrome/browser/ntp_snippets",
     "//ios/chrome/browser/optimization_guide",
     "//ios/chrome/browser/passwords",
     "//ios/chrome/browser/paths",
diff --git a/ios/chrome/browser/browser_state/browser_state_keyed_service_factories.mm b/ios/chrome/browser/browser_state/browser_state_keyed_service_factories.mm
index b2999ab8..5410647 100644
--- a/ios/chrome/browser/browser_state/browser_state_keyed_service_factories.mm
+++ b/ios/chrome/browser/browser_state/browser_state_keyed_service_factories.mm
@@ -40,7 +40,6 @@
 #import "ios/chrome/browser/language/url_language_histogram_factory.h"
 #import "ios/chrome/browser/mailto_handler/mailto_handler_service_factory.h"
 #import "ios/chrome/browser/metrics/ios_profile_session_durations_service_factory.h"
-#import "ios/chrome/browser/ntp_snippets/ios_chrome_content_suggestions_service_factory.h"
 #import "ios/chrome/browser/optimization_guide/optimization_guide_service_factory.h"
 #import "ios/chrome/browser/passwords/ios_chrome_account_password_store_factory.h"
 #import "ios/chrome/browser/passwords/ios_chrome_password_check_manager_factory.h"
@@ -138,7 +137,6 @@
   GoogleLogoServiceFactory::GetInstance();
   IdentityManagerFactory::GetInstance();
   IOSChromeAccountPasswordStoreFactory::GetInstance();
-  IOSChromeContentSuggestionsServiceFactory::GetInstance();
   IOSChromeFaviconLoaderFactory::GetInstance();
   IOSChromeGCMProfileServiceFactory::GetInstance();
   IOSChromeLargeIconCacheFactory::GetInstance();
diff --git a/ios/chrome/browser/find_in_page/BUILD.gn b/ios/chrome/browser/find_in_page/BUILD.gn
index 19b4834..801f387 100644
--- a/ios/chrome/browser/find_in_page/BUILD.gn
+++ b/ios/chrome/browser/find_in_page/BUILD.gn
@@ -5,13 +5,15 @@
 source_set("find_in_page") {
   configs += [ "//build/config/compiler:enable_arc" ]
   sources = [
-    "find_in_page_controller.h",
-    "find_in_page_controller.mm",
+    "constants.h",
+    "constants.mm",
     "find_in_page_model.h",
     "find_in_page_model.mm",
     "find_in_page_response_delegate.h",
-    "find_tab_helper.h",
-    "find_tab_helper.mm",
+    "java_script_find_in_page_controller.h",
+    "java_script_find_in_page_controller.mm",
+    "java_script_find_tab_helper.h",
+    "java_script_find_tab_helper.mm",
   ]
   deps = [
     "//base",
@@ -27,7 +29,7 @@
 source_set("unit_tests") {
   testonly = true
   configs += [ "//build/config/compiler:enable_arc" ]
-  sources = [ "find_in_page_controller_unittest.mm" ]
+  sources = [ "java_script_find_in_page_controller_unittest.mm" ]
   deps = [
     ":find_in_page",
     "//base",
diff --git a/ios/chrome/browser/find_in_page/constants.h b/ios/chrome/browser/find_in_page/constants.h
new file mode 100644
index 0000000..898d6f71
--- /dev/null
+++ b/ios/chrome/browser/find_in_page/constants.h
@@ -0,0 +1,13 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_FIND_IN_PAGE_CONSTANTS_H_
+#define IOS_CHROME_BROWSER_FIND_IN_PAGE_CONSTANTS_H_
+
+#import <Foundation/Foundation.h>
+
+extern NSString* const kFindBarTextFieldWillBecomeFirstResponderNotification;
+extern NSString* const kFindBarTextFieldDidResignFirstResponderNotification;
+
+#endif  // IOS_CHROME_BROWSER_FIND_IN_PAGE_CONSTANTS_H_
diff --git a/ios/chrome/browser/find_in_page/constants.mm b/ios/chrome/browser/find_in_page/constants.mm
new file mode 100644
index 0000000..c580e9c
--- /dev/null
+++ b/ios/chrome/browser/find_in_page/constants.mm
@@ -0,0 +1,14 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/find_in_page/constants.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+NSString* const kFindBarTextFieldWillBecomeFirstResponderNotification =
+    @"kFindBarTextFieldWillBecomeFirstResponderNotification";
+NSString* const kFindBarTextFieldDidResignFirstResponderNotification =
+    @"kFindBarTextFieldDidResignFirstResponderNotification";
diff --git a/ios/chrome/browser/find_in_page/find_in_page_response_delegate.h b/ios/chrome/browser/find_in_page/find_in_page_response_delegate.h
index 010ec48a..272149c5 100644
--- a/ios/chrome/browser/find_in_page/find_in_page_response_delegate.h
+++ b/ios/chrome/browser/find_in_page/find_in_page_response_delegate.h
@@ -7,7 +7,7 @@
 
 @class FindInPageModel;
 
-// Delegate class to relay responses of FindInPageController calls to
+// Delegate class to relay responses of JavaScriptFindInPageController calls to
 // BrowserViewController.
 @protocol FindInPageResponseDelegate
 @optional
diff --git a/ios/chrome/browser/find_in_page/find_tab_helper.mm b/ios/chrome/browser/find_in_page/find_tab_helper.mm
deleted file mode 100644
index 2a02d2d..0000000
--- a/ios/chrome/browser/find_in_page/find_tab_helper.mm
+++ /dev/null
@@ -1,114 +0,0 @@
-// Copyright 2017 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/chrome/browser/find_in_page/find_tab_helper.h"
-
-#import "base/memory/ptr_util.h"
-#import "base/metrics/user_metrics.h"
-#import "base/metrics/user_metrics_action.h"
-#import "ios/chrome/browser/find_in_page/find_in_page_controller.h"
-#import "ios/chrome/browser/find_in_page/find_in_page_model.h"
-#import "ios/web/public/navigation/navigation_context.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-FindTabHelper::FindTabHelper(web::WebState* web_state) {
-  DCHECK(web_state);
-  observation_.Observe(web_state);
-
-  if (web_state->IsRealized()) {
-    CreateFindInPageController(web_state);
-  }
-}
-
-FindTabHelper::~FindTabHelper() {}
-
-void FindTabHelper::SetResponseDelegate(
-    id<FindInPageResponseDelegate> response_delegate) {
-  if (!controller_) {
-    response_delegate_ = response_delegate;
-  } else {
-    controller_.responseDelegate = response_delegate;
-  }
-}
-
-void FindTabHelper::StartFinding(NSString* search_term) {
-  [controller_ findStringInPage:search_term];
-}
-
-void FindTabHelper::ContinueFinding(FindDirection direction) {
-  if (direction == FORWARD) {
-    [controller_ findNextStringInPage];
-
-  } else if (direction == REVERSE) {
-    [controller_ findPreviousStringInPage];
-
-  } else {
-    NOTREACHED();
-  }
-}
-
-void FindTabHelper::StopFinding() {
-  SetFindUIActive(false);
-  [controller_ disableFindInPage];
-}
-
-FindInPageModel* FindTabHelper::GetFindResult() const {
-  return controller_.findInPageModel;
-}
-
-bool FindTabHelper::CurrentPageSupportsFindInPage() const {
-  return [controller_ canFindInPage];
-}
-
-bool FindTabHelper::IsFindUIActive() const {
-  return controller_.findInPageModel.enabled;
-}
-
-void FindTabHelper::SetFindUIActive(bool active) {
-  controller_.findInPageModel.enabled = active;
-}
-
-void FindTabHelper::PersistSearchTerm() {
-  [controller_ saveSearchTerm];
-}
-
-void FindTabHelper::RestoreSearchTerm() {
-  [controller_ restoreSearchTerm];
-}
-
-void FindTabHelper::CreateFindInPageController(web::WebState* web_state) {
-  DCHECK(!controller_);
-  controller_ = [[FindInPageController alloc] initWithWebState:web_state];
-  if (response_delegate_) {
-    controller_.responseDelegate = response_delegate_;
-    response_delegate_ = nil;
-  }
-}
-
-void FindTabHelper::WebStateRealized(web::WebState* web_state) {
-  CreateFindInPageController(web_state);
-}
-
-void FindTabHelper::WebStateDestroyed(web::WebState* web_state) {
-  observation_.Reset();
-
-  [controller_ detachFromWebState];
-  controller_ = nil;
-}
-
-void FindTabHelper::DidFinishNavigation(
-    web::WebState* web_state,
-    web::NavigationContext* navigation_context) {
-  if (navigation_context->IsSameDocument()) {
-    return;
-  }
-  if (IsFindUIActive()) {
-    StopFinding();
-  }
-}
-
-WEB_STATE_USER_DATA_KEY_IMPL(FindTabHelper)
diff --git a/ios/chrome/browser/find_in_page/find_in_page_controller.h b/ios/chrome/browser/find_in_page/java_script_find_in_page_controller.h
similarity index 81%
rename from ios/chrome/browser/find_in_page/find_in_page_controller.h
rename to ios/chrome/browser/find_in_page/java_script_find_in_page_controller.h
index ad916df1..aa9b79d7 100644
--- a/ios/chrome/browser/find_in_page/find_in_page_controller.h
+++ b/ios/chrome/browser/find_in_page/java_script_find_in_page_controller.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef IOS_CHROME_BROWSER_FIND_IN_PAGE_FIND_IN_PAGE_CONTROLLER_H_
-#define IOS_CHROME_BROWSER_FIND_IN_PAGE_FIND_IN_PAGE_CONTROLLER_H_
+#ifndef IOS_CHROME_BROWSER_FIND_IN_PAGE_JAVA_SCRIPT_FIND_IN_PAGE_CONTROLLER_H_
+#define IOS_CHROME_BROWSER_FIND_IN_PAGE_JAVA_SCRIPT_FIND_IN_PAGE_CONTROLLER_H_
 
 #import <Foundation/Foundation.h>
 
@@ -16,10 +16,7 @@
 @class FindInPageModel;
 @protocol FindInPageResponseDelegate;
 
-extern NSString* const kFindBarTextFieldWillBecomeFirstResponderNotification;
-extern NSString* const kFindBarTextFieldDidResignFirstResponderNotification;
-
-@interface FindInPageController : NSObject
+@interface JavaScriptFindInPageController : NSObject
 
 // Find In Page model.
 @property(nonatomic, readonly, strong) FindInPageModel* findInPageModel;
@@ -59,4 +56,4 @@
 + (NSString*)searchTerm;
 @end
 
-#endif  // IOS_CHROME_BROWSER_FIND_IN_PAGE_FIND_IN_PAGE_CONTROLLER_H_
+#endif  // IOS_CHROME_BROWSER_FIND_IN_PAGE_JAVA_SCRIPT_FIND_IN_PAGE_CONTROLLER_H_
diff --git a/ios/chrome/browser/find_in_page/find_in_page_controller.mm b/ios/chrome/browser/find_in_page/java_script_find_in_page_controller.mm
similarity index 92%
rename from ios/chrome/browser/find_in_page/find_in_page_controller.mm
rename to ios/chrome/browser/find_in_page/java_script_find_in_page_controller.mm
index a7eea5f4..4ee2ea9f 100644
--- a/ios/chrome/browser/find_in_page/find_in_page_controller.mm
+++ b/ios/chrome/browser/find_in_page/java_script_find_in_page_controller.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/chrome/browser/find_in_page/find_in_page_controller.h"
+#import "ios/chrome/browser/find_in_page/java_script_find_in_page_controller.h"
 
 #import <UIKit/UIKit.h>
 
@@ -13,10 +13,11 @@
 #import "base/mac/foundation_util.h"
 #import "base/notreached.h"
 #import "components/ukm/ios/ukm_url_recorder.h"
+#import "ios/chrome/browser/find_in_page/constants.h"
 #import "ios/chrome/browser/find_in_page/find_in_page_model.h"
 #import "ios/chrome/browser/find_in_page/find_in_page_response_delegate.h"
-#import "ios/web/public/find_in_page/find_in_page_manager.h"
 #import "ios/web/public/find_in_page/find_in_page_manager_delegate_bridge.h"
+#import "ios/web/public/find_in_page/java_script_find_in_page_manager.h"
 #import "ios/web/public/ui/crw_web_view_proxy.h"
 #import "ios/web/public/ui/crw_web_view_scroll_view_proxy.h"
 #import "ios/web/public/web_state.h"
@@ -26,11 +27,6 @@
 #error "This file requires ARC support."
 #endif
 
-NSString* const kFindBarTextFieldWillBecomeFirstResponderNotification =
-    @"kFindBarTextFieldWillBecomeFirstResponderNotification";
-NSString* const kFindBarTextFieldDidResignFirstResponderNotification =
-    @"kFindBarTextFieldDidResignFirstResponderNotification";
-
 namespace {
 // Keeps find in page search term to be shared between different tabs. Never
 // reset, not stored on disk.
@@ -41,9 +37,9 @@
 // TODO(crbug.com/1395828): This is a temporary workaround. The context string
 // announcement might still fail. A retry mechanism needs to be implemented.
 const int64_t kContextStringAnnouncementDelayInNanoseconds = 0.1 * NSEC_PER_SEC;
-}
+}  // namespace
 
-@interface FindInPageController () <CRWFindInPageManagerDelegate>
+@interface JavaScriptFindInPageController () <CRWFindInPageManagerDelegate>
 
 // The web view's scroll view.
 - (CRWWebViewScrollViewProxy*)webViewScrollView;
@@ -60,9 +56,9 @@
                    atPoint:(CGPoint)point;
 @end
 
-@implementation FindInPageController {
+@implementation JavaScriptFindInPageController {
   // Object that manages searches and match traversals.
-  web::FindInPageManager* _findInPageManager;
+  web::JavaScriptFindInPageManager* _findInPageManager;
 
   // Access to the web view from the web state.
   id<CRWWebViewProxy> _webViewProxy;
@@ -100,7 +96,8 @@
     _findInPageModel = [[FindInPageModel alloc] init];
     _findInPageDelegateBridge =
         std::make_unique<web::FindInPageManagerDelegateBridge>(self);
-    _findInPageManager = web::FindInPageManager::FromWebState(_webState);
+    _findInPageManager =
+        web::JavaScriptFindInPageManager::FromWebState(_webState);
     _findInPageManager->SetDelegate(_findInPageDelegateBridge.get());
 
     _webViewProxy = _webState->GetWebViewProxy();
@@ -179,8 +176,8 @@
   // to avoid WKWebView crash on deallocation due to outstanding completion
   // handler.
   if (_findStringStarted) {
-      _findInPageManager->StopFinding();
-      _findStringStarted = NO;
+    _findInPageManager->StopFinding();
+    _findStringStarted = NO;
   }
 }
 
@@ -201,7 +198,7 @@
 
 #pragma mark - CRWFindInPageManagerDelegate
 
-- (void)findInPageManager:(web::FindInPageManager*)manager
+- (void)findInPageManager:(web::JavaScriptFindInPageManager*)manager
     didHighlightMatchesOfQuery:(NSString*)query
                 withMatchCount:(NSInteger)matchCount
                    forWebState:(web::WebState*)webState {
@@ -215,7 +212,7 @@
   [self.responseDelegate findDidFinishWithUpdatedModel:self.findInPageModel];
 }
 
-- (void)findInPageManager:(web::FindInPageManager*)manager
+- (void)findInPageManager:(web::JavaScriptFindInPageManager*)manager
     didSelectMatchAtIndex:(NSInteger)index
         withContextString:(NSString*)contextString
               forWebState:(web::WebState*)webState {
diff --git a/ios/chrome/browser/find_in_page/find_in_page_controller_unittest.mm b/ios/chrome/browser/find_in_page/java_script_find_in_page_controller_unittest.mm
similarity index 88%
rename from ios/chrome/browser/find_in_page/find_in_page_controller_unittest.mm
rename to ios/chrome/browser/find_in_page/java_script_find_in_page_controller_unittest.mm
index e77d027..8e410e3 100644
--- a/ios/chrome/browser/find_in_page/find_in_page_controller_unittest.mm
+++ b/ios/chrome/browser/find_in_page/java_script_find_in_page_controller_unittest.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/chrome/browser/find_in_page/find_in_page_controller.h"
+#import "ios/chrome/browser/find_in_page/java_script_find_in_page_controller.h"
 
 #import "base/mac/foundation_util.h"
 #import "base/test/ios/wait_util.h"
@@ -41,9 +41,9 @@
 const char kFindInPageUkmSearchMatchesEvent[] = "IOS.FindInPageSearchMatches";
 const char kFindInPageUkmSearchMetric[] = "HasMatches";
 
-class FindInPageControllerTest : public PlatformTest {
+class JavaScriptFindInPageControllerTest : public PlatformTest {
  protected:
-  FindInPageControllerTest()
+  JavaScriptFindInPageControllerTest()
       : web_client_(std::make_unique<ChromeWebClient>()) {
     browser_state_ = TestChromeBrowserState::Builder().Build();
 
@@ -52,12 +52,12 @@
     web_state_->GetView();
     web_state_->SetKeepRenderProcessAlive(true);
   }
-  ~FindInPageControllerTest() override {}
+  ~JavaScriptFindInPageControllerTest() override {}
 
   void SetUp() override {
     PlatformTest::SetUp();
     find_in_page_controller_ =
-        [[FindInPageController alloc] initWithWebState:web_state()];
+        [[JavaScriptFindInPageController alloc] initWithWebState:web_state()];
     delegate_ = [[TestFindInPageResponseDelegate alloc] init];
     find_in_page_controller_.responseDelegate = delegate_;
     ukm::InitializeSourceUrlRecorderForWebState(web_state());
@@ -75,14 +75,14 @@
   web::WebTaskEnvironment task_environment_;
   std::unique_ptr<TestChromeBrowserState> browser_state_;
   std::unique_ptr<web::WebState> web_state_;
-  FindInPageController* find_in_page_controller_ = nil;
+  JavaScriptFindInPageController* find_in_page_controller_ = nil;
   TestFindInPageResponseDelegate* delegate_;
   ukm::TestAutoSetUkmRecorder test_ukm_recorder_;
 };
 
 // Loads html that contains "some string", searches for it, and ensures UKM has
 // logged the search as having found a match.
-TEST_F(FindInPageControllerTest, VerifyUKMLoggedTrue) {
+TEST_F(JavaScriptFindInPageControllerTest, VerifyUKMLoggedTrue) {
   test_ukm_recorder_.Purge();
   web::test::LoadHtml(@"<html><p>some string</p></html>", web_state());
   [find_in_page_controller_ findStringInPage:@"some string"];
@@ -101,7 +101,7 @@
 
 // Loads html that contains "some string", searches for something that does not
 // match, and ensures UKM has not logged the search as having found a match.
-TEST_F(FindInPageControllerTest, VerifyUKMLoggedFalse) {
+TEST_F(JavaScriptFindInPageControllerTest, VerifyUKMLoggedFalse) {
   test_ukm_recorder_.Purge();
   web::test::LoadHtml(@"<html><p>some string</p></html>", web_state());
   [find_in_page_controller_ findStringInPage:@"nothing"];
@@ -118,4 +118,4 @@
   test_ukm_recorder_.ExpectEntryMetric(entry, kFindInPageUkmSearchMetric,
                                        false);
 }
-}
+}  // namespace
diff --git a/ios/chrome/browser/find_in_page/find_tab_helper.h b/ios/chrome/browser/find_in_page/java_script_find_tab_helper.h
similarity index 75%
rename from ios/chrome/browser/find_in_page/find_tab_helper.h
rename to ios/chrome/browser/find_in_page/java_script_find_tab_helper.h
index 102d3c0..2cef4a8 100644
--- a/ios/chrome/browser/find_in_page/find_tab_helper.h
+++ b/ios/chrome/browser/find_in_page/java_script_find_tab_helper.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef IOS_CHROME_BROWSER_FIND_IN_PAGE_FIND_TAB_HELPER_H_
-#define IOS_CHROME_BROWSER_FIND_IN_PAGE_FIND_TAB_HELPER_H_
+#ifndef IOS_CHROME_BROWSER_FIND_IN_PAGE_JAVA_SCRIPT_FIND_TAB_HELPER_H_
+#define IOS_CHROME_BROWSER_FIND_IN_PAGE_JAVA_SCRIPT_FIND_TAB_HELPER_H_
 
 #include <Foundation/Foundation.h>
 
@@ -12,18 +12,19 @@
 #include "ios/web/public/web_state_observer.h"
 #import "ios/web/public/web_state_user_data.h"
 
-@class FindInPageController;
+@class JavaScriptFindInPageController;
 @class FindInPageModel;
 @protocol FindInPageResponseDelegate;
 
 // Adds support for the "Find in page" feature.
-class FindTabHelper : public web::WebStateObserver,
-                      public web::WebStateUserData<FindTabHelper> {
+class JavaScriptFindTabHelper
+    : public web::WebStateObserver,
+      public web::WebStateUserData<JavaScriptFindTabHelper> {
  public:
-  FindTabHelper(const FindTabHelper&) = delete;
-  FindTabHelper& operator=(const FindTabHelper&) = delete;
+  JavaScriptFindTabHelper(const JavaScriptFindTabHelper&) = delete;
+  JavaScriptFindTabHelper& operator=(const JavaScriptFindTabHelper&) = delete;
 
-  ~FindTabHelper() override;
+  ~JavaScriptFindTabHelper() override;
 
   enum FindDirection {
     FORWARD,
@@ -70,13 +71,13 @@
 
  private:
   friend class FindTabHelperTest;
-  friend class web::WebStateUserData<FindTabHelper>;
+  friend class web::WebStateUserData<JavaScriptFindTabHelper>;
 
   // Private constructor used by CreateForWebState().
-  FindTabHelper(web::WebState* web_state);
+  JavaScriptFindTabHelper(web::WebState* web_state);
 
-  // Create the FindInPageController for `web_state`. Only called if/when
-  // the WebState is realized.
+  // Create the JavaScriptFindInPageController for `web_state`. Only called
+  // if/when the WebState is realized.
   void CreateFindInPageController(web::WebState* web_state);
 
   // web::WebStateObserver.
@@ -86,9 +87,10 @@
                            web::NavigationContext* navigation_context) override;
 
   // The ObjC find in page controller (nil if the WebState is not realized).
-  FindInPageController* controller_ = nil;
+  JavaScriptFindInPageController* controller_ = nil;
 
-  // The delegate to register with FindInPageController when it is created.
+  // The delegate to register with JavaScriptFindInPageController when it is
+  // created.
   __weak id<FindInPageResponseDelegate> response_delegate_ = nil;
 
   // Manage the registration of this instance as a WebStateObserver.
@@ -98,4 +100,4 @@
   WEB_STATE_USER_DATA_KEY_DECL();
 };
 
-#endif  // IOS_CHROME_BROWSER_FIND_IN_PAGE_FIND_TAB_HELPER_H_
+#endif  // IOS_CHROME_BROWSER_FIND_IN_PAGE_JAVA_SCRIPT_FIND_TAB_HELPER_H_
diff --git a/ios/chrome/browser/find_in_page/java_script_find_tab_helper.mm b/ios/chrome/browser/find_in_page/java_script_find_tab_helper.mm
new file mode 100644
index 0000000..338f396c
--- /dev/null
+++ b/ios/chrome/browser/find_in_page/java_script_find_tab_helper.mm
@@ -0,0 +1,116 @@
+// Copyright 2017 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/find_in_page/java_script_find_tab_helper.h"
+
+#import "base/memory/ptr_util.h"
+#import "base/metrics/user_metrics.h"
+#import "base/metrics/user_metrics_action.h"
+#import "ios/chrome/browser/find_in_page/find_in_page_model.h"
+#import "ios/chrome/browser/find_in_page/java_script_find_in_page_controller.h"
+#import "ios/web/public/navigation/navigation_context.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+JavaScriptFindTabHelper::JavaScriptFindTabHelper(web::WebState* web_state) {
+  DCHECK(web_state);
+  observation_.Observe(web_state);
+
+  if (web_state->IsRealized()) {
+    CreateFindInPageController(web_state);
+  }
+}
+
+JavaScriptFindTabHelper::~JavaScriptFindTabHelper() {}
+
+void JavaScriptFindTabHelper::SetResponseDelegate(
+    id<FindInPageResponseDelegate> response_delegate) {
+  if (!controller_) {
+    response_delegate_ = response_delegate;
+  } else {
+    controller_.responseDelegate = response_delegate;
+  }
+}
+
+void JavaScriptFindTabHelper::StartFinding(NSString* search_term) {
+  [controller_ findStringInPage:search_term];
+}
+
+void JavaScriptFindTabHelper::ContinueFinding(FindDirection direction) {
+  if (direction == FORWARD) {
+    [controller_ findNextStringInPage];
+
+  } else if (direction == REVERSE) {
+    [controller_ findPreviousStringInPage];
+
+  } else {
+    NOTREACHED();
+  }
+}
+
+void JavaScriptFindTabHelper::StopFinding() {
+  SetFindUIActive(false);
+  [controller_ disableFindInPage];
+}
+
+FindInPageModel* JavaScriptFindTabHelper::GetFindResult() const {
+  return controller_.findInPageModel;
+}
+
+bool JavaScriptFindTabHelper::CurrentPageSupportsFindInPage() const {
+  return [controller_ canFindInPage];
+}
+
+bool JavaScriptFindTabHelper::IsFindUIActive() const {
+  return controller_.findInPageModel.enabled;
+}
+
+void JavaScriptFindTabHelper::SetFindUIActive(bool active) {
+  controller_.findInPageModel.enabled = active;
+}
+
+void JavaScriptFindTabHelper::PersistSearchTerm() {
+  [controller_ saveSearchTerm];
+}
+
+void JavaScriptFindTabHelper::RestoreSearchTerm() {
+  [controller_ restoreSearchTerm];
+}
+
+void JavaScriptFindTabHelper::CreateFindInPageController(
+    web::WebState* web_state) {
+  DCHECK(!controller_);
+  controller_ =
+      [[JavaScriptFindInPageController alloc] initWithWebState:web_state];
+  if (response_delegate_) {
+    controller_.responseDelegate = response_delegate_;
+    response_delegate_ = nil;
+  }
+}
+
+void JavaScriptFindTabHelper::WebStateRealized(web::WebState* web_state) {
+  CreateFindInPageController(web_state);
+}
+
+void JavaScriptFindTabHelper::WebStateDestroyed(web::WebState* web_state) {
+  observation_.Reset();
+
+  [controller_ detachFromWebState];
+  controller_ = nil;
+}
+
+void JavaScriptFindTabHelper::DidFinishNavigation(
+    web::WebState* web_state,
+    web::NavigationContext* navigation_context) {
+  if (navigation_context->IsSameDocument()) {
+    return;
+  }
+  if (IsFindUIActive()) {
+    StopFinding();
+  }
+}
+
+WEB_STATE_USER_DATA_KEY_IMPL(JavaScriptFindTabHelper)
diff --git a/ios/chrome/browser/flags/about_flags.mm b/ios/chrome/browser/flags/about_flags.mm
index a6a0a09..20f6c91c 100644
--- a/ios/chrome/browser/flags/about_flags.mm
+++ b/ios/chrome/browser/flags/about_flags.mm
@@ -1247,6 +1247,13 @@
      flag_descriptions::kOmniboxMultilineSearchSuggestName,
      flag_descriptions::kOmniboxMultilineSearchSuggestDescription,
      flags_ui::kOsIos, FEATURE_VALUE_TYPE(kOmniboxMultilineSearchSuggest)},
+    {"autofill-suggest-server-card-instead-of-local-card",
+     flag_descriptions::kAutofillSuggestServerCardInsteadOfLocalCardName,
+     flag_descriptions::kAutofillSuggestServerCardInsteadOfLocalCardDescription,
+     flags_ui::kOsIos,
+     FEATURE_VALUE_TYPE(
+         autofill::features::kAutofillSuggestServerCardInsteadOfLocalCard)},
+
 };
 
 bool SkipConditionalFeatureEntry(const flags_ui::FeatureEntry& entry) {
diff --git a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
index 243e31cd..3a5e933 100644
--- a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
+++ b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
@@ -103,6 +103,13 @@
     "Dismisses the Save Card Infobar on a user initiated Navigation, other "
     "than one caused by submitted form.";
 
+const char kAutofillSuggestServerCardInsteadOfLocalCardName[] =
+    "Suggest Server card instead of Local card for deduped cards";
+const char kAutofillSuggestServerCardInsteadOfLocalCardDescription[] =
+    "When enabled, Autofill suggestions that consist of a local and server "
+    "version of the same card will attempt to fill the server card upon "
+    "selection instead of the local card.";
+
 const char kAutofillUpstreamAllowAdditionalEmailDomainsName[] =
     "Allow Autofill credit card upload save for select non-Google-based "
     "accounts";
diff --git a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
index ce9fb21..a120abf6 100644
--- a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
+++ b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
@@ -89,6 +89,11 @@
 extern const char kAutofillSaveCardDismissOnNavigationName[];
 extern const char kAutofillSaveCardDismissOnNavigationDescription[];
 
+// Title and description for the flag to suggest Server card instead of a
+// deduped Local card.
+extern const char kAutofillSuggestServerCardInsteadOfLocalCardName[];
+extern const char kAutofillSuggestServerCardInsteadOfLocalCardDescription[];
+
 // Title and description for the flag to control allowing credit card upload
 // save for accounts from common email providers.
 extern const char kAutofillUpstreamAllowAdditionalEmailDomainsName[];
diff --git a/ios/chrome/browser/ntp/BUILD.gn b/ios/chrome/browser/ntp/BUILD.gn
index 8412f130..d3c4639 100644
--- a/ios/chrome/browser/ntp/BUILD.gn
+++ b/ios/chrome/browser/ntp/BUILD.gn
@@ -60,7 +60,6 @@
     "//ios/chrome/browser/browser_state:test_support",
     "//ios/chrome/browser/favicon:favicon",
     "//ios/chrome/browser/ntp",
-    "//ios/chrome/browser/ntp_snippets:ntp_snippets",
     "//ios/chrome/browser/prefs:pref_names",
     "//ios/chrome/browser/search_engines:search_engines",
     "//ios/chrome/browser/url:constants",
diff --git a/ios/chrome/browser/ntp/new_tab_page_tab_helper_unittest.mm b/ios/chrome/browser/ntp/new_tab_page_tab_helper_unittest.mm
index 1cb913d..452d805 100644
--- a/ios/chrome/browser/ntp/new_tab_page_tab_helper_unittest.mm
+++ b/ios/chrome/browser/ntp/new_tab_page_tab_helper_unittest.mm
@@ -13,7 +13,6 @@
 #import "ios/chrome/browser/browser_state/test_chrome_browser_state_manager.h"
 #import "ios/chrome/browser/favicon/ios_chrome_large_icon_service_factory.h"
 #import "ios/chrome/browser/ntp/new_tab_page_tab_helper_delegate.h"
-#import "ios/chrome/browser/ntp_snippets/ios_chrome_content_suggestions_service_factory.h"
 #import "ios/chrome/browser/search_engines/template_url_service_factory.h"
 #import "ios/chrome/browser/url/chrome_url_constants.h"
 #import "ios/chrome/browser/web_state_list/fake_web_state_list_delegate.h"
@@ -48,9 +47,6 @@
         ios::TemplateURLServiceFactory::GetInstance(),
         ios::TemplateURLServiceFactory::GetDefaultFactory());
     test_cbs_builder.AddTestingFactory(
-        IOSChromeContentSuggestionsServiceFactory::GetInstance(),
-        IOSChromeContentSuggestionsServiceFactory::GetDefaultFactory());
-    test_cbs_builder.AddTestingFactory(
         IOSChromeLargeIconServiceFactory::GetInstance(),
         IOSChromeLargeIconServiceFactory::GetDefaultFactory());
 
diff --git a/ios/chrome/browser/ntp_snippets/BUILD.gn b/ios/chrome/browser/ntp_snippets/BUILD.gn
deleted file mode 100644
index 7e35d39..0000000
--- a/ios/chrome/browser/ntp_snippets/BUILD.gn
+++ /dev/null
@@ -1,42 +0,0 @@
-# Copyright 2016 The Chromium Authors
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-source_set("ntp_snippets") {
-  configs += [ "//build/config/compiler:enable_arc" ]
-  sources = [
-    "ios_chrome_content_suggestions_service_factory.cc",
-    "ios_chrome_content_suggestions_service_factory.h",
-    "ios_chrome_content_suggestions_service_factory_util.h",
-    "ios_chrome_content_suggestions_service_factory_util.mm",
-  ]
-  deps = [
-    "//base",
-    "//components/bookmarks/browser",
-    "//components/image_fetcher/core",
-    "//components/image_fetcher/ios",
-    "//components/keyed_service/core",
-    "//components/keyed_service/ios",
-    "//components/ntp_snippets",
-    "//components/pref_registry",
-    "//components/reading_list/core",
-    "//components/signin/public/identity_manager",
-    "//components/version_info",
-    "//google_apis",
-    "//ios/chrome/app:tests_hook",
-    "//ios/chrome/browser/application_context",
-    "//ios/chrome/browser/bookmarks",
-    "//ios/chrome/browser/browser_state",
-    "//ios/chrome/browser/favicon",
-    "//ios/chrome/browser/history",
-    "//ios/chrome/browser/json_parser",
-    "//ios/chrome/browser/prefs:pref_names",
-    "//ios/chrome/browser/reading_list",
-    "//ios/chrome/browser/signin",
-    "//ios/chrome/browser/ui:feature_flags",
-    "//ios/chrome/browser/ui/util",
-    "//ios/chrome/common",
-    "//ios/web",
-    "//net",
-  ]
-}
diff --git a/ios/chrome/browser/ntp_snippets/DEPS b/ios/chrome/browser/ntp_snippets/DEPS
deleted file mode 100644
index 1365eff8..0000000
--- a/ios/chrome/browser/ntp_snippets/DEPS
+++ /dev/null
@@ -1,7 +0,0 @@
-specific_include_rules = {
-  # TODO(crbug.com/1294160): Remove this dependency.
-  "^ios_chrome_content_suggestions_service_factory_util.mm": [
-    "+ios/chrome/browser/ui/ui_feature_flags.h",
-  ],
-}
-
diff --git a/ios/chrome/browser/ntp_snippets/OWNERS b/ios/chrome/browser/ntp_snippets/OWNERS
deleted file mode 100644
index 7f58514..0000000
--- a/ios/chrome/browser/ntp_snippets/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-noyau@chromium.org
-gambard@chromium.org
diff --git a/ios/chrome/browser/ntp_snippets/ios_chrome_content_suggestions_service_factory.cc b/ios/chrome/browser/ntp_snippets/ios_chrome_content_suggestions_service_factory.cc
deleted file mode 100644
index fa01f003..0000000
--- a/ios/chrome/browser/ntp_snippets/ios_chrome_content_suggestions_service_factory.cc
+++ /dev/null
@@ -1,81 +0,0 @@
-// Copyright 2015 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ios/chrome/browser/ntp_snippets/ios_chrome_content_suggestions_service_factory.h"
-
-#include "base/bind.h"
-#include "base/no_destructor.h"
-#include "components/keyed_service/ios/browser_state_dependency_manager.h"
-#include "components/ntp_snippets/content_suggestions_service.h"
-#include "components/pref_registry/pref_registry_syncable.h"
-#include "ios/chrome/app/tests_hook.h"
-#include "ios/chrome/browser/browser_state/chrome_browser_state.h"
-#include "ios/chrome/browser/favicon/ios_chrome_large_icon_service_factory.h"
-#include "ios/chrome/browser/history/history_service_factory.h"
-#include "ios/chrome/browser/ntp_snippets/ios_chrome_content_suggestions_service_factory_util.h"
-#import "ios/chrome/browser/prefs/pref_names.h"
-#include "ios/chrome/browser/reading_list/reading_list_model_factory.h"
-#include "ios/chrome/browser/signin/identity_manager_factory.h"
-
-using ntp_snippets::ContentSuggestionsService;
-
-// static
-ContentSuggestionsService*
-IOSChromeContentSuggestionsServiceFactory::GetForBrowserState(
-    ChromeBrowserState* browser_state) {
-  return static_cast<ContentSuggestionsService*>(
-      GetInstance()->GetServiceForBrowserState(browser_state, true));
-}
-
-// static
-IOSChromeContentSuggestionsServiceFactory*
-IOSChromeContentSuggestionsServiceFactory::GetInstance() {
-  static base::NoDestructor<IOSChromeContentSuggestionsServiceFactory> instance;
-  return instance.get();
-}
-
-// static
-BrowserStateKeyedServiceFactory::TestingFactory
-IOSChromeContentSuggestionsServiceFactory::GetDefaultFactory() {
-  return base::BindRepeating(
-      &ntp_snippets::CreateChromeContentSuggestionsServiceWithProviders);
-}
-
-IOSChromeContentSuggestionsServiceFactory::
-    IOSChromeContentSuggestionsServiceFactory()
-    : BrowserStateKeyedServiceFactory(
-          "ContentSuggestionsService",
-          BrowserStateDependencyManager::GetInstance()) {
-  DependsOn(IdentityManagerFactory::GetInstance());
-  DependsOn(ios::HistoryServiceFactory::GetInstance());
-  DependsOn(IOSChromeLargeIconServiceFactory::GetInstance());
-  DependsOn(ReadingListModelFactory::GetInstance());
-}
-
-IOSChromeContentSuggestionsServiceFactory::
-    ~IOSChromeContentSuggestionsServiceFactory() {}
-
-std::unique_ptr<KeyedService>
-IOSChromeContentSuggestionsServiceFactory::BuildServiceInstanceFor(
-    web::BrowserState* browser_state) const {
-  if (tests_hook::DisableContentSuggestions()) {
-    return ntp_snippets::CreateChromeContentSuggestionsService(browser_state);
-  } else {
-    return ntp_snippets::CreateChromeContentSuggestionsServiceWithProviders(
-        browser_state);
-  }
-}
-
-void IOSChromeContentSuggestionsServiceFactory::RegisterBrowserStatePrefs(
-    user_prefs::PrefRegistrySyncable* registry) {
-  registry->RegisterBooleanPref(
-      prefs::kArticlesForYouEnabled, true,
-      user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
-  registry->RegisterBooleanPref(
-      prefs::kNTPContentSuggestionsEnabled, true,
-      user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
-  registry->RegisterBooleanPref(
-      prefs::kNTPContentSuggestionsForSupervisedUserEnabled, true,
-      user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
-}
diff --git a/ios/chrome/browser/ntp_snippets/ios_chrome_content_suggestions_service_factory.h b/ios/chrome/browser/ntp_snippets/ios_chrome_content_suggestions_service_factory.h
deleted file mode 100644
index 61ed197..0000000
--- a/ios/chrome/browser/ntp_snippets/ios_chrome_content_suggestions_service_factory.h
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright 2015 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_NTP_SNIPPETS_IOS_CHROME_CONTENT_SUGGESTIONS_SERVICE_FACTORY_H_
-#define IOS_CHROME_BROWSER_NTP_SNIPPETS_IOS_CHROME_CONTENT_SUGGESTIONS_SERVICE_FACTORY_H_
-
-#include <memory>
-
-#include "base/no_destructor.h"
-#include "components/keyed_service/ios/browser_state_keyed_service_factory.h"
-
-class ChromeBrowserState;
-
-namespace ntp_snippets {
-class ContentSuggestionsService;
-}  // namespace ntp_snippets
-
-// A factory to create a ContentSuggestionsService and associate it to
-// ChromeBrowserState.
-class IOSChromeContentSuggestionsServiceFactory
-    : public BrowserStateKeyedServiceFactory {
- public:
-  static ntp_snippets::ContentSuggestionsService* GetForBrowserState(
-      ChromeBrowserState* browser_state);
-
-  static IOSChromeContentSuggestionsServiceFactory* GetInstance();
-
-  // Returns the default factory used to build ContentSuggestionsServices. Can
-  // be registered with SetTestingFactory to use real instances during testing.
-  static TestingFactory GetDefaultFactory();
-
-  IOSChromeContentSuggestionsServiceFactory(
-      const IOSChromeContentSuggestionsServiceFactory&) = delete;
-  IOSChromeContentSuggestionsServiceFactory& operator=(
-      const IOSChromeContentSuggestionsServiceFactory&) = delete;
-
- private:
-  friend class base::NoDestructor<IOSChromeContentSuggestionsServiceFactory>;
-
-  IOSChromeContentSuggestionsServiceFactory();
-  ~IOSChromeContentSuggestionsServiceFactory() override;
-
-  // BrowserStateKeyedServiceFactory implementation.
-  std::unique_ptr<KeyedService> BuildServiceInstanceFor(
-      web::BrowserState* context) const override;
-  void RegisterBrowserStatePrefs(
-      user_prefs::PrefRegistrySyncable* registry) override;
-};
-
-#endif  // IOS_CHROME_BROWSER_NTP_SNIPPETS_IOS_CHROME_CONTENT_SUGGESTIONS_SERVICE_FACTORY_H_
diff --git a/ios/chrome/browser/ntp_snippets/ios_chrome_content_suggestions_service_factory_util.h b/ios/chrome/browser/ntp_snippets/ios_chrome_content_suggestions_service_factory_util.h
deleted file mode 100644
index 343628b..0000000
--- a/ios/chrome/browser/ntp_snippets/ios_chrome_content_suggestions_service_factory_util.h
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright 2017 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_NTP_SNIPPETS_IOS_CHROME_CONTENT_SUGGESTIONS_SERVICE_FACTORY_UTIL_H_
-#define IOS_CHROME_BROWSER_NTP_SNIPPETS_IOS_CHROME_CONTENT_SUGGESTIONS_SERVICE_FACTORY_UTIL_H_
-
-#include <memory>
-
-#include "components/keyed_service/core/keyed_service.h"
-#include "ios/web/public/browser_state.h"
-
-namespace ntp_snippets {
-class ContentSuggestionsService;
-}  // namespace ntp_snippets
-
-namespace ntp_snippets {
-
-// Returns a new ContentSuggestionsService with all providers registered.
-std::unique_ptr<KeyedService>
-CreateChromeContentSuggestionsServiceWithProviders(
-    web::BrowserState* browser_state);
-
-// Returns a new ContentSuggestionsService with no provider registered.
-std::unique_ptr<KeyedService> CreateChromeContentSuggestionsService(
-    web::BrowserState* browser_state);
-
-// Registers the RemoteSuggestionsProvider (articles provider).
-void RegisterRemoteSuggestionsProvider(ContentSuggestionsService* service,
-                                       web::BrowserState* browser_state);
-
-}  // namespace ntp_snippets
-
-#endif  // IOS_CHROME_BROWSER_NTP_SNIPPETS_IOS_CHROME_CONTENT_SUGGESTIONS_SERVICE_FACTORY_UTIL_H_
diff --git a/ios/chrome/browser/ntp_snippets/ios_chrome_content_suggestions_service_factory_util.mm b/ios/chrome/browser/ntp_snippets/ios_chrome_content_suggestions_service_factory_util.mm
deleted file mode 100644
index 28f0f35..0000000
--- a/ios/chrome/browser/ntp_snippets/ios_chrome_content_suggestions_service_factory_util.mm
+++ /dev/null
@@ -1,175 +0,0 @@
-// Copyright 2017 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/chrome/browser/ntp_snippets/ios_chrome_content_suggestions_service_factory_util.h"
-
-#import <string>
-#import <utility>
-
-#import "base/bind.h"
-#import "base/feature_list.h"
-#import "base/files/file_path.h"
-#import "base/threading/thread_task_runner_handle.h"
-#import "base/time/default_clock.h"
-#import "base/timer/timer.h"
-#import "base/values.h"
-#import "components/image_fetcher/core/image_decoder.h"
-#import "components/image_fetcher/core/image_fetcher.h"
-#import "components/image_fetcher/core/image_fetcher_impl.h"
-#import "components/image_fetcher/ios/ios_image_decoder_impl.h"
-#import "components/keyed_service/core/service_access_type.h"
-#import "components/keyed_service/ios/browser_state_dependency_manager.h"
-#import "components/ntp_snippets/category_rankers/category_ranker.h"
-#import "components/ntp_snippets/category_rankers/click_based_category_ranker.h"
-#import "components/ntp_snippets/category_rankers/constant_category_ranker.h"
-#import "components/ntp_snippets/content_suggestions_service.h"
-#import "components/ntp_snippets/features.h"
-#import "components/ntp_snippets/ntp_snippets_constants.h"
-#import "components/ntp_snippets/remote/persistent_scheduler.h"
-#import "components/ntp_snippets/remote/remote_suggestions_database.h"
-#import "components/ntp_snippets/remote/remote_suggestions_fetcher_impl.h"
-#import "components/ntp_snippets/remote/remote_suggestions_provider_impl.h"
-#import "components/ntp_snippets/remote/remote_suggestions_scheduler_impl.h"
-#import "components/ntp_snippets/remote/remote_suggestions_status_service_impl.h"
-#import "components/ntp_snippets/user_classifier.h"
-#import "components/signin/public/identity_manager/identity_manager.h"
-#import "components/version_info/version_info.h"
-#import "google_apis/google_api_keys.h"
-#import "ios/chrome/browser/application_context/application_context.h"
-#import "ios/chrome/browser/browser_state/chrome_browser_state.h"
-#import "ios/chrome/browser/favicon/ios_chrome_large_icon_service_factory.h"
-#import "ios/chrome/browser/history/history_service_factory.h"
-#import "ios/chrome/browser/json_parser/in_process_json_parser.h"
-#import "ios/chrome/browser/prefs/pref_names.h"
-#import "ios/chrome/browser/signin/identity_manager_factory.h"
-#import "ios/chrome/browser/ui/ui_feature_flags.h"
-#import "ios/chrome/common/channel_info.h"
-#import "ios/web/public/browser_state.h"
-#import "net/url_request/url_request_context_getter.h"
-#import "services/network/public/cpp/shared_url_loader_factory.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-using history::HistoryService;
-using image_fetcher::CreateIOSImageDecoder;
-using image_fetcher::ImageFetcherImpl;
-using ntp_snippets::ContentSuggestionsService;
-using ntp_snippets::GetFetchEndpoint;
-using ntp_snippets::PersistentScheduler;
-using ntp_snippets::RemoteSuggestionsDatabase;
-using ntp_snippets::RemoteSuggestionsFetcherImpl;
-using ntp_snippets::RemoteSuggestionsProviderImpl;
-using ntp_snippets::RemoteSuggestionsSchedulerImpl;
-using ntp_snippets::RemoteSuggestionsStatusServiceImpl;
-using ntp_snippets::UserClassifier;
-
-namespace ntp_snippets {
-
-std::unique_ptr<KeyedService>
-CreateChromeContentSuggestionsServiceWithProviders(
-    web::BrowserState* browser_state) {
-  auto service =
-      ntp_snippets::CreateChromeContentSuggestionsService(browser_state);
-  ContentSuggestionsService* suggestions_service =
-      static_cast<ContentSuggestionsService*>(service.get());
-
-  if (base::FeatureList::IsEnabled(ntp_snippets::kArticleSuggestionsFeature)) {
-    ntp_snippets::RegisterRemoteSuggestionsProvider(suggestions_service,
-                                                    browser_state);
-  }
-
-  return service;
-}
-
-std::unique_ptr<KeyedService> CreateChromeContentSuggestionsService(
-    web::BrowserState* browser_state) {
-  using State = ContentSuggestionsService::State;
-  ChromeBrowserState* chrome_browser_state =
-      ChromeBrowserState::FromBrowserState(browser_state);
-  DCHECK(!browser_state->IsOffTheRecord());
-  PrefService* prefs = chrome_browser_state->GetPrefs();
-
-  auto user_classifier = std::make_unique<UserClassifier>(
-      prefs, base::DefaultClock::GetInstance());
-
-  // TODO(crbug.com/676249): Implement a persistent scheduler for iOS.
-  auto scheduler = std::make_unique<RemoteSuggestionsSchedulerImpl>(
-      /*persistent_scheduler=*/nullptr, user_classifier.get(), prefs,
-      GetApplicationContext()->GetLocalState(),
-      base::DefaultClock::GetInstance());
-
-  // Create the ContentSuggestionsService.
-  signin::IdentityManager* identity_manager =
-      IdentityManagerFactory::GetForBrowserState(chrome_browser_state);
-  HistoryService* history_service =
-      ios::HistoryServiceFactory::GetForBrowserState(
-          chrome_browser_state, ServiceAccessType::EXPLICIT_ACCESS);
-  favicon::LargeIconService* large_icon_service =
-      IOSChromeLargeIconServiceFactory::GetForBrowserState(
-          chrome_browser_state);
-  std::unique_ptr<ntp_snippets::CategoryRanker> category_ranker =
-      ntp_snippets::BuildSelectedCategoryRanker(
-          prefs, base::DefaultClock::GetInstance());
-  return std::make_unique<ContentSuggestionsService>(
-      State::ENABLED, identity_manager, history_service, large_icon_service,
-      prefs, std::move(category_ranker), std::move(user_classifier),
-      std::move(scheduler));
-}
-
-void RegisterRemoteSuggestionsProvider(ContentSuggestionsService* service,
-                                       web::BrowserState* browser_state) {
-  ChromeBrowserState* chrome_browser_state =
-      ChromeBrowserState::FromBrowserState(browser_state);
-  PrefService* prefs = chrome_browser_state->GetPrefs();
-  signin::IdentityManager* identity_manager =
-      IdentityManagerFactory::GetForBrowserState(chrome_browser_state);
-  scoped_refptr<net::URLRequestContextGetter> request_context =
-      browser_state->GetRequestContext();
-  scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory =
-      browser_state->GetSharedURLLoaderFactory();
-
-  base::FilePath database_dir(
-      browser_state->GetStatePath().Append(ntp_snippets::kDatabaseFolder));
-
-  std::string api_key;
-  // This API needs allow-listed API keys. Get the key only if it is not a
-  // dummy key.
-  if (google_apis::HasAPIKeyConfigured()) {
-    bool is_stable_channel = GetChannel() == version_info::Channel::STABLE;
-    api_key = is_stable_channel ? google_apis::GetAPIKey()
-                                : google_apis::GetNonStableAPIKey();
-  }
-  auto suggestions_fetcher = std::make_unique<RemoteSuggestionsFetcherImpl>(
-      identity_manager, url_loader_factory, prefs, nullptr,
-      base::BindRepeating(&InProcessJsonParser::Parse), GetFetchEndpoint(),
-      api_key, service->user_classifier());
-
-  leveldb_proto::ProtoDatabaseProvider* db_provider =
-      chrome_browser_state->GetProtoDatabaseProvider();
-
-  // This pref is also used for logging. If it is changed, change it in the
-  // other places.
-  std::vector<std::string> prefs_vector = {prefs::kArticlesForYouEnabled};
-  prefs_vector.push_back(prefs::kNTPContentSuggestionsEnabled);
-
-  auto provider = std::make_unique<RemoteSuggestionsProviderImpl>(
-      service, prefs, GetApplicationContext()->GetApplicationLocale(),
-      service->category_ranker(), service->remote_suggestions_scheduler(),
-      std::move(suggestions_fetcher),
-      std::make_unique<ImageFetcherImpl>(
-          CreateIOSImageDecoder(), browser_state->GetSharedURLLoaderFactory()),
-      std::make_unique<RemoteSuggestionsDatabase>(db_provider, database_dir),
-      std::make_unique<RemoteSuggestionsStatusServiceImpl>(
-          identity_manager->HasPrimaryAccount(signin::ConsentLevel::kSync),
-          prefs, prefs_vector),
-      std::make_unique<base::OneShotTimer>());
-
-  service->remote_suggestions_scheduler()->SetProvider(provider.get());
-  service->set_remote_suggestions_provider(provider.get());
-  service->RegisterProvider(std::move(provider));
-}
-
-}  // namespace ntp_snippets
diff --git a/ios/chrome/browser/policy/BUILD.gn b/ios/chrome/browser/policy/BUILD.gn
index 8d3b0491..ee99e89a 100644
--- a/ios/chrome/browser/policy/BUILD.gn
+++ b/ios/chrome/browser/policy/BUILD.gn
@@ -343,7 +343,6 @@
   ]
   deps = [
     "//base",
-    "//ios/chrome/test/earl_grey:eg_test_support+eg2",
     "//ios/testing/earl_grey:eg_test_support+eg2",
     "//ios/third_party/earl_grey2:test_lib",
   ]
diff --git a/ios/chrome/browser/policy/policy_app_interface_stub.mm b/ios/chrome/browser/policy/policy_app_interface_stub.mm
index 103e9bf..9e5758cf 100644
--- a/ios/chrome/browser/policy/policy_app_interface_stub.mm
+++ b/ios/chrome/browser/policy/policy_app_interface_stub.mm
@@ -3,7 +3,8 @@
 // found in the LICENSE file.
 
 #import "ios/chrome/browser/policy/policy_app_interface.h"
-#import "ios/testing/earl_grey/earl_grey_test.h"
+
+#import <TestLib/EarlGreyImpl/EarlGrey.h>
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
diff --git a/ios/chrome/browser/prefs/browser_prefs.mm b/ios/chrome/browser/prefs/browser_prefs.mm
index 8003075..7316336 100644
--- a/ios/chrome/browser/prefs/browser_prefs.mm
+++ b/ios/chrome/browser/prefs/browser_prefs.mm
@@ -269,6 +269,15 @@
   registry->RegisterBooleanPref(
       prefs::kTrackPricesOnTabsEnabled, true,
       user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
+  registry->RegisterBooleanPref(
+      prefs::kNTPContentSuggestionsEnabled, true,
+      user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
+  registry->RegisterBooleanPref(
+      prefs::kArticlesForYouEnabled, true,
+      user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
+  registry->RegisterBooleanPref(
+      prefs::kNTPContentSuggestionsForSupervisedUserEnabled, true,
+      user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
 
   registry->RegisterStringPref(prefs::kDefaultCharset,
                                l10n_util::GetStringUTF8(IDS_DEFAULT_ENCODING),
diff --git a/ios/chrome/browser/tabs/tab_helper_util.mm b/ios/chrome/browser/tabs/tab_helper_util.mm
index 97c96c8..f8438d22 100644
--- a/ios/chrome/browser/tabs/tab_helper_util.mm
+++ b/ios/chrome/browser/tabs/tab_helper_util.mm
@@ -40,7 +40,7 @@
 #import "ios/chrome/browser/download/safari_download_tab_helper.h"
 #import "ios/chrome/browser/download/vcard_tab_helper.h"
 #import "ios/chrome/browser/favicon/favicon_service_factory.h"
-#import "ios/chrome/browser/find_in_page/find_tab_helper.h"
+#import "ios/chrome/browser/find_in_page/java_script_find_tab_helper.h"
 #import "ios/chrome/browser/follow/follow_tab_helper.h"
 #import "ios/chrome/browser/history/history_service_factory.h"
 #import "ios/chrome/browser/history/history_tab_helper.h"
@@ -123,7 +123,7 @@
   IOSChromeSyncedTabDelegate::CreateForWebState(web_state);
   InfoBarManagerImpl::CreateForWebState(web_state);
   BlockedPopupTabHelper::CreateForWebState(web_state);
-  FindTabHelper::CreateForWebState(web_state);
+  JavaScriptFindTabHelper::CreateForWebState(web_state);
   ITunesUrlsHandlerTabHelper::CreateForWebState(web_state);
   HistoryTabHelper::CreateForWebState(web_state);
   LoadTimingTabHelper::CreateForWebState(web_state);
diff --git a/ios/chrome/browser/ui/browser_view/BUILD.gn b/ios/chrome/browser/ui/browser_view/BUILD.gn
index dc598d0..0aa2a9c 100644
--- a/ios/chrome/browser/ui/browser_view/BUILD.gn
+++ b/ios/chrome/browser/ui/browser_view/BUILD.gn
@@ -144,6 +144,7 @@
     "//ios/chrome/browser/ui/ntp:coordinator",
     "//ios/chrome/browser/ui/ntp:logo",
     "//ios/chrome/browser/ui/ntp:util",
+    "//ios/chrome/browser/ui/omnibox",
     "//ios/chrome/browser/ui/omnibox:omnibox_internal",
     "//ios/chrome/browser/ui/omnibox/popup",
     "//ios/chrome/browser/ui/open_in",
diff --git a/ios/chrome/browser/ui/browser_view/DEPS b/ios/chrome/browser/ui/browser_view/DEPS
index f92d39e..c2df8930 100644
--- a/ios/chrome/browser/ui/browser_view/DEPS
+++ b/ios/chrome/browser/ui/browser_view/DEPS
@@ -4,6 +4,6 @@
 
 specific_include_rules = {
   "key_commands_provider_unittest\.mm": [
-    "+ios/web/find_in_page/find_in_page_manager_impl.h",
+    "+ios/web/find_in_page/java_script_find_in_page_manager_impl.h",
   ],
 }
diff --git a/ios/chrome/browser/ui/browser_view/browser_coordinator.mm b/ios/chrome/browser/ui/browser_view/browser_coordinator.mm
index 8f5d3d6..8ebaf91 100644
--- a/ios/chrome/browser/ui/browser_view/browser_coordinator.mm
+++ b/ios/chrome/browser/ui/browser_view/browser_coordinator.mm
@@ -28,7 +28,7 @@
 #import "ios/chrome/browser/download/pass_kit_tab_helper.h"
 #import "ios/chrome/browser/feature_engagement/tracker_factory.h"
 #import "ios/chrome/browser/feature_engagement/tracker_util.h"
-#import "ios/chrome/browser/find_in_page/find_tab_helper.h"
+#import "ios/chrome/browser/find_in_page/java_script_find_tab_helper.h"
 #import "ios/chrome/browser/follow/follow_browser_agent.h"
 #import "ios/chrome/browser/follow/follow_tab_helper.h"
 #import "ios/chrome/browser/follow/followed_web_site.h"
@@ -1536,9 +1536,10 @@
       self.browser->GetWebStateList()->GetActiveWebState();
 
   if (currentWebState) {
-    FindTabHelper* findTabHelper = FindTabHelper::FromWebState(currentWebState);
-    if (findTabHelper->IsFindUIActive()) {
-      findTabHelper->StopFinding();
+    JavaScriptFindTabHelper* helper =
+        JavaScriptFindTabHelper::FromWebState(currentWebState);
+    if (helper->IsFindUIActive()) {
+      helper->StopFinding();
     } else {
       [self.findBarCoordinator stop];
       self.findBarCoordinator = nil;
@@ -1549,7 +1550,7 @@
 - (void)showFindUIIfActive {
   web::WebState* currentWebState =
       self.browser->GetWebStateList()->GetActiveWebState();
-  auto* findHelper = FindTabHelper::FromWebState(currentWebState);
+  auto* findHelper = JavaScriptFindTabHelper::FromWebState(currentWebState);
   if (findHelper && findHelper->IsFindUIActive() &&
       !_toolbarAccessoryPresenter.isPresenting) {
     DCHECK(!self.findBarCoordinator);
@@ -1571,7 +1572,8 @@
   web::WebState* currentWebState =
       self.browser->GetWebStateList()->GetActiveWebState();
   DCHECK(currentWebState);
-  FindTabHelper* helper = FindTabHelper::FromWebState(currentWebState);
+  JavaScriptFindTabHelper* helper =
+      JavaScriptFindTabHelper::FromWebState(currentWebState);
   helper->StartFinding([self.findBarCoordinator.findBarController searchTerm]);
 
   if (!self.browser->GetBrowserState()->IsOffTheRecord())
@@ -1583,8 +1585,8 @@
       self.browser->GetWebStateList()->GetActiveWebState();
   DCHECK(currentWebState);
   // TODO(crbug.com/603524): Reshow find bar if necessary.
-  FindTabHelper::FromWebState(currentWebState)
-      ->ContinueFinding(FindTabHelper::FORWARD);
+  JavaScriptFindTabHelper::FromWebState(currentWebState)
+      ->ContinueFinding(JavaScriptFindTabHelper::FORWARD);
 }
 
 - (void)findPreviousStringInPage {
@@ -1592,8 +1594,8 @@
       self.browser->GetWebStateList()->GetActiveWebState();
   DCHECK(currentWebState);
   // TODO(crbug.com/603524): Reshow find bar if necessary.
-  FindTabHelper::FromWebState(currentWebState)
-      ->ContinueFinding(FindTabHelper::REVERSE);
+  JavaScriptFindTabHelper::FromWebState(currentWebState)
+      ->ContinueFinding(JavaScriptFindTabHelper::REVERSE);
 }
 
 #pragma mark - FindInPageCommands Helpers
@@ -1605,7 +1607,7 @@
     return NO;
   }
 
-  auto* helper = FindTabHelper::FromWebState(currentWebState);
+  auto* helper = JavaScriptFindTabHelper::FromWebState(currentWebState);
   return (helper && helper->CurrentPageSupportsFindInPage() &&
           !helper->IsFindUIActive());
 }
diff --git a/ios/chrome/browser/ui/browser_view/browser_view_controller.h b/ios/chrome/browser/ui/browser_view/browser_view_controller.h
index 6ba66bf..7ad6ea3 100644
--- a/ios/chrome/browser/ui/browser_view/browser_view_controller.h
+++ b/ios/chrome/browser/ui/browser_view/browser_view_controller.h
@@ -15,11 +15,11 @@
 #import "ios/chrome/browser/ui/find_bar/find_bar_coordinator.h"
 #import "ios/chrome/browser/ui/incognito_reauth/incognito_reauth_consumer.h"
 #import "ios/chrome/browser/ui/ntp/logo_animation_controller.h"
+#import "ios/chrome/browser/ui/omnibox/omnibox_focus_delegate.h"
 #import "ios/chrome/browser/ui/omnibox/popup/omnibox_popup_presenter.h"
 #import "ios/chrome/browser/ui/page_info/requirements/page_info_presentation.h"
 #import "ios/chrome/browser/ui/settings/sync/utils/sync_presenter.h"
 #import "ios/chrome/browser/ui/thumb_strip/thumb_strip_supporting.h"
-#import "ios/chrome/browser/ui/toolbar/toolbar_coordinator_delegate.h"
 #import "ios/chrome/browser/web/web_navigation_ntp_delegate.h"
 #import "ios/chrome/browser/web/web_state_container_view_provider.h"
 
@@ -84,9 +84,9 @@
     : UIViewController <FindBarPresentationDelegate,
                         IncognitoReauthConsumer,
                         LogoAnimationControllerOwnerOwner,
+                        OmniboxFocusDelegate,
                         OmniboxPopupPresenterDelegate,
                         ThumbStripSupporting,
-                        ToolbarCoordinatorDelegate,
                         WebStateContainerViewProvider,
                         BrowserCommands>
 
diff --git a/ios/chrome/browser/ui/browser_view/browser_view_controller.mm b/ios/chrome/browser/ui/browser_view/browser_view_controller.mm
index 4d9489d3..9249231e 100644
--- a/ios/chrome/browser/ui/browser_view/browser_view_controller.mm
+++ b/ios/chrome/browser/ui/browser_view/browser_view_controller.mm
@@ -3056,9 +3056,9 @@
   return _mainContentUIUpdater.state;
 }
 
-#pragma mark - ToolbarCoordinatorDelegate (Public)
+#pragma mark - OmniboxFocusDelegate (Public)
 
-- (void)locationBarDidBecomeFirstResponder {
+- (void)omniboxDidBecomeFirstResponder {
   if (self.isNTPActiveForCurrentWebState) {
     [self.ntpCoordinator locationBarDidBecomeFirstResponder];
   }
@@ -3079,7 +3079,7 @@
   [self.primaryToolbarCoordinator transitionToLocationBarFocusedState:YES];
 }
 
-- (void)locationBarDidResignFirstResponder {
+- (void)omniboxDidResignFirstResponder {
   [_sideSwipeController setEnabled:YES];
 
   [self.ntpCoordinator locationBarDidResignFirstResponder];
diff --git a/ios/chrome/browser/ui/browser_view/key_commands_provider.mm b/ios/chrome/browser/ui/browser_view/key_commands_provider.mm
index 9744cbb9..5f046281 100644
--- a/ios/chrome/browser/ui/browser_view/key_commands_provider.mm
+++ b/ios/chrome/browser/ui/browser_view/key_commands_provider.mm
@@ -13,7 +13,7 @@
 #import "components/strings/grit/components_strings.h"
 #import "ios/chrome/browser/bookmarks/bookmark_model_factory.h"
 #import "ios/chrome/browser/browser_state/chrome_browser_state.h"
-#import "ios/chrome/browser/find_in_page/find_tab_helper.h"
+#import "ios/chrome/browser/find_in_page/java_script_find_tab_helper.h"
 #import "ios/chrome/browser/main/browser.h"
 #import "ios/chrome/browser/sessions/ios_chrome_tab_restore_service_factory.h"
 #import "ios/chrome/browser/tabs/tab_title_util.h"
@@ -542,7 +542,8 @@
     return NO;
   }
 
-  FindTabHelper* helper = FindTabHelper::FromWebState(currentWebState);
+  JavaScriptFindTabHelper* helper =
+      JavaScriptFindTabHelper::FromWebState(currentWebState);
   return (helper && helper->CurrentPageSupportsFindInPage());
 }
 
@@ -553,7 +554,8 @@
     return NO;
   }
 
-  FindTabHelper* helper = FindTabHelper::FromWebState(currentWebState);
+  JavaScriptFindTabHelper* helper =
+      JavaScriptFindTabHelper::FromWebState(currentWebState);
   return (helper && helper->IsFindUIActive());
 }
 
diff --git a/ios/chrome/browser/ui/browser_view/key_commands_provider_unittest.mm b/ios/chrome/browser/ui/browser_view/key_commands_provider_unittest.mm
index 0d077fd..3c6fe13 100644
--- a/ios/chrome/browser/ui/browser_view/key_commands_provider_unittest.mm
+++ b/ios/chrome/browser/ui/browser_view/key_commands_provider_unittest.mm
@@ -12,7 +12,7 @@
 #import "components/bookmarks/test/bookmark_test_helpers.h"
 #import "ios/chrome/browser/bookmarks/bookmark_model_factory.h"
 #import "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
-#import "ios/chrome/browser/find_in_page/find_tab_helper.h"
+#import "ios/chrome/browser/find_in_page/java_script_find_tab_helper.h"
 #import "ios/chrome/browser/lens/lens_browser_agent.h"
 #import "ios/chrome/browser/main/test_browser.h"
 #import "ios/chrome/browser/ntp/new_tab_page_tab_helper.h"
@@ -36,7 +36,7 @@
 #import "ios/chrome/grit/ios_strings.h"
 #import "ios/public/provider/chrome/browser/user_feedback/user_feedback_api.h"
 #import "ios/web/common/uikit_ui_util.h"
-#import "ios/web/find_in_page/find_in_page_manager_impl.h"
+#import "ios/web/find_in_page/java_script_find_in_page_manager_impl.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"
@@ -279,8 +279,8 @@
 
   // Open a tab.
   web::FakeWebState* web_state = InsertNewWebState(0);
-  web::FindInPageManagerImpl::CreateForWebState(web_state);
-  FindTabHelper::CreateForWebState(web_state);
+  web::JavaScriptFindInPageManagerImpl::CreateForWebState(web_state);
+  JavaScriptFindTabHelper::CreateForWebState(web_state);
 
   // No Find in Page.
   web_state->SetContentIsHTML(false);
@@ -293,7 +293,8 @@
   EXPECT_FALSE(CanPerform(@"keyCommand_findPrevious"));
 
   // Find UI active.
-  FindTabHelper* helper = FindTabHelper::FromWebState(web_state);
+  JavaScriptFindTabHelper* helper =
+      JavaScriptFindTabHelper::FromWebState(web_state);
   helper->SetFindUIActive(YES);
   EXPECT_TRUE(CanPerform(@"keyCommand_findNext"));
   EXPECT_TRUE(CanPerform(@"keyCommand_findPrevious"));
@@ -844,8 +845,8 @@
 TEST_F(KeyCommandsProviderTest, ValidateCommands) {
   // Open a tab.
   web::FakeWebState* web_state = InsertNewWebState(0);
-  web::FindInPageManagerImpl::CreateForWebState(web_state);
-  FindTabHelper::CreateForWebState(web_state);
+  web::JavaScriptFindInPageManagerImpl::CreateForWebState(web_state);
+  JavaScriptFindTabHelper::CreateForWebState(web_state);
 
   // Can Find in Page.
   web_state->SetContentIsHTML(true);
diff --git a/ios/chrome/browser/ui/content_suggestions/BUILD.gn b/ios/chrome/browser/ui/content_suggestions/BUILD.gn
index fb0d8df..a6412aa 100644
--- a/ios/chrome/browser/ui/content_suggestions/BUILD.gn
+++ b/ios/chrome/browser/ui/content_suggestions/BUILD.gn
@@ -81,7 +81,6 @@
     "//ios/chrome/browser/metrics:metrics_internal",
     "//ios/chrome/browser/ntp",
     "//ios/chrome/browser/ntp:features",
-    "//ios/chrome/browser/ntp_snippets",
     "//ios/chrome/browser/ntp_tiles",
     "//ios/chrome/browser/policy:policy_util",
     "//ios/chrome/browser/prefs:pref_names",
@@ -304,7 +303,6 @@
     "//ios/chrome/browser/main:public",
     "//ios/chrome/browser/main:test_support",
     "//ios/chrome/browser/ntp",
-    "//ios/chrome/browser/ntp_snippets",
     "//ios/chrome/browser/reading_list",
     "//ios/chrome/browser/reading_list:test_support",
     "//ios/chrome/browser/search_engines",
@@ -438,7 +436,6 @@
     "//ios/chrome/browser/application_context",
     "//ios/chrome/browser/browser_state",
     "//ios/chrome/browser/flags:system_flags",
-    "//ios/chrome/browser/ntp_snippets",
     "//ios/chrome/browser/search_engines",
     "//ios/chrome/browser/ui/ntp:constants",
     "//ios/chrome/browser/ui/util",
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_egtest.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_egtest.mm
index 9c6a5c4..d485d5455 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_egtest.mm
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_egtest.mm
@@ -82,25 +82,19 @@
 
 + (void)setUpHelper {
   [self closeAllTabs];
-
-  [NewTabPageAppInterface setUpService];
 }
 
 + (void)tearDown {
   [self closeAllTabs];
 
-  [NewTabPageAppInterface resetService];
-
   [super tearDown];
 }
 
 - (void)setUp {
   [super setUp];
-  [NewTabPageAppInterface makeSuggestionsAvailable];
 }
 
 - (void)tearDown {
-  [NewTabPageAppInterface disableSuggestions];
   [ChromeEarlGrey clearBrowsingHistory];
   [super tearDown];
 }
diff --git a/ios/chrome/browser/ui/content_suggestions/new_tab_page_app_interface.h b/ios/chrome/browser/ui/content_suggestions/new_tab_page_app_interface.h
index 0c93e8b..ca7c12b 100644
--- a/ios/chrome/browser/ui/content_suggestions/new_tab_page_app_interface.h
+++ b/ios/chrome/browser/ui/content_suggestions/new_tab_page_app_interface.h
@@ -8,32 +8,8 @@
 #import <UIKit/UIKit.h>
 
 // App interface for the NTP.
-// TODO(crbug.com/1299373): Separate content suggestions functions into
-// ContentSuggestionsAppInterface interface and move this to /ui/ntp.
 @interface NewTabPageAppInterface : NSObject
 
-// Sets the fake service up.
-+ (void)setUpService;
-
-// Resets the service to the real, non-fake service to avoid leaking the fake.
-+ (void)resetService;
-
-// Marks the suggestions as available.
-+ (void)makeSuggestionsAvailable;
-
-// Disables the suggestions.
-+ (void)disableSuggestions;
-
-// Adds `numberOfSuggestions` suggestions to the list of suggestions provided.
-// The suggestions have the name "chromium<suggestionNumber>" and the url
-// http://chromium/<suggestionNumber>.
-+ (void)addNumberOfSuggestions:(NSInteger)numberOfSuggestions
-      additionalSuggestionsURL:(NSURL*)URL;
-
-// Add one particular suggestion, following the convention explained above, with
-// `suggestionNumber`.
-+ (void)addSuggestionNumber:(NSInteger)suggestionNumber;
-
 // Returns the short name of the default search engine.
 + (NSString*)defaultSearchEngine;
 
diff --git a/ios/chrome/browser/ui/content_suggestions/new_tab_page_app_interface.mm b/ios/chrome/browser/ui/content_suggestions/new_tab_page_app_interface.mm
index 207b736..08b3cd2c 100644
--- a/ios/chrome/browser/ui/content_suggestions/new_tab_page_app_interface.mm
+++ b/ios/chrome/browser/ui/content_suggestions/new_tab_page_app_interface.mm
@@ -8,16 +8,11 @@
 #import "base/strings/sys_string_conversions.h"
 #import "base/strings/utf_string_conversions.h"
 #import "components/keyed_service/ios/browser_state_keyed_service_factory.h"
-#import "components/ntp_snippets/content_suggestion.h"
-#import "components/ntp_snippets/content_suggestions_service.h"
-#import "components/ntp_snippets/mock_content_suggestions_provider.h"
 #import "components/search_engines/template_url.h"
 #import "components/search_engines/template_url_service.h"
 #import "ios/chrome/browser/application_context/application_context.h"
 #import "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #import "ios/chrome/browser/flags/system_flags.h"
-#import "ios/chrome/browser/ntp_snippets/ios_chrome_content_suggestions_service_factory.h"
-#import "ios/chrome/browser/ntp_snippets/ios_chrome_content_suggestions_service_factory_util.h"
 #import "ios/chrome/browser/search_engines/template_url_service_factory.h"
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils.h"
 #import "ios/chrome/browser/ui/content_suggestions/ntp_home_provider_test_singleton.h"
@@ -31,106 +26,12 @@
 #endif
 
 using content_suggestions::SearchFieldWidth;
-using ntp_snippets::AdditionalSuggestionsHelper;
-using ntp_snippets::Category;
-using ntp_snippets::CategoryStatus;
-using ntp_snippets::ContentSuggestion;
-using ntp_snippets::ContentSuggestionsService;
-using ntp_snippets::CreateChromeContentSuggestionsService;
-using ntp_snippets::KnownCategories;
-using ntp_snippets::MockContentSuggestionsProvider;
 using testing::_;
 using testing::Invoke;
 using testing::WithArg;
 
-namespace {
-// Returns a suggestion created from the `category`, `suggestion_id` and the
-// `url`.
-ContentSuggestion CreateSuggestion(Category category,
-                                   std::string suggestion_id,
-                                   GURL url) {
-  ContentSuggestion suggestion(category, suggestion_id, url);
-  suggestion.set_title(base::UTF8ToUTF16(url.spec()));
-
-  return suggestion;
-}
-
-}  // namespace
-
 @implementation NewTabPageAppInterface
 
-+ (void)setUpService {
-  ChromeBrowserState* browserState =
-      chrome_test_util::GetOriginalBrowserState();
-  // Sets the ContentSuggestionsService associated with this browserState to a
-  // service with no provider registered, allowing to register fake providers
-  // which do not require internet connection. The previous service is deleted.
-  IOSChromeContentSuggestionsServiceFactory::GetInstance()->SetTestingFactory(
-      browserState,
-      base::BindRepeating(&CreateChromeContentSuggestionsService));
-
-  ContentSuggestionsService* service =
-      IOSChromeContentSuggestionsServiceFactory::GetForBrowserState(
-          browserState);
-  [[ContentSuggestionsTestSingleton sharedInstance]
-      registerArticleProvider:service];
-}
-
-+ (void)resetService {
-  ChromeBrowserState* browserState =
-      chrome_test_util::GetOriginalBrowserState();
-
-  // Resets the Service associated with this browserState to a new service with
-  // no providers. The previous service is deleted.
-  IOSChromeContentSuggestionsServiceFactory::GetInstance()->SetTestingFactory(
-      browserState,
-      base::BindRepeating(&CreateChromeContentSuggestionsService));
-}
-
-+ (void)makeSuggestionsAvailable {
-  [self provider]->FireCategoryStatusChanged([self category],
-                                             CategoryStatus::AVAILABLE);
-}
-
-+ (void)disableSuggestions {
-  [self provider]->FireCategoryStatusChanged(
-      [self category], CategoryStatus::ALL_SUGGESTIONS_EXPLICITLY_DISABLED);
-}
-
-+ (void)addNumberOfSuggestions:(NSInteger)numberOfSuggestions
-      additionalSuggestionsURL:(NSURL*)URL {
-  GURL newURL = net::GURLWithNSURL(URL);
-  std::vector<ContentSuggestion> suggestions;
-  for (NSInteger i = 1; i <= numberOfSuggestions; i++) {
-    std::string index = base::SysNSStringToUTF8(@(i).stringValue);
-    suggestions.push_back(
-        CreateSuggestion([self category], "chromium" + index,
-                         GURL("http://chromium.org/" + index)));
-  }
-  [self provider]->FireSuggestionsChanged([self category],
-                                          std::move(suggestions));
-
-  if (URL) {
-    // Set up the action when "More" is tapped.
-    [[ContentSuggestionsTestSingleton sharedInstance]
-        resetAdditionalSuggestionsHelperWithURL:newURL];
-    EXPECT_CALL(*[self provider], FetchMock(_, _, _))
-        .WillRepeatedly(WithArg<2>(
-            Invoke([[ContentSuggestionsTestSingleton sharedInstance]
-                       additionalSuggestionsHelper],
-                   &AdditionalSuggestionsHelper::SendAdditionalSuggestions)));
-  }
-}
-
-+ (void)addSuggestionNumber:(NSInteger)suggestionNumber {
-  std::string index = base::NumberToString(suggestionNumber);
-  std::vector<ContentSuggestion> suggestions;
-  suggestions.push_back(CreateSuggestion([self category], "chromium" + index,
-                                         GURL("http://chromium.org/" + index)));
-  [self provider]->FireSuggestionsChanged([self category],
-                                          std::move(suggestions));
-}
-
 + (NSString*)defaultSearchEngine {
   // Get the default Search Engine.
   ChromeBrowserState* browser_state =
@@ -183,14 +84,4 @@
   return ntp_home::DiscoverHeaderLabel();
 }
 
-#pragma mark - Helper
-
-+ (MockContentSuggestionsProvider*)provider {
-  return [[ContentSuggestionsTestSingleton sharedInstance] provider];
-}
-
-+ (Category)category {
-  return Category::FromKnownCategory(KnownCategories::ARTICLES);
-}
-
 @end
diff --git a/ios/chrome/browser/ui/content_suggestions/ntp_home_egtest.mm b/ios/chrome/browser/ui/content_suggestions/ntp_home_egtest.mm
index bf0c76ba2..938e8c9c 100644
--- a/ios/chrome/browser/ui/content_suggestions/ntp_home_egtest.mm
+++ b/ios/chrome/browser/ui/content_suggestions/ntp_home_egtest.mm
@@ -148,8 +148,6 @@
 
 - (void)setUp {
   [super setUp];
-  [NewTabPageAppInterface setUpService];
-  [NewTabPageAppInterface makeSuggestionsAvailable];
   [ChromeEarlGreyAppInterface
       setBoolValue:YES
        forUserPref:base::SysUTF8ToNSString(prefs::kArticlesForYouEnabled)];
@@ -521,10 +519,6 @@
 - (void)DISABLED_testPositionRestored {
   [self addMostVisitedTile];
 
-  // Add suggestions to be able to scroll on iPad.
-  [NewTabPageAppInterface addNumberOfSuggestions:15
-                        additionalSuggestionsURL:nil];
-
   // Scroll to have a position to restored.
   [[EarlGrey selectElementWithMatcher:chrome_test_util::NTPCollectionView()]
       performAction:grey_scrollToContentEdge(kGREYContentEdgeTop)];
@@ -552,11 +546,6 @@
 // is selected.
 - (void)testPositionRestoredWithShiftingOffset {
   [self addMostVisitedTile];
-
-  // Add suggestions to be able to scroll on iPad.
-  [NewTabPageAppInterface addNumberOfSuggestions:15
-                        additionalSuggestionsURL:nil];
-
   // Scroll to have a position to restored.
   [[EarlGrey selectElementWithMatcher:chrome_test_util::NTPCollectionView()]
       performAction:grey_scrollInDirection(kGREYDirectionDown, 50)];
@@ -590,10 +579,6 @@
 - (void)DISABLED_testPositionRestoredWithoutShiftingOffset {
   [self addMostVisitedTile];
 
-  // Add suggestions to be able to scroll on iPad.
-  [NewTabPageAppInterface addNumberOfSuggestions:15
-                        additionalSuggestionsURL:nil];
-
   // Scroll enough to naturally pin the omnibox to the top.
   [[EarlGrey selectElementWithMatcher:chrome_test_util::NTPCollectionView()]
       performAction:grey_scrollToContentEdge(kGREYContentEdgeTop)];
diff --git a/ios/chrome/browser/ui/find_bar/BUILD.gn b/ios/chrome/browser/ui/find_bar/BUILD.gn
index 73f33a08c..95880bb 100644
--- a/ios/chrome/browser/ui/find_bar/BUILD.gn
+++ b/ios/chrome/browser/ui/find_bar/BUILD.gn
@@ -87,8 +87,8 @@
   ]
   testonly = true
   sources = [
-    "find_in_page_controller_app_interface.h",
-    "find_in_page_controller_app_interface.mm",
+    "java_script_find_in_page_controller_app_interface.h",
+    "java_script_find_in_page_controller_app_interface.mm",
   ]
   deps = [ "//ios/chrome/browser/find_in_page" ]
 }
@@ -100,8 +100,8 @@
   ]
   testonly = true
   sources = [
-    "find_in_page_controller_app_interface.h",
-    "find_in_page_controller_app_interface_stub.mm",
+    "java_script_find_in_page_controller_app_interface.h",
+    "java_script_find_in_page_controller_app_interface_stub.mm",
   ]
   deps = [ "//ios/third_party/earl_grey2:test_lib" ]
 }
diff --git a/ios/chrome/browser/ui/find_bar/find_bar_controller_ios.mm b/ios/chrome/browser/ui/find_bar/find_bar_controller_ios.mm
index 6deb44a..6953263 100644
--- a/ios/chrome/browser/ui/find_bar/find_bar_controller_ios.mm
+++ b/ios/chrome/browser/ui/find_bar/find_bar_controller_ios.mm
@@ -10,7 +10,7 @@
 #import "base/mac/foundation_util.h"
 #import "base/strings/sys_string_conversions.h"
 #import "components/strings/grit/components_strings.h"
-#import "ios/chrome/browser/find_in_page/find_in_page_controller.h"
+#import "ios/chrome/browser/find_in_page/constants.h"
 #import "ios/chrome/browser/find_in_page/find_in_page_model.h"
 #import "ios/chrome/browser/ui/commands/browser_commands.h"
 #import "ios/chrome/browser/ui/commands/find_in_page_commands.h"
diff --git a/ios/chrome/browser/ui/find_bar/find_bar_coordinator.mm b/ios/chrome/browser/ui/find_bar/find_bar_coordinator.mm
index a094c4f..c195562 100644
--- a/ios/chrome/browser/ui/find_bar/find_bar_coordinator.mm
+++ b/ios/chrome/browser/ui/find_bar/find_bar_coordinator.mm
@@ -5,7 +5,7 @@
 #import "ios/chrome/browser/ui/find_bar/find_bar_coordinator.h"
 
 #import "ios/chrome/browser/browser_state/chrome_browser_state.h"
-#import "ios/chrome/browser/find_in_page/find_tab_helper.h"
+#import "ios/chrome/browser/find_in_page/java_script_find_tab_helper.h"
 #import "ios/chrome/browser/main/browser.h"
 #import "ios/chrome/browser/ui/commands/browser_commands.h"
 #import "ios/chrome/browser/ui/commands/command_dispatcher.h"
@@ -48,7 +48,8 @@
   self.mediator.consumer = self.findBarController;
 
   DCHECK(self.currentWebState);
-  FindTabHelper* helper = FindTabHelper::FromWebState(self.currentWebState);
+  JavaScriptFindTabHelper* helper =
+      JavaScriptFindTabHelper::FromWebState(self.currentWebState);
   helper->SetResponseDelegate(self.mediator);
   // If the FindUI is already active, just reshow it.
   if (helper->IsFindUIActive()) {
@@ -69,7 +70,8 @@
   // the UI will be brought back later.
   BOOL animated;
   if (self.currentWebState) {
-    FindTabHelper* helper = FindTabHelper::FromWebState(self.currentWebState);
+    JavaScriptFindTabHelper* helper =
+        JavaScriptFindTabHelper::FromWebState(self.currentWebState);
     animated = helper && !helper->IsFindUIActive();
   } else {
     animated = true;
@@ -94,7 +96,8 @@
   if (!self.currentWebState) {
     return;
   }
-  FindTabHelper* helper = FindTabHelper::FromWebState(self.currentWebState);
+  JavaScriptFindTabHelper* helper =
+      JavaScriptFindTabHelper::FromWebState(self.currentWebState);
   DCHECK(helper && helper->IsFindUIActive());
   if (!self.browser->GetBrowserState()->IsOffTheRecord()) {
     helper->RestoreSearchTerm();
@@ -106,7 +109,8 @@
 }
 
 - (void)defocusFindBar {
-  FindTabHelper* helper = FindTabHelper::FromWebState(self.currentWebState);
+  JavaScriptFindTabHelper* helper =
+      JavaScriptFindTabHelper::FromWebState(self.currentWebState);
   if (helper && helper->IsFindUIActive()) {
     [self.findBarController updateView:helper->GetFindResult()
                          initialUpdate:NO
diff --git a/ios/chrome/browser/ui/find_bar/find_in_page_controller_app_interface.h b/ios/chrome/browser/ui/find_bar/find_in_page_controller_app_interface.h
deleted file mode 100644
index 702d17db..0000000
--- a/ios/chrome/browser/ui/find_bar/find_in_page_controller_app_interface.h
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright 2019 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_UI_FIND_BAR_FIND_IN_PAGE_CONTROLLER_APP_INTERFACE_H_
-#define IOS_CHROME_BROWSER_UI_FIND_BAR_FIND_IN_PAGE_CONTROLLER_APP_INTERFACE_H_
-
-#import <Foundation/Foundation.h>
-
-// InfobarManagerAppInterface contains the app-side
-// implementation for helpers. These helpers are compiled into
-// the app binary and can be called from either app or test code.
-@interface FindInPageControllerAppInterface : NSObject
-
-// Clears the search term in FindInPageController.
-+ (void)clearSearchTerm;
-
-@end
-
-#endif  // IOS_CHROME_BROWSER_UI_FIND_BAR_FIND_IN_PAGE_CONTROLLER_APP_INTERFACE_H_
diff --git a/ios/chrome/browser/ui/find_bar/find_in_page_controller_app_interface.mm b/ios/chrome/browser/ui/find_bar/find_in_page_controller_app_interface.mm
deleted file mode 100644
index 6f3c060b..0000000
--- a/ios/chrome/browser/ui/find_bar/find_in_page_controller_app_interface.mm
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright 2019 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/chrome/browser/ui/find_bar/find_in_page_controller_app_interface.h"
-
-#import "ios/chrome/browser/find_in_page/find_in_page_controller.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-@implementation FindInPageControllerAppInterface
-
-+ (void)clearSearchTerm {
-  [FindInPageController setSearchTerm:nil];
-}
-
-@end
diff --git a/ios/chrome/browser/ui/find_bar/find_in_page_egtest.mm b/ios/chrome/browser/ui/find_bar/find_in_page_egtest.mm
index 6da080c..fd6d71f 100644
--- a/ios/chrome/browser/ui/find_bar/find_in_page_egtest.mm
+++ b/ios/chrome/browser/ui/find_bar/find_in_page_egtest.mm
@@ -9,7 +9,7 @@
 #import "base/test/ios/wait_util.h"
 #import "components/strings/grit/components_strings.h"
 #import "ios/chrome/browser/ui/find_bar/find_bar_constants.h"
-#import "ios/chrome/browser/ui/find_bar/find_in_page_controller_app_interface.h"
+#import "ios/chrome/browser/ui/find_bar/java_script_find_in_page_controller_app_interface.h"
 #import "ios/chrome/browser/ui/popup_menu/popup_menu_constants.h"
 #import "ios/chrome/browser/ui/toolbar/accessory/toolbar_accessory_constants.h"
 #import "ios/chrome/test/earl_grey/chrome_earl_grey.h"
@@ -70,7 +70,7 @@
   [super setUp];
 
   // Clear saved search term.
-  [FindInPageControllerAppInterface clearSearchTerm];
+  [JavaScriptFindInPageControllerAppInterface clearSearchTerm];
 
   // Setup find in page test URL.
   std::map<GURL, std::string> responses;
diff --git a/ios/chrome/browser/ui/find_bar/java_script_find_in_page_controller_app_interface.h b/ios/chrome/browser/ui/find_bar/java_script_find_in_page_controller_app_interface.h
new file mode 100644
index 0000000..63fc2b92
--- /dev/null
+++ b/ios/chrome/browser/ui/find_bar/java_script_find_in_page_controller_app_interface.h
@@ -0,0 +1,20 @@
+// Copyright 2019 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_UI_FIND_BAR_JAVA_SCRIPT_FIND_IN_PAGE_CONTROLLER_APP_INTERFACE_H_
+#define IOS_CHROME_BROWSER_UI_FIND_BAR_JAVA_SCRIPT_FIND_IN_PAGE_CONTROLLER_APP_INTERFACE_H_
+
+#import <Foundation/Foundation.h>
+
+// InfobarManagerAppInterface contains the app-side
+// implementation for helpers. These helpers are compiled into
+// the app binary and can be called from either app or test code.
+@interface JavaScriptFindInPageControllerAppInterface : NSObject
+
+// Clears the search term in JavaScriptFindInPageController.
++ (void)clearSearchTerm;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_FIND_BAR_JAVA_SCRIPT_FIND_IN_PAGE_CONTROLLER_APP_INTERFACE_H_
diff --git a/ios/chrome/browser/ui/find_bar/java_script_find_in_page_controller_app_interface.mm b/ios/chrome/browser/ui/find_bar/java_script_find_in_page_controller_app_interface.mm
new file mode 100644
index 0000000..a6d5dde
--- /dev/null
+++ b/ios/chrome/browser/ui/find_bar/java_script_find_in_page_controller_app_interface.mm
@@ -0,0 +1,19 @@
+// Copyright 2019 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/ui/find_bar/java_script_find_in_page_controller_app_interface.h"
+
+#import "ios/chrome/browser/find_in_page/java_script_find_in_page_controller.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+@implementation JavaScriptFindInPageControllerAppInterface
+
++ (void)clearSearchTerm {
+  [JavaScriptFindInPageController setSearchTerm:nil];
+}
+
+@end
diff --git a/ios/chrome/browser/ui/find_bar/find_in_page_controller_app_interface_stub.mm b/ios/chrome/browser/ui/find_bar/java_script_find_in_page_controller_app_interface_stub.mm
similarity index 63%
rename from ios/chrome/browser/ui/find_bar/find_in_page_controller_app_interface_stub.mm
rename to ios/chrome/browser/ui/find_bar/java_script_find_in_page_controller_app_interface_stub.mm
index 3716a64..25d6ce6 100644
--- a/ios/chrome/browser/ui/find_bar/find_in_page_controller_app_interface_stub.mm
+++ b/ios/chrome/browser/ui/find_bar/java_script_find_in_page_controller_app_interface_stub.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/chrome/browser/ui/find_bar/find_in_page_controller_app_interface.h"
+#import "ios/chrome/browser/ui/find_bar/java_script_find_in_page_controller_app_interface.h"
 
 #import <TestLib/EarlGreyImpl/EarlGrey.h>
 
@@ -10,4 +10,4 @@
 #error "This file requires ARC support."
 #endif
 
-GREY_STUB_CLASS_IN_APP_MAIN_QUEUE(FindInPageControllerAppInterface)
+GREY_STUB_CLASS_IN_APP_MAIN_QUEUE(JavaScriptFindInPageControllerAppInterface)
diff --git a/ios/chrome/browser/ui/location_bar/BUILD.gn b/ios/chrome/browser/ui/location_bar/BUILD.gn
index 7bbb4011..998e021 100644
--- a/ios/chrome/browser/ui/location_bar/BUILD.gn
+++ b/ios/chrome/browser/ui/location_bar/BUILD.gn
@@ -173,6 +173,7 @@
     "//ios/chrome/browser/ui/commands",
     "//ios/chrome/browser/ui/location_bar/test",
     "//ios/chrome/browser/ui/main:scene_state_header",
+    "//ios/chrome/browser/ui/omnibox",
     "//ios/chrome/browser/ui/toolbar",
     "//ios/chrome/browser/ui/toolbar/test",
     "//ios/chrome/browser/url_loading",
diff --git a/ios/chrome/browser/ui/location_bar/location_bar_coordinator.h b/ios/chrome/browser/ui/location_bar/location_bar_coordinator.h
index 510aeef1..50b2d0d 100644
--- a/ios/chrome/browser/ui/location_bar/location_bar_coordinator.h
+++ b/ios/chrome/browser/ui/location_bar/location_bar_coordinator.h
@@ -9,13 +9,12 @@
 
 #import "ios/chrome/browser/ui/commands/omnibox_commands.h"
 #import "ios/chrome/browser/ui/location_bar/location_bar_url_loader.h"
-#import "ios/chrome/browser/ui/omnibox/location_bar_delegate.h"
 
 @protocol BrowserCoordinatorCommands;
 @protocol EditViewAnimatee;
 @protocol LocationBarAnimatee;
 @protocol OmniboxPopupPresenterDelegate;
-@protocol ToolbarCoordinatorDelegate;
+@protocol OmniboxFocusDelegate;
 
 // Location bar coordinator.
 @interface LocationBarCoordinator
@@ -26,7 +25,7 @@
     UIViewController* locationBarViewController;
 // Delegate for this coordinator.
 // TODO(crbug.com/799446): Change this.
-@property(nonatomic, weak) id<ToolbarCoordinatorDelegate> delegate;
+@property(nonatomic, weak) id<OmniboxFocusDelegate> delegate;
 
 @property(nonatomic, weak) id<OmniboxPopupPresenterDelegate>
     popupPresenterDelegate;
diff --git a/ios/chrome/browser/ui/location_bar/location_bar_coordinator.mm b/ios/chrome/browser/ui/location_bar/location_bar_coordinator.mm
index 5dbb5b0c..024f8ba 100644
--- a/ios/chrome/browser/ui/location_bar/location_bar_coordinator.mm
+++ b/ios/chrome/browser/ui/location_bar/location_bar_coordinator.mm
@@ -52,12 +52,12 @@
 #import "ios/chrome/browser/ui/main/layout_guide_util.h"
 #import "ios/chrome/browser/ui/main/scene_state_browser_agent.h"
 #import "ios/chrome/browser/ui/ntp/new_tab_page_util.h"
-#import "ios/chrome/browser/ui/omnibox/location_bar_delegate.h"
+#import "ios/chrome/browser/ui/omnibox/omnibox_controller_delegate.h"
 #import "ios/chrome/browser/ui/omnibox/omnibox_coordinator.h"
+#import "ios/chrome/browser/ui/omnibox/omnibox_focus_delegate.h"
 #import "ios/chrome/browser/ui/omnibox/omnibox_text_field_ios.h"
 #import "ios/chrome/browser/ui/omnibox/popup/omnibox_popup_coordinator.h"
 #import "ios/chrome/browser/ui/omnibox/web_omnibox_edit_controller_impl.h"
-#import "ios/chrome/browser/ui/toolbar/toolbar_coordinator_delegate.h"
 #import "ios/chrome/browser/ui/util/pasteboard_util.h"
 #import "ios/chrome/browser/url_loading/image_search_param_generator.h"
 #import "ios/chrome/browser/url_loading/url_loading_browser_agent.h"
@@ -83,10 +83,10 @@
 }  // namespace
 
 @interface LocationBarCoordinator () <LoadQueryCommands,
-                                      LocationBarDelegate,
                                       LocationBarViewControllerDelegate,
                                       LocationBarConsumer,
                                       LocationBarSteadyViewConsumer,
+                                      OmniboxControllerDelegate,
                                       URLDragDataSource> {
   // API endpoint for omnibox.
   std::unique_ptr<WebOmniboxEditControllerImpl> _editController;
@@ -172,7 +172,8 @@
   self.viewController.layoutGuideCenter =
       LayoutGuideCenterForBrowser(self.browser);
 
-  _editController = std::make_unique<WebOmniboxEditControllerImpl>(self);
+  _editController =
+      std::make_unique<WebOmniboxEditControllerImpl>(self, self.delegate);
   _editController->SetURLLoader(self);
 
   self.omniboxCoordinator =
@@ -376,15 +377,7 @@
   self.isCancellingOmniboxEdit = NO;
 }
 
-#pragma mark - LocationBarDelegate
-
-- (void)locationBarHasBecomeFirstResponder {
-  [self.delegate locationBarDidBecomeFirstResponder];
-}
-
-- (void)locationBarHasResignedFirstResponder {
-  [self.delegate locationBarDidResignFirstResponder];
-}
+#pragma mark - OmniboxControllerDelegate
 
 - (web::WebState*)webState {
   return self.webStateList->GetActiveWebState();
@@ -394,10 +387,6 @@
   return _locationBarModel.get();
 }
 
-- (void)locationBarRequestScribbleTargetFocus {
-  [self.omniboxCoordinator focusOmniboxForScribble];
-}
-
 #pragma mark - LocationBarViewControllerDelegate
 
 - (void)locationBarSteadyViewTapped {
@@ -408,6 +397,10 @@
   StoreURLInPasteboard(self.webState->GetVisibleURL());
 }
 
+- (void)locationBarRequestScribbleTargetFocus {
+  [self.omniboxCoordinator focusOmniboxForScribble];
+}
+
 - (void)recordShareButtonPressed {
   if (!self.browserState) {
     return;
diff --git a/ios/chrome/browser/ui/location_bar/location_bar_coordinator_unittest.mm b/ios/chrome/browser/ui/location_bar/location_bar_coordinator_unittest.mm
index 47c6704..db67556 100644
--- a/ios/chrome/browser/ui/location_bar/location_bar_coordinator_unittest.mm
+++ b/ios/chrome/browser/ui/location_bar/location_bar_coordinator_unittest.mm
@@ -25,7 +25,7 @@
 #import "ios/chrome/browser/ui/commands/qr_scanner_commands.h"
 #import "ios/chrome/browser/ui/main/scene_state.h"
 #import "ios/chrome/browser/ui/main/scene_state_browser_agent.h"
-#import "ios/chrome/browser/ui/toolbar/toolbar_coordinator_delegate.h"
+#import "ios/chrome/browser/ui/omnibox/omnibox_focus_delegate.h"
 #import "ios/chrome/browser/url_loading/fake_url_loading_browser_agent.h"
 #import "ios/chrome/browser/url_loading/url_loading_notifier_browser_agent.h"
 #import "ios/chrome/browser/url_loading/url_loading_params.h"
@@ -43,17 +43,17 @@
 
 using variations::VariationsIdsProvider;
 
-@interface TestToolbarCoordinatorDelegate : NSObject<ToolbarCoordinatorDelegate>
+@interface TestOmniboxFocusDelegate : NSObject <OmniboxFocusDelegate>
 
 @end
 
-@implementation TestToolbarCoordinatorDelegate {
+@implementation TestOmniboxFocusDelegate {
   std::unique_ptr<LocationBarModel> _model;
 }
 
-- (void)locationBarDidBecomeFirstResponder {
+- (void)omniboxDidBecomeFirstResponder {
 }
-- (void)locationBarDidResignFirstResponder {
+- (void)omniboxDidResignFirstResponder {
 }
 
 - (LocationBarModel*)locationBarModel {
@@ -135,7 +135,7 @@
         startDispatchingToTarget:mockApplicationSettingsCommandHandler
                      forProtocol:@protocol(ApplicationSettingsCommands)];
 
-    delegate_ = [[TestToolbarCoordinatorDelegate alloc] init];
+    delegate_ = [[TestOmniboxFocusDelegate alloc] init];
 
     coordinator_ = [[LocationBarCoordinator alloc]
         initWithBaseViewController:nil
@@ -157,7 +157,7 @@
   std::unique_ptr<TestChromeBrowserState> browser_state_;
   std::unique_ptr<Browser> browser_;
   SceneState* scene_state_;
-  TestToolbarCoordinatorDelegate* delegate_;
+  TestOmniboxFocusDelegate* delegate_;
 };
 
 TEST_F(LocationBarCoordinatorTest, Stops) {
diff --git a/ios/chrome/browser/ui/location_bar/location_bar_generic_coordinator.h b/ios/chrome/browser/ui/location_bar/location_bar_generic_coordinator.h
index d12bdc5..ffdcba30 100644
--- a/ios/chrome/browser/ui/location_bar/location_bar_generic_coordinator.h
+++ b/ios/chrome/browser/ui/location_bar/location_bar_generic_coordinator.h
@@ -9,7 +9,6 @@
 
 #import "ios/chrome/browser/ui/commands/omnibox_commands.h"
 #import "ios/chrome/browser/ui/location_bar/location_bar_url_loader.h"
-#import "ios/chrome/browser/ui/omnibox/location_bar_delegate.h"
 
 class ChromeBrowserState;
 class WebStateList;
@@ -19,7 +18,7 @@
 @protocol EditViewAnimatee;
 @protocol LocationBarAnimatee;
 @protocol OmniboxPopupPresenterDelegate;
-@protocol ToolbarCoordinatorDelegate;
+@protocol OmniboxFocusDelegate;
 
 @protocol LocationBarGenericCoordinator <NSObject,
                                          LocationBarURLLoader,
@@ -35,7 +34,7 @@
 @property(nonatomic, weak) CommandDispatcher* dispatcher;
 // Delegate for this coordinator.
 // TODO(crbug.com/799446): Change this.
-@property(nonatomic, weak) id<ToolbarCoordinatorDelegate> delegate;
+@property(nonatomic, weak) id<OmniboxFocusDelegate> delegate;
 // The web state list this ToolbarCoordinator is handling.
 @property(nonatomic, assign) WebStateList* webStateList;
 
diff --git a/ios/chrome/browser/ui/ntp/BUILD.gn b/ios/chrome/browser/ui/ntp/BUILD.gn
index c56a54a..d43610e 100644
--- a/ios/chrome/browser/ui/ntp/BUILD.gn
+++ b/ios/chrome/browser/ui/ntp/BUILD.gn
@@ -211,7 +211,6 @@
     "//ios/chrome/browser/metrics:metrics_internal",
     "//ios/chrome/browser/ntp",
     "//ios/chrome/browser/ntp:features",
-    "//ios/chrome/browser/ntp_snippets",
     "//ios/chrome/browser/ntp_tiles",
     "//ios/chrome/browser/policy:policy_util",
     "//ios/chrome/browser/reading_list",
@@ -296,7 +295,6 @@
     "//ios/chrome/browser/favicon",
     "//ios/chrome/browser/main:test_support",
     "//ios/chrome/browser/ntp",
-    "//ios/chrome/browser/ntp_snippets",
     "//ios/chrome/browser/search_engines",
     "//ios/chrome/browser/sessions",
     "//ios/chrome/browser/sessions:test_support",
diff --git a/ios/chrome/browser/ui/ntp/new_tab_page_coordinator_unittest.mm b/ios/chrome/browser/ui/ntp/new_tab_page_coordinator_unittest.mm
index 7abda1f..7b64858 100644
--- a/ios/chrome/browser/ui/ntp/new_tab_page_coordinator_unittest.mm
+++ b/ios/chrome/browser/ui/ntp/new_tab_page_coordinator_unittest.mm
@@ -10,7 +10,6 @@
 #import "ios/chrome/browser/favicon/ios_chrome_large_icon_service_factory.h"
 #import "ios/chrome/browser/main/test_browser.h"
 #import "ios/chrome/browser/ntp/new_tab_page_tab_helper.h"
-#import "ios/chrome/browser/ntp_snippets/ios_chrome_content_suggestions_service_factory.h"
 #import "ios/chrome/browser/search_engines/template_url_service_factory.h"
 #import "ios/chrome/browser/signin/authentication_service_factory.h"
 #import "ios/chrome/browser/signin/fake_authentication_service_delegate.h"
@@ -51,9 +50,6 @@
         ios::TemplateURLServiceFactory::GetInstance(),
         ios::TemplateURLServiceFactory::GetDefaultFactory());
     test_cbs_builder.AddTestingFactory(
-        IOSChromeContentSuggestionsServiceFactory::GetInstance(),
-        IOSChromeContentSuggestionsServiceFactory::GetDefaultFactory());
-    test_cbs_builder.AddTestingFactory(
         IOSChromeLargeIconServiceFactory::GetInstance(),
         IOSChromeLargeIconServiceFactory::GetDefaultFactory());
     test_cbs_builder.AddTestingFactory(
diff --git a/ios/chrome/browser/ui/ntp/new_tab_page_mediator_unittest.mm b/ios/chrome/browser/ui/ntp/new_tab_page_mediator_unittest.mm
index ad91662..14e9d82 100644
--- a/ios/chrome/browser/ui/ntp/new_tab_page_mediator_unittest.mm
+++ b/ios/chrome/browser/ui/ntp/new_tab_page_mediator_unittest.mm
@@ -12,7 +12,6 @@
 #import "ios/chrome/browser/main/browser.h"
 #import "ios/chrome/browser/main/test_browser.h"
 #import "ios/chrome/browser/ntp/new_tab_page_tab_helper.h"
-#import "ios/chrome/browser/ntp_snippets/ios_chrome_content_suggestions_service_factory.h"
 #import "ios/chrome/browser/search_engines/template_url_service_factory.h"
 #import "ios/chrome/browser/signin/authentication_service_factory.h"
 #import "ios/chrome/browser/signin/chrome_account_manager_service_factory.h"
@@ -47,9 +46,6 @@
         ios::TemplateURLServiceFactory::GetInstance(),
         ios::TemplateURLServiceFactory::GetDefaultFactory());
     test_cbs_builder.AddTestingFactory(
-        IOSChromeContentSuggestionsServiceFactory::GetInstance(),
-        IOSChromeContentSuggestionsServiceFactory::GetDefaultFactory());
-    test_cbs_builder.AddTestingFactory(
         AuthenticationServiceFactory::GetInstance(),
         AuthenticationServiceFactory::GetDefaultFactory());
     chrome_browser_state_ = test_cbs_builder.Build();
diff --git a/ios/chrome/browser/ui/omnibox/BUILD.gn b/ios/chrome/browser/ui/omnibox/BUILD.gn
index 9d25b18..a7d4649 100644
--- a/ios/chrome/browser/ui/omnibox/BUILD.gn
+++ b/ios/chrome/browser/ui/omnibox/BUILD.gn
@@ -6,7 +6,8 @@
   configs += [ "//build/config/compiler:enable_arc" ]
 
   sources = [
-    "location_bar_delegate.h",
+    "omnibox_controller_delegate.h",
+    "omnibox_focus_delegate.h",
     "web_omnibox_edit_controller.cc",
     "web_omnibox_edit_controller.h",
   ]
diff --git a/ios/chrome/browser/ui/omnibox/location_bar_delegate.h b/ios/chrome/browser/ui/omnibox/location_bar_delegate.h
deleted file mode 100644
index f69600a6..0000000
--- a/ios/chrome/browser/ui/omnibox/location_bar_delegate.h
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2017 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_UI_OMNIBOX_LOCATION_BAR_DELEGATE_H_
-#define IOS_CHROME_BROWSER_UI_OMNIBOX_LOCATION_BAR_DELEGATE_H_
-
-#import <Foundation/Foundation.h>
-
-class LocationBarModel;
-
-namespace web {
-class WebState;
-}
-// Delegate for LocationBarController objects.  Used to provide the location bar
-// a way to open URLs and otherwise interact with the browser.
-@protocol LocationBarDelegate
-- (void)locationBarHasBecomeFirstResponder;
-- (void)locationBarHasResignedFirstResponder;
-- (web::WebState*)webState;
-- (LocationBarModel*)locationBarModel;
-@end
-
-#endif  // IOS_CHROME_BROWSER_UI_OMNIBOX_LOCATION_BAR_DELEGATE_H_
diff --git a/ios/chrome/browser/ui/omnibox/omnibox_controller_delegate.h b/ios/chrome/browser/ui/omnibox/omnibox_controller_delegate.h
new file mode 100644
index 0000000..c79ade1
--- /dev/null
+++ b/ios/chrome/browser/ui/omnibox/omnibox_controller_delegate.h
@@ -0,0 +1,22 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_UI_OMNIBOX_OMNIBOX_CONTROLLER_DELEGATE_H_
+#define IOS_CHROME_BROWSER_UI_OMNIBOX_OMNIBOX_CONTROLLER_DELEGATE_H_
+
+#import <Foundation/Foundation.h>
+
+class LocationBarModel;
+
+namespace web {
+class WebState;
+}
+// Delegate for omniboxController object. Used to provide the location bar
+// a way to open URLs and otherwise interact with the browser.
+@protocol OmniboxControllerDelegate
+- (web::WebState*)webState;
+- (LocationBarModel*)locationBarModel;
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_OMNIBOX_OMNIBOX_CONTROLLER_DELEGATE_H_
diff --git a/ios/chrome/browser/ui/omnibox/omnibox_focus_delegate.h b/ios/chrome/browser/ui/omnibox/omnibox_focus_delegate.h
new file mode 100644
index 0000000..c82dfe0
--- /dev/null
+++ b/ios/chrome/browser/ui/omnibox/omnibox_focus_delegate.h
@@ -0,0 +1,20 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_UI_OMNIBOX_OMNIBOX_FOCUS_DELEGATE_H_
+#define IOS_CHROME_BROWSER_UI_OMNIBOX_OMNIBOX_FOCUS_DELEGATE_H_
+
+#import <Foundation/Foundation.h>
+
+// Protocol receiving notification when the omnibox is focused/unfocused.
+@protocol OmniboxFocusDelegate
+
+// Called when the omnibox gains keyboard focus.
+- (void)omniboxDidBecomeFirstResponder;
+// Called when the omnibox loses keyboard focus.
+- (void)omniboxDidResignFirstResponder;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_OMNIBOX_OMNIBOX_FOCUS_DELEGATE_H_
diff --git a/ios/chrome/browser/ui/omnibox/web_omnibox_edit_controller_impl.h b/ios/chrome/browser/ui/omnibox/web_omnibox_edit_controller_impl.h
index f8371406..80d0cb1 100644
--- a/ios/chrome/browser/ui/omnibox/web_omnibox_edit_controller_impl.h
+++ b/ios/chrome/browser/ui/omnibox/web_omnibox_edit_controller_impl.h
@@ -7,8 +7,9 @@
 
 #include "ios/chrome/browser/ui/omnibox/web_omnibox_edit_controller.h"
 
-@protocol LocationBarDelegate;
 @protocol LocationBarURLLoader;
+@protocol OmniboxControllerDelegate;
+@protocol OmniboxFocusDelegate;
 
 // A minimal implementation of WebOmniboxEditController. Designed to work with
 // LocationBarMediator and LocationBarCoordinator.
@@ -17,7 +18,8 @@
 // it.
 class WebOmniboxEditControllerImpl : public WebOmniboxEditController {
  public:
-  WebOmniboxEditControllerImpl(id<LocationBarDelegate> delegate);
+  WebOmniboxEditControllerImpl(id<OmniboxControllerDelegate> delegate,
+                               id<OmniboxFocusDelegate> focus_delegate);
   ~WebOmniboxEditControllerImpl() override;
 
   void SetURLLoader(id<LocationBarURLLoader> URLLoader) {
@@ -47,7 +49,8 @@
   const LocationBarModel* GetLocationBarModel() const override;
 
  private:
-  __weak id<LocationBarDelegate> delegate_;
+  __weak id<OmniboxControllerDelegate> delegate_;
+  __weak id<OmniboxFocusDelegate> focus_delegate_;
   __weak id<LocationBarURLLoader> URLLoader_;
 };
 
diff --git a/ios/chrome/browser/ui/omnibox/web_omnibox_edit_controller_impl.mm b/ios/chrome/browser/ui/omnibox/web_omnibox_edit_controller_impl.mm
index 6525280..1e245775 100644
--- a/ios/chrome/browser/ui/omnibox/web_omnibox_edit_controller_impl.mm
+++ b/ios/chrome/browser/ui/omnibox/web_omnibox_edit_controller_impl.mm
@@ -6,7 +6,8 @@
 
 #import "components/omnibox/browser/location_bar_model.h"
 #import "ios/chrome/browser/ui/location_bar/location_bar_url_loader.h"
-#import "ios/chrome/browser/ui/omnibox/location_bar_delegate.h"
+#import "ios/chrome/browser/ui/omnibox/omnibox_controller_delegate.h"
+#import "ios/chrome/browser/ui/omnibox/omnibox_focus_delegate.h"
 #import "url/gurl.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -14,8 +15,9 @@
 #endif
 
 WebOmniboxEditControllerImpl::WebOmniboxEditControllerImpl(
-    id<LocationBarDelegate> delegate)
-    : delegate_(delegate){
+    id<OmniboxControllerDelegate> delegate,
+    id<OmniboxFocusDelegate> focus_delegate)
+    : delegate_(delegate), focus_delegate_(focus_delegate) {
   // TODO(crbug.com/818645): add security icon and its a11y labels
 }
 
@@ -27,12 +29,12 @@
 
 void WebOmniboxEditControllerImpl::OnKillFocus() {
   // TODO(crbug.com/818648): disable fullscreen in LocationBarMediator.
-  [delegate_ locationBarHasResignedFirstResponder];
+  [focus_delegate_ omniboxDidResignFirstResponder];
 }
 
 void WebOmniboxEditControllerImpl::OnSetFocus() {
   // TODO(crbug.com/818648): reenable fullscreen in LocationBarMediator.
-  [delegate_ locationBarHasBecomeFirstResponder];
+  [focus_delegate_ omniboxDidBecomeFirstResponder];
 }
 
 void WebOmniboxEditControllerImpl::OnAutocompleteAccept(
diff --git a/ios/chrome/browser/ui/overscroll_actions/overscroll_actions_controller.mm b/ios/chrome/browser/ui/overscroll_actions/overscroll_actions_controller.mm
index 22c3e8f..cc123c6 100644
--- a/ios/chrome/browser/ui/overscroll_actions/overscroll_actions_controller.mm
+++ b/ios/chrome/browser/ui/overscroll_actions/overscroll_actions_controller.mm
@@ -443,9 +443,12 @@
       shouldAllowOverscrollActionsForOverscrollActionsController:self];
   const BOOL isCurrentlyProcessingOverscroll =
       self.overscrollState != OverscrollState::NO_PULL_STARTED;
+  const BOOL fullscreenModeDisablesOverscrollActions =
+      [_webViewProxy isWebPageInFullscreenMode];
   return isCurrentlyProcessingOverscroll ||
          (isScrolledToTop && isMinimumTimeBetweenScrollRespected &&
-          delegateAllowOverscrollActions && !isZooming);
+          delegateAllowOverscrollActions && !isZooming &&
+          !fullscreenModeDisablesOverscrollActions);
 }
 
 - (void)scrollViewDidEndDraggingWillDecelerate:(BOOL)decelerate
diff --git a/ios/chrome/browser/ui/popup_menu/overflow_menu/overflow_menu_mediator.mm b/ios/chrome/browser/ui/popup_menu/overflow_menu/overflow_menu_mediator.mm
index 969ba0a..c654d9b2 100644
--- a/ios/chrome/browser/ui/popup_menu/overflow_menu/overflow_menu_mediator.mm
+++ b/ios/chrome/browser/ui/popup_menu/overflow_menu/overflow_menu_mediator.mm
@@ -26,7 +26,7 @@
 #import "ios/chrome/browser/bookmarks/bookmark_model_bridge_observer.h"
 #import "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #import "ios/chrome/browser/commerce/push_notification/push_notification_feature.h"
-#import "ios/chrome/browser/find_in_page/find_tab_helper.h"
+#import "ios/chrome/browser/find_in_page/java_script_find_tab_helper.h"
 #import "ios/chrome/browser/flags/system_flags.h"
 #import "ios/chrome/browser/follow/follow_browser_agent.h"
 #import "ios/chrome/browser/follow/follow_menu_updater.h"
@@ -1314,7 +1314,7 @@
     return NO;
   }
 
-  auto* helper = FindTabHelper::FromWebState(self.webState);
+  auto* helper = JavaScriptFindTabHelper::FromWebState(self.webState);
   return (helper && helper->CurrentPageSupportsFindInPage() &&
           !helper->IsFindUIActive());
 }
diff --git a/ios/chrome/browser/ui/popup_menu/popup_menu_mediator.mm b/ios/chrome/browser/ui/popup_menu/popup_menu_mediator.mm
index c7a73e1..5519d5e3 100644
--- a/ios/chrome/browser/ui/popup_menu/popup_menu_mediator.mm
+++ b/ios/chrome/browser/ui/popup_menu/popup_menu_mediator.mm
@@ -29,7 +29,7 @@
 #import "ios/chrome/browser/bookmarks/bookmark_model_bridge_observer.h"
 #import "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #import "ios/chrome/browser/commerce/push_notification/push_notification_feature.h"
-#import "ios/chrome/browser/find_in_page/find_tab_helper.h"
+#import "ios/chrome/browser/find_in_page/java_script_find_tab_helper.h"
 #import "ios/chrome/browser/follow/follow_browser_agent.h"
 #import "ios/chrome/browser/follow/follow_menu_updater.h"
 #import "ios/chrome/browser/follow/follow_tab_helper.h"
@@ -881,7 +881,7 @@
 - (BOOL)isFindInPageEnabled {
   if (!self.webState)
     return NO;
-  auto* helper = FindTabHelper::FromWebState(self.webState);
+  auto* helper = JavaScriptFindTabHelper::FromWebState(self.webState);
   return (helper && helper->CurrentPageSupportsFindInPage() &&
           !helper->IsFindUIActive());
 }
diff --git a/ios/chrome/browser/ui/settings/password/password_details/password_details_table_view_controller.mm b/ios/chrome/browser/ui/settings/password/password_details/password_details_table_view_controller.mm
index 6a8f703..3112e0aed 100644
--- a/ios/chrome/browser/ui/settings/password/password_details/password_details_table_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/password/password_details/password_details_table_view_controller.mm
@@ -232,8 +232,9 @@
     (PasswordDetails*)passwordDetails {
   TableViewStackedDetailsItem* item =
       [[TableViewStackedDetailsItem alloc] initWithType:ItemTypeWebsite];
-  // TODO(crbug.com/1358982): Update text to "Sites".
-  item.titleText = l10n_util::GetNSString(IDS_IOS_SHOW_PASSWORD_VIEW_SITE);
+  item.titleText = l10n_util::GetNSString(
+      IsPasswordGroupingEnabled() ? IDS_IOS_SHOW_PASSWORD_VIEW_SITES
+                                  : IDS_IOS_SHOW_PASSWORD_VIEW_SITE);
   item.detailTexts = passwordDetails.websites;
 
   return item;
diff --git a/ios/chrome/browser/ui/settings/password/password_manager_egtest.mm b/ios/chrome/browser/ui/settings/password/password_manager_egtest.mm
index 5aa9feb6..8ac6413 100644
--- a/ios/chrome/browser/ui/settings/password/password_manager_egtest.mm
+++ b/ios/chrome/browser/ui/settings/password/password_manager_egtest.mm
@@ -163,14 +163,6 @@
   return TextFieldForCellWithLabelId(IDS_IOS_SHOW_PASSWORD_VIEW_SITE);
 }
 
-// Matcher for the websites in Password Details view.
-// `websites` should be in the format "website1, website2,..." with `websiteN`
-// being the website displayed in the nth detail row of the website cell.
-id<GREYMatcher> PasswordDetailWebsites(NSString* websites) {
-  return grey_accessibilityLabel(
-      [NSString stringWithFormat:@"Site, %@", websites]);
-}
-
 // Matcher for the username in Password Details view.
 id<GREYMatcher> PasswordDetailUsername() {
   return TextFieldForCellWithLabelId(IDS_IOS_SHOW_PASSWORD_VIEW_USERNAME);
@@ -431,6 +423,11 @@
     interactionForSinglePasswordEntryWithDomain:(NSString*)domain
                                        username:(NSString*)username;
 
+// Matcher for the websites in Password Details view.
+// `websites` should be in the format "website1, website2,..." with `websiteN`
+// being the website displayed in the nth detail row of the website cell.
+- (id<GREYMatcher>)matcherForPasswordDetailCellWithWebsites:(NSString*)websites;
+
 @end
 
 @implementation PasswordManagerTestCase {
@@ -451,6 +448,12 @@
                                    kGREYDirectionDown);
 }
 
+- (id<GREYMatcher>)matcherForPasswordDetailCellWithWebsites:
+    (NSString*)websites {
+  return grey_accessibilityLabel(
+      [NSString stringWithFormat:@"Sites, %@", websites]);
+}
+
 - (void)setUp {
   [super setUp];
   GREYAssertNil([MetricsAppInterface setupHistogramTester],
@@ -725,7 +728,7 @@
       performAction:grey_tap()];
 
   CopyPasswordDetailWithInteraction(GetInteractionForPasswordDetailItem(
-      PasswordDetailWebsites(@"https://example.com/")));
+      [self matcherForPasswordDetailCellWithWebsites:@"https://example.com/"]));
 
   NSString* snackbarLabel =
       l10n_util::GetNSString(IDS_IOS_SETTINGS_SITE_WAS_COPIED_MESSAGE);
@@ -1174,7 +1177,8 @@
 
   // Check that the Site and Username are present and correct.
   [[EarlGrey
-      selectElementWithMatcher:PasswordDetailWebsites(@"https://example.com/")]
+      selectElementWithMatcher:[self matcherForPasswordDetailCellWithWebsites:
+                                         @"https://example.com/"]]
       assertWithMatcher:grey_notNil()];
   [[EarlGrey selectElementWithMatcher:PasswordDetailUsername()]
       assertWithMatcher:grey_textFieldValue(@"federated username")];
@@ -1217,7 +1221,8 @@
       performAction:grey_tap()];
 
   [[EarlGrey
-      selectElementWithMatcher:PasswordDetailWebsites(@"https://example.com/")]
+      selectElementWithMatcher:[self matcherForPasswordDetailCellWithWebsites:
+                                         @"https://example.com/"]]
       assertWithMatcher:grey_notNil()];
   [[EarlGrey selectElementWithMatcher:PasswordDetailUsername()]
       assertWithMatcher:grey_textFieldValue(@"concrete username")];
@@ -1233,7 +1238,8 @@
   [GetInteractionForPasswordDetailItem(PasswordDetailUsername())
       assertWithMatcher:grey_layout(
                             @[ Below() ],
-                            PasswordDetailWebsites(@"https://example.com/"))];
+                            [self matcherForPasswordDetailCellWithWebsites:
+                                      @"https://example.com/"])];
 
   [[EarlGrey selectElementWithMatcher:SettingsMenuBackButton()]
       performAction:grey_tap()];
@@ -1255,7 +1261,8 @@
   [GetInteractionForPasswordEntry(@"example.com") performAction:grey_tap()];
 
   [[EarlGrey
-      selectElementWithMatcher:PasswordDetailWebsites(@"https://example.com/")]
+      selectElementWithMatcher:[self matcherForPasswordDetailCellWithWebsites:
+                                         @"https://example.com/"]]
       assertWithMatcher:grey_notNil()];
   [[EarlGrey selectElementWithMatcher:PasswordDetailUsername()]
       assertWithMatcher:grey_nil()];
@@ -1288,7 +1295,8 @@
       performAction:grey_tap()];
 
   [[EarlGrey
-      selectElementWithMatcher:PasswordDetailWebsites(@"https://example.com/")]
+      selectElementWithMatcher:[self matcherForPasswordDetailCellWithWebsites:
+                                         @"https://example.com/"]]
       assertWithMatcher:grey_notNil()];
   [[EarlGrey selectElementWithMatcher:PasswordDetailUsername()]
       assertWithMatcher:grey_textFieldValue(@"federated username")];
@@ -1300,7 +1308,8 @@
   [GetInteractionForPasswordDetailItem(PasswordDetailUsername())
       assertWithMatcher:grey_layout(
                             @[ Below() ],
-                            PasswordDetailWebsites(@"https://example.com/"))];
+                            [self matcherForPasswordDetailCellWithWebsites:
+                                      @"https://example.com/"])];
   [[EarlGrey selectElementWithMatcher:PasswordDetailFederation()]
       assertWithMatcher:grey_layout(@[ Below() ], PasswordDetailUsername())];
 
@@ -1553,8 +1562,9 @@
   // Check that the detail view loaded correctly by verifying the site content.
   [[EarlGrey
       selectElementWithMatcher:
-          PasswordDetailWebsites([NSString
-              stringWithFormat:@"https://www%02d.example.com/", kRemoteIndex])]
+          [self matcherForPasswordDetailCellWithWebsites:
+                    [NSString stringWithFormat:@"https://www%02d.example.com/",
+                                               kRemoteIndex]]]
       assertWithMatcher:grey_notNil()];
 
   [[EarlGrey selectElementWithMatcher:SettingsMenuBackButton()]
@@ -2797,6 +2807,12 @@
                                    kGREYDirectionDown);
 }
 
+- (id<GREYMatcher>)matcherForPasswordDetailCellWithWebsites:
+    (NSString*)websites {
+  return grey_accessibilityLabel(
+      [NSString stringWithFormat:@"Site, %@", websites]);
+}
+
 // This causes the test case to actually be detected as a test case. The actual
 // tests are all inherited from the parent class.
 - (void)testEmpty {
diff --git a/ios/chrome/browser/ui/sharing/activity_services/data/share_to_data_builder.mm b/ios/chrome/browser/ui/sharing/activity_services/data/share_to_data_builder.mm
index 96fb50b..bfb87b89 100644
--- a/ios/chrome/browser/ui/sharing/activity_services/data/share_to_data_builder.mm
+++ b/ios/chrome/browser/ui/sharing/activity_services/data/share_to_data_builder.mm
@@ -8,7 +8,7 @@
 #import "base/strings/sys_string_conversions.h"
 #import "components/send_tab_to_self/entry_point_display_reason.h"
 #import "ios/chrome/browser/browser_state/chrome_browser_state.h"
-#import "ios/chrome/browser/find_in_page/find_tab_helper.h"
+#import "ios/chrome/browser/find_in_page/java_script_find_tab_helper.h"
 #import "ios/chrome/browser/signin/chrome_account_manager_service.h"
 #import "ios/chrome/browser/signin/chrome_account_manager_service_factory.h"
 #import "ios/chrome/browser/sync/send_tab_to_self_sync_service_factory.h"
@@ -69,7 +69,8 @@
     userAgent = visibleItem->GetUserAgentType();
   }
 
-  FindTabHelper* helper = FindTabHelper::FromWebState(web_state);
+  JavaScriptFindTabHelper* helper =
+      JavaScriptFindTabHelper::FromWebState(web_state);
   BOOL is_page_searchable =
       (helper && helper->CurrentPageSupportsFindInPage() &&
        !helper->IsFindUIActive());
diff --git a/ios/chrome/browser/ui/toolbar/BUILD.gn b/ios/chrome/browser/ui/toolbar/BUILD.gn
index ffaf06c..9882222 100644
--- a/ios/chrome/browser/ui/toolbar/BUILD.gn
+++ b/ios/chrome/browser/ui/toolbar/BUILD.gn
@@ -16,7 +16,6 @@
     "toolbar_coordinatee.h",
     "toolbar_coordinator_adaptor.h",
     "toolbar_coordinator_adaptor.mm",
-    "toolbar_coordinator_delegate.h",
     "toolbar_mediator.h",
     "toolbar_mediator.mm",
   ]
diff --git a/ios/chrome/browser/ui/toolbar/primary_toolbar_coordinator.h b/ios/chrome/browser/ui/toolbar/primary_toolbar_coordinator.h
index bd4e8ace..7b88f71 100644
--- a/ios/chrome/browser/ui/toolbar/primary_toolbar_coordinator.h
+++ b/ios/chrome/browser/ui/toolbar/primary_toolbar_coordinator.h
@@ -10,7 +10,7 @@
 
 @protocol ActivityServicePositioner;
 @protocol OmniboxPopupPresenterDelegate;
-@protocol ToolbarCoordinatorDelegate;
+@protocol OmniboxFocusDelegate;
 @class ViewRevealingVerticalPanHandler;
 @protocol ViewRevealingAnimatee;
 
@@ -21,7 +21,7 @@
 
 // Delegate for this coordinator.
 // TODO(crbug.com/799446): Change this.
-@property(nonatomic, weak) id<ToolbarCoordinatorDelegate> delegate;
+@property(nonatomic, weak) id<OmniboxFocusDelegate> delegate;
 
 // Defines where the omnibox popup will be positioned.
 @property(nonatomic, weak) id<OmniboxPopupPresenterDelegate>
diff --git a/ios/chrome/browser/ui/toolbar/primary_toolbar_coordinator.mm b/ios/chrome/browser/ui/toolbar/primary_toolbar_coordinator.mm
index 3f28992..4afc2f2 100644
--- a/ios/chrome/browser/ui/toolbar/primary_toolbar_coordinator.mm
+++ b/ios/chrome/browser/ui/toolbar/primary_toolbar_coordinator.mm
@@ -32,7 +32,6 @@
 #import "ios/chrome/browser/ui/toolbar/primary_toolbar_mediator.h"
 #import "ios/chrome/browser/ui/toolbar/primary_toolbar_view_controller.h"
 #import "ios/chrome/browser/ui/toolbar/primary_toolbar_view_controller_delegate.h"
-#import "ios/chrome/browser/ui/toolbar/toolbar_coordinator_delegate.h"
 #import "ios/chrome/browser/ui/util/uikit_ui_util.h"
 #import "ios/chrome/browser/web_state_list/web_state_list.h"
 #import "ios/components/webui/web_ui_url_constants.h"
diff --git a/ios/chrome/browser/ui/toolbar/toolbar_coordinator_delegate.h b/ios/chrome/browser/ui/toolbar/toolbar_coordinator_delegate.h
deleted file mode 100644
index 8d1b2190..0000000
--- a/ios/chrome/browser/ui/toolbar/toolbar_coordinator_delegate.h
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2017 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_UI_TOOLBAR_TOOLBAR_COORDINATOR_DELEGATE_H_
-#define IOS_CHROME_BROWSER_UI_TOOLBAR_TOOLBAR_COORDINATOR_DELEGATE_H_
-
-#import <Foundation/Foundation.h>
-
-class LocationBarModel;
-
-// Protocol receiving notification when the some events occur in the
-// ToolbarCoordinator
-@protocol ToolbarCoordinatorDelegate<NSObject>
-
-// Called when the location bar gains keyboard focus.
-- (void)locationBarDidBecomeFirstResponder;
-// Called when the location bar loses keyboard focus.
-- (void)locationBarDidResignFirstResponder;
-
-@end
-
-#endif  // IOS_CHROME_BROWSER_UI_TOOLBAR_TOOLBAR_COORDINATOR_DELEGATE_H_
diff --git a/ios/chrome/test/earl_grey/BUILD.gn b/ios/chrome/test/earl_grey/BUILD.gn
index 21e9c7a..e715b84e 100644
--- a/ios/chrome/test/earl_grey/BUILD.gn
+++ b/ios/chrome/test/earl_grey/BUILD.gn
@@ -267,6 +267,7 @@
     "//components/strings",
     "//components/sync/base",
     "//ios/chrome/app/strings",
+    "//ios/chrome/browser/policy:eg_test_support+eg2",
     "//ios/chrome/browser/ui/ntp:feature_flags",
     "//ios/chrome/browser/ui/popup_menu:constants",
     "//ios/chrome/browser/ui/recent_tabs:recent_tabs_ui_constants",
diff --git a/ios/chrome/test/earl_grey/chrome_test_case.mm b/ios/chrome/test/earl_grey/chrome_test_case.mm
index a81d3676..2ece0bf 100644
--- a/ios/chrome/test/earl_grey/chrome_test_case.mm
+++ b/ios/chrome/test/earl_grey/chrome_test_case.mm
@@ -12,6 +12,7 @@
 #import "base/ios/ios_util.h"
 #import "base/strings/sys_string_conversions.h"
 #import "base/test/ios/wait_util.h"
+#import "ios/chrome/browser/policy/policy_earl_grey_utils.h"
 #import "ios/chrome/browser/ui/ntp/new_tab_page_feature.h"
 #import "ios/chrome/browser/web/features.h"
 #import "ios/chrome/test/earl_grey/chrome_earl_grey.h"
@@ -253,6 +254,10 @@
       [[self class] removeAnyOpenMenusAndInfoBars];
     }
     [[self class] closeAllTabs];
+
+    // Clear testing policies to make sure they don't change the browser's
+    // behavior in follow-up tests.
+    policy_test_utils::ClearPolicies();
   }
 
   if ([[GREY_REMOTE_CLASS_IN_APP(UIDevice) currentDevice] orientation] !=
diff --git a/ios/web/BUILD.gn b/ios/web/BUILD.gn
index 365ee2b..e4929d21 100644
--- a/ios/web/BUILD.gn
+++ b/ios/web/BUILD.gn
@@ -614,7 +614,7 @@
   ]
   sources = [
     "browser_state_web_view_partition_inttest.mm",
-    "find_in_page/find_in_page_manager_inttest.mm",
+    "find_in_page/java_script_find_in_page_manager_inttest.mm",
     "navigation/crw_error_page_helper_inttest.mm",
     "navigation/crw_wk_navigation_handler_inttest.mm",
     "navigation/history_state_operations_inttest.mm",
diff --git a/ios/web/find_in_page/BUILD.gn b/ios/web/find_in_page/BUILD.gn
index 253fccdf..36f08a7 100644
--- a/ios/web/find_in_page/BUILD.gn
+++ b/ios/web/find_in_page/BUILD.gn
@@ -24,10 +24,10 @@
     "find_in_page_java_script_feature.h",
     "find_in_page_java_script_feature.mm",
     "find_in_page_manager_delegate_bridge.mm",
-    "find_in_page_manager_impl.h",
-    "find_in_page_manager_impl.mm",
-    "find_in_page_request.h",
-    "find_in_page_request.mm",
+    "java_script_find_in_page_manager_impl.h",
+    "java_script_find_in_page_manager_impl.mm",
+    "java_script_find_in_page_request.h",
+    "java_script_find_in_page_request.mm",
   ]
 
   configs += [ "//build/config/compiler:enable_arc" ]
@@ -82,8 +82,8 @@
   sources = [
     "find_in_page_js_unittest.mm",
     "find_in_page_manager_delegate_bridge_unittest.mm",
-    "find_in_page_manger_impl_unittest.mm",
-    "find_in_page_request_unittest.mm",
+    "java_script_find_in_page_manager_impl_unittest.mm",
+    "java_script_find_in_page_request_unittest.mm",
   ]
 
   configs += [ "//build/config/compiler:enable_arc" ]
diff --git a/ios/web/find_in_page/find_in_page_manager_delegate_bridge.mm b/ios/web/find_in_page/find_in_page_manager_delegate_bridge.mm
index adb0ef3..b17d1dcf 100644
--- a/ios/web/find_in_page/find_in_page_manager_delegate_bridge.mm
+++ b/ios/web/find_in_page/find_in_page_manager_delegate_bridge.mm
@@ -4,7 +4,7 @@
 
 #import "ios/web/public/find_in_page/find_in_page_manager_delegate_bridge.h"
 
-#import "ios/web/public/find_in_page/find_in_page_manager.h"
+#import "ios/web/public/find_in_page/java_script_find_in_page_manager.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -24,7 +24,8 @@
   if ([delegate_ respondsToSelector:@selector
                  (findInPageManager:
                      didHighlightMatchesOfQuery:withMatchCount:forWebState:)]) {
-    [delegate_ findInPageManager:web::FindInPageManager::FromWebState(web_state)
+    [delegate_ findInPageManager:web::JavaScriptFindInPageManager::FromWebState(
+                                     web_state)
         didHighlightMatchesOfQuery:query
                     withMatchCount:match_count
                        forWebState:web_state];
@@ -37,7 +38,8 @@
   if ([delegate_ respondsToSelector:@selector
                  (findInPageManager:
                      didSelectMatchAtIndex:withContextString:forWebState:)]) {
-    [delegate_ findInPageManager:web::FindInPageManager::FromWebState(web_state)
+    [delegate_ findInPageManager:web::JavaScriptFindInPageManager::FromWebState(
+                                     web_state)
            didSelectMatchAtIndex:index
                withContextString:context_string
                      forWebState:web_state];
diff --git a/ios/web/find_in_page/find_in_page_manager_delegate_bridge_unittest.mm b/ios/web/find_in_page/find_in_page_manager_delegate_bridge_unittest.mm
index 422043e2..e3e32ee 100644
--- a/ios/web/find_in_page/find_in_page_manager_delegate_bridge_unittest.mm
+++ b/ios/web/find_in_page/find_in_page_manager_delegate_bridge_unittest.mm
@@ -4,7 +4,7 @@
 
 #import "ios/web/public/find_in_page/find_in_page_manager_delegate_bridge.h"
 
-#import "ios/web/find_in_page/find_in_page_manager_impl.h"
+#import "ios/web/find_in_page/java_script_find_in_page_manager_impl.h"
 #import "ios/web/public/test/fakes/crw_fake_find_in_page_manager_delegate.h"
 #import "ios/web/public/test/fakes/fake_web_state.h"
 #import "testing/platform_test.h"
@@ -21,7 +21,7 @@
   FindInPageManagerDelegateBridgeTest()
       : delegate_([[CRWFakeFindInPageManagerDelegate alloc] init]),
         bridge_(std::make_unique<FindInPageManagerDelegateBridge>(delegate_)) {
-    FindInPageManagerImpl::CreateForWebState(&fake_web_state_);
+    JavaScriptFindInPageManagerImpl::CreateForWebState(&fake_web_state_);
   }
 
   CRWFakeFindInPageManagerDelegate* delegate_ = nil;
diff --git a/ios/web/find_in_page/find_in_page_manager_impl.h b/ios/web/find_in_page/java_script_find_in_page_manager_impl.h
similarity index 78%
rename from ios/web/find_in_page/find_in_page_manager_impl.h
rename to ios/web/find_in_page/java_script_find_in_page_manager_impl.h
index a301c08..48ea841 100644
--- a/ios/web/find_in_page/find_in_page_manager_impl.h
+++ b/ios/web/find_in_page/java_script_find_in_page_manager_impl.h
@@ -2,14 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef IOS_WEB_FIND_IN_PAGE_FIND_IN_PAGE_MANAGER_IMPL_H_
-#define IOS_WEB_FIND_IN_PAGE_FIND_IN_PAGE_MANAGER_IMPL_H_
+#ifndef IOS_WEB_FIND_IN_PAGE_JAVA_SCRIPT_FIND_IN_PAGE_MANAGER_IMPL_H_
+#define IOS_WEB_FIND_IN_PAGE_JAVA_SCRIPT_FIND_IN_PAGE_MANAGER_IMPL_H_
 
 #include <string>
 
 #include "base/memory/weak_ptr.h"
-#import "ios/web/find_in_page/find_in_page_request.h"
-#import "ios/web/public/find_in_page/find_in_page_manager.h"
+#import "ios/web/find_in_page/java_script_find_in_page_request.h"
+#import "ios/web/public/find_in_page/java_script_find_in_page_manager.h"
 #include "ios/web/public/web_state_observer.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
@@ -27,11 +27,11 @@
 class WebState;
 class WebFrame;
 
-class FindInPageManagerImpl : public FindInPageManager,
-                              public web::WebStateObserver {
+class JavaScriptFindInPageManagerImpl : public JavaScriptFindInPageManager,
+                                        public web::WebStateObserver {
  public:
-  explicit FindInPageManagerImpl(web::WebState* web_state);
-  ~FindInPageManagerImpl() override;
+  explicit JavaScriptFindInPageManagerImpl(web::WebState* web_state);
+  ~JavaScriptFindInPageManagerImpl() override;
 
   // Need to overload FindInPageManager::CreateForWebState() as the default
   // implementation inherited from WebStateUserData<FindInPageManager> would
@@ -46,7 +46,7 @@
   void SetDelegate(FindInPageManagerDelegate* delegate) override;
 
  private:
-  friend class web::WebStateUserData<FindInPageManagerImpl>;
+  friend class web::WebStateUserData<JavaScriptFindInPageManagerImpl>;
 
   // Executes find logic for `FindInPageSearch` option.
   void StartSearch(NSString* query);
@@ -81,11 +81,11 @@
 
  protected:
   // Holds the state of the most recent find in page request.
-  FindInPageRequest last_find_request_;
+  JavaScriptFindInPageRequest last_find_request_;
   FindInPageManagerDelegate* delegate_ = nullptr;
   web::WebState* web_state_ = nullptr;
-  base::WeakPtrFactory<FindInPageManagerImpl> weak_factory_;
+  base::WeakPtrFactory<JavaScriptFindInPageManagerImpl> weak_factory_;
 };
 }  // namespace web
 
-#endif  // IOS_WEB_FIND_IN_PAGE_FIND_IN_PAGE_MANAGER_IMPL_H_
+#endif  // IOS_WEB_FIND_IN_PAGE_JAVA_SCRIPT_FIND_IN_PAGE_MANAGER_IMPL_H_
diff --git a/ios/web/find_in_page/find_in_page_manager_impl.mm b/ios/web/find_in_page/java_script_find_in_page_manager_impl.mm
similarity index 75%
rename from ios/web/find_in_page/find_in_page_manager_impl.mm
rename to ios/web/find_in_page/java_script_find_in_page_manager_impl.mm
index 8b16b68..d112419e9 100644
--- a/ios/web/find_in_page/find_in_page_manager_impl.mm
+++ b/ios/web/find_in_page/java_script_find_in_page_manager_impl.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/web/find_in_page/find_in_page_manager_impl.h"
+#import "ios/web/find_in_page/java_script_find_in_page_manager_impl.h"
 
 #import "base/metrics/user_metrics.h"
 #import "base/metrics/user_metrics_action.h"
@@ -25,41 +25,46 @@
 namespace web {
 
 // static
-FindInPageManagerImpl::FindInPageManagerImpl(WebState* web_state)
+JavaScriptFindInPageManagerImpl::JavaScriptFindInPageManagerImpl(
+    WebState* web_state)
     : web_state_(web_state), weak_factory_(this) {
   web_state_->AddObserver(this);
 }
 
-void FindInPageManagerImpl::CreateForWebState(WebState* web_state) {
+void JavaScriptFindInPageManagerImpl::CreateForWebState(WebState* web_state) {
   DCHECK(web_state);
   if (!FromWebState(web_state)) {
-    web_state->SetUserData(UserDataKey(),
-                           std::make_unique<FindInPageManagerImpl>(web_state));
+    web_state->SetUserData(
+        UserDataKey(),
+        std::make_unique<JavaScriptFindInPageManagerImpl>(web_state));
   }
 }
 
-FindInPageManagerImpl::~FindInPageManagerImpl() {
+JavaScriptFindInPageManagerImpl::~JavaScriptFindInPageManagerImpl() {
   if (web_state_) {
     web_state_->RemoveObserver(this);
     web_state_ = nullptr;
   }
 }
 
-FindInPageManagerDelegate* FindInPageManagerImpl::GetDelegate() {
+FindInPageManagerDelegate* JavaScriptFindInPageManagerImpl::GetDelegate() {
   return delegate_;
 }
-void FindInPageManagerImpl::SetDelegate(FindInPageManagerDelegate* delegate) {
+void JavaScriptFindInPageManagerImpl::SetDelegate(
+    FindInPageManagerDelegate* delegate) {
   delegate_ = delegate;
 }
 
-void FindInPageManagerImpl::WebFrameDidBecomeAvailable(WebState* web_state,
-                                                       WebFrame* web_frame) {
+void JavaScriptFindInPageManagerImpl::WebFrameDidBecomeAvailable(
+    WebState* web_state,
+    WebFrame* web_frame) {
   const std::string frame_id = web_frame->GetFrameId();
   last_find_request_.AddFrame(web_frame);
 }
 
-void FindInPageManagerImpl::WebFrameWillBecomeUnavailable(WebState* web_state,
-                                                          WebFrame* web_frame) {
+void JavaScriptFindInPageManagerImpl::WebFrameWillBecomeUnavailable(
+    WebState* web_state,
+    WebFrame* web_frame) {
   int match_count =
       last_find_request_.GetMatchCountForFrame(web_frame->GetFrameId());
   last_find_request_.RemoveFrame(web_frame->GetFrameId());
@@ -72,12 +77,13 @@
   }
 }
 
-void FindInPageManagerImpl::WebStateDestroyed(WebState* web_state) {
+void JavaScriptFindInPageManagerImpl::WebStateDestroyed(WebState* web_state) {
   web_state_->RemoveObserver(this);
   web_state_ = nullptr;
 }
 
-void FindInPageManagerImpl::Find(NSString* query, FindInPageOptions options) {
+void JavaScriptFindInPageManagerImpl::Find(NSString* query,
+                                           FindInPageOptions options) {
   DCHECK(CanSearchContent());
 
   switch (options) {
@@ -94,7 +100,7 @@
   }
 }
 
-void FindInPageManagerImpl::StartSearch(NSString* query) {
+void JavaScriptFindInPageManagerImpl::StartSearch(NSString* query) {
   base::RecordAction(base::UserMetricsAction(kFindActionName));
   std::set<WebFrame*> all_frames =
       web_state_->GetWebFramesManager()->GetAllWebFrames();
@@ -104,17 +110,19 @@
     // Call asyncronously to match behavior if find was successful in frames.
     GetUIThreadTaskRunner({})->PostTask(
         FROM_HERE,
-        base::BindOnce(&FindInPageManagerImpl::LastFindRequestCompleted,
-                       weak_factory_.GetWeakPtr()));
+        base::BindOnce(
+            &JavaScriptFindInPageManagerImpl::LastFindRequestCompleted,
+            weak_factory_.GetWeakPtr()));
     return;
   }
 
   for (WebFrame* frame : all_frames) {
     bool result = FindInPageJavaScriptFeature::GetInstance()->Search(
         frame, base::SysNSStringToUTF8(query),
-        base::BindOnce(&FindInPageManagerImpl::ProcessFindInPageResult,
-                       weak_factory_.GetWeakPtr(), frame->GetFrameId(),
-                       last_find_request_.GetRequestId()));
+        base::BindOnce(
+            &JavaScriptFindInPageManagerImpl::ProcessFindInPageResult,
+            weak_factory_.GetWeakPtr(), frame->GetFrameId(),
+            last_find_request_.GetRequestId()));
 
     if (!result) {
       // Calling JavaScript function failed or the frame does not support
@@ -124,14 +132,15 @@
         // Call asyncronously to match behavior if find was done in frames.
         GetUIThreadTaskRunner({})->PostTask(
             FROM_HERE,
-            base::BindOnce(&FindInPageManagerImpl::LastFindRequestCompleted,
-                           weak_factory_.GetWeakPtr()));
+            base::BindOnce(
+                &JavaScriptFindInPageManagerImpl::LastFindRequestCompleted,
+                weak_factory_.GetWeakPtr()));
       }
     }
   }
 }
 
-void FindInPageManagerImpl::StopFinding() {
+void JavaScriptFindInPageManagerImpl::StopFinding() {
   last_find_request_.Reset(/*new_query=*/nil,
                            /*new_pending_frame_call_count=*/0);
 
@@ -145,11 +154,11 @@
   }
 }
 
-bool FindInPageManagerImpl::CanSearchContent() {
+bool JavaScriptFindInPageManagerImpl::CanSearchContent() {
   return web_state_->ContentIsHTML();
 }
 
-void FindInPageManagerImpl::ProcessFindInPageResult(
+void JavaScriptFindInPageManagerImpl::ProcessFindInPageResult(
     const std::string& frame_id,
     const int unique_id,
     absl::optional<int> result_matches) {
@@ -173,9 +182,9 @@
     // JavaScript. Call pumpSearch to continue find.
     if (result_matches.value() == find_in_page::kFindInPagePending) {
       FindInPageJavaScriptFeature::GetInstance()->Pump(
-          frame,
-          base::BindOnce(&FindInPageManagerImpl::ProcessFindInPageResult,
-                         weak_factory_.GetWeakPtr(), frame_id, unique_id));
+          frame, base::BindOnce(
+                     &JavaScriptFindInPageManagerImpl::ProcessFindInPageResult,
+                     weak_factory_.GetWeakPtr(), frame_id, unique_id));
       return;
     }
 
@@ -187,7 +196,7 @@
   }
 }
 
-void FindInPageManagerImpl::LastFindRequestCompleted() {
+void JavaScriptFindInPageManagerImpl::LastFindRequestCompleted() {
   if (delegate_) {
     delegate_->DidHighlightMatches(web_state_,
                                    last_find_request_.GetTotalMatchCount(),
@@ -203,7 +212,8 @@
   }
 }
 
-void FindInPageManagerImpl::SelectDidFinish(const base::Value* result) {
+void JavaScriptFindInPageManagerImpl::SelectDidFinish(
+    const base::Value* result) {
   std::string match_context_string;
   if (result && result->is_dict()) {
     // Get updated match count.
@@ -240,31 +250,31 @@
   }
 }
 
-void FindInPageManagerImpl::SelectNextMatch() {
+void JavaScriptFindInPageManagerImpl::SelectNextMatch() {
   base::RecordAction(base::UserMetricsAction(kFindNextActionName));
   if (last_find_request_.GoToNextMatch()) {
     SelectCurrentMatch();
   }
 }
 
-void FindInPageManagerImpl::SelectPreviousMatch() {
+void JavaScriptFindInPageManagerImpl::SelectPreviousMatch() {
   base::RecordAction(base::UserMetricsAction(kFindPreviousActionName));
   if (last_find_request_.GoToPreviousMatch()) {
     SelectCurrentMatch();
   }
 }
 
-void FindInPageManagerImpl::SelectCurrentMatch() {
+void JavaScriptFindInPageManagerImpl::SelectCurrentMatch() {
   web::WebFrame* frame =
       GetWebFrameWithId(web_state_, last_find_request_.GetSelectedFrameId());
   if (frame) {
     FindInPageJavaScriptFeature::GetInstance()->SelectMatch(
         frame, last_find_request_.GetCurrentSelectedMatchFrameIndex(),
-        base::BindOnce(&FindInPageManagerImpl::SelectDidFinish,
+        base::BindOnce(&JavaScriptFindInPageManagerImpl::SelectDidFinish,
                        weak_factory_.GetWeakPtr()));
   }
 }
 
-WEB_STATE_USER_DATA_KEY_IMPL(FindInPageManager)
+WEB_STATE_USER_DATA_KEY_IMPL(JavaScriptFindInPageManager)
 
 }  // namespace web
diff --git a/ios/web/find_in_page/find_in_page_manger_impl_unittest.mm b/ios/web/find_in_page/java_script_find_in_page_manager_impl_unittest.mm
similarity index 92%
rename from ios/web/find_in_page/find_in_page_manger_impl_unittest.mm
rename to ios/web/find_in_page/java_script_find_in_page_manager_impl_unittest.mm
index 5e73439f..c4bdecd 100644
--- a/ios/web/find_in_page/find_in_page_manger_impl_unittest.mm
+++ b/ios/web/find_in_page/java_script_find_in_page_manager_impl_unittest.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/web/find_in_page/find_in_page_manager_impl.h"
+#import "ios/web/find_in_page/java_script_find_in_page_manager_impl.h"
 
 #import "base/run_loop.h"
 #import "base/test/ios/wait_util.h"
@@ -29,11 +29,12 @@
 
 namespace web {
 
-// Tests FindInPageManagerImpl and verifies that the state of
+// Tests JavaScriptFindInPageManagerImpl and verifies that the state of
 // FindInPageManagerDelegate is correct depending on what web frames return.
-class FindInPageManagerImplTest : public WebTest {
+class JavaScriptFindInPageManagerImplTest : public WebTest {
  protected:
-  FindInPageManagerImplTest() : WebTest(std::make_unique<FakeWebClient>()) {}
+  JavaScriptFindInPageManagerImplTest()
+      : WebTest(std::make_unique<FakeWebClient>()) {}
 
   void SetUp() override {
     WebTest::SetUp();
@@ -46,13 +47,13 @@
 
     JavaScriptFeatureManager::FromBrowserState(GetBrowserState())
         ->ConfigureFeatures({FindInPageJavaScriptFeature::GetInstance()});
-    FindInPageManagerImpl::CreateForWebState(fake_web_state_.get());
+    JavaScriptFindInPageManagerImpl::CreateForWebState(fake_web_state_.get());
     GetFindInPageManager()->SetDelegate(&fake_delegate_);
   }
 
-  // Returns the FindInPageManager associated with `fake_web_state_`.
-  FindInPageManager* GetFindInPageManager() {
-    return FindInPageManager::FromWebState(fake_web_state_.get());
+  // Returns the JavaScriptFindInPageManager associated with `fake_web_state_`.
+  JavaScriptFindInPageManager* GetFindInPageManager() {
+    return JavaScriptFindInPageManager::FromWebState(fake_web_state_.get());
   }
 
   // Returns a fake WebFrame that represents the main frame which will return
@@ -95,7 +96,7 @@
 
 // Tests that Find In Page responds with a total match count of three when a
 // frame has one match and another frame has two matches.
-TEST_F(FindInPageManagerImplTest, FindMatchesMultipleFrames) {
+TEST_F(JavaScriptFindInPageManagerImplTest, FindMatchesMultipleFrames) {
   auto one = std::make_unique<base::Value>(1.0);
   auto two = std::make_unique<base::Value>(2.0);
   auto frame_with_one_match = CreateMainWebFrameWithJsResultForFind(one.get());
@@ -125,7 +126,7 @@
 // Tests that Find In Page responds with a total match count of one when a frame
 // has one match but find in one frame was cancelled. This can occur if the
 // frame becomes unavailable.
-TEST_F(FindInPageManagerImplTest, FrameCancelFind) {
+TEST_F(JavaScriptFindInPageManagerImplTest, FrameCancelFind) {
   auto null = std::make_unique<base::Value>();
   auto one = std::make_unique<base::Value>(1.0);
   auto frame_with_null_result =
@@ -154,7 +155,7 @@
 
 // Tests that Find In Page returns a total match count matching the latest find
 // if two finds are called.
-TEST_F(FindInPageManagerImplTest, ReturnLatestFind) {
+TEST_F(JavaScriptFindInPageManagerImplTest, ReturnLatestFind) {
   auto one = std::make_unique<base::Value>(1.0);
   auto two = std::make_unique<base::Value>(2.0);
   auto frame_with_one_match = CreateMainWebFrameWithJsResultForFind(one.get());
@@ -190,7 +191,7 @@
 
 // Tests that Find In Page should not return if the web state is destroyed
 // during a find.
-TEST_F(FindInPageManagerImplTest, DestroyWebStateDuringFind) {
+TEST_F(JavaScriptFindInPageManagerImplTest, DestroyWebStateDuringFind) {
   auto one = std::make_unique<base::Value>(1.0);
   auto frame_with_one_match = CreateMainWebFrameWithJsResultForFind(one.get());
   AddWebFrame(std::move(frame_with_one_match));
@@ -204,7 +205,8 @@
 
 // Tests that Find In Page updates total match count when a frame with matches
 // becomes unavailable during find.
-TEST_F(FindInPageManagerImplTest, FrameUnavailableAfterDelegateCallback) {
+TEST_F(JavaScriptFindInPageManagerImplTest,
+       FrameUnavailableAfterDelegateCallback) {
   auto one = std::make_unique<base::Value>(1.0);
   auto two = std::make_unique<base::Value>(2.0);
   auto frame_with_one_match = CreateMainWebFrameWithJsResultForFind(one.get());
@@ -237,7 +239,7 @@
 
 // Tests that Find In Page returns with the right match count for a frame with
 // one match and another that requires pumping to return its two matches.
-TEST_F(FindInPageManagerImplTest, FrameRespondsWithPending) {
+TEST_F(JavaScriptFindInPageManagerImplTest, FrameRespondsWithPending) {
   auto negative_one = std::make_unique<base::Value>(-1.0);
   auto one = std::make_unique<base::Value>(1.0);
   auto two = std::make_unique<base::Value>(2.0);
@@ -271,7 +273,7 @@
 }
 
 // Tests that Find In Page doesn't fail when delegate is not set.
-TEST_F(FindInPageManagerImplTest, DelegateNotSet) {
+TEST_F(JavaScriptFindInPageManagerImplTest, DelegateNotSet) {
   GetFindInPageManager()->SetDelegate(nullptr);
   auto one = std::make_unique<base::Value>(1.0);
   auto frame_with_one_match = CreateMainWebFrameWithJsResultForFind(one.get());
@@ -286,7 +288,7 @@
 }
 
 // Tests that Find In Page returns no matches if can't call JavaScript function.
-TEST_F(FindInPageManagerImplTest, FrameCannotCallJavaScriptFunction) {
+TEST_F(JavaScriptFindInPageManagerImplTest, FrameCannotCallJavaScriptFunction) {
   auto one = std::make_unique<base::Value>(1.0);
   auto frame_cannot_call_func =
       CreateMainWebFrameWithJsResultForFind(one.get());
@@ -304,7 +306,7 @@
 
 // Tests that  Find In Page responds with a total match count of zero when there
 // are no known webpage frames.
-TEST_F(FindInPageManagerImplTest, NoFrames) {
+TEST_F(JavaScriptFindInPageManagerImplTest, NoFrames) {
   GetFindInPageManager()->Find(@"foo", FindInPageOptions::FindInPageSearch);
 
   ASSERT_TRUE(WaitUntilConditionOrTimeout(kWaitForJSCompletionTimeout, ^bool {
@@ -317,7 +319,7 @@
 // Tests that Find in Page responds with a total match count of zero when there
 // are no matches in the only frame. Tests that Find in Page also did not
 // respond with an selected match index value.
-TEST_F(FindInPageManagerImplTest, FrameWithNoMatchNoHighlight) {
+TEST_F(JavaScriptFindInPageManagerImplTest, FrameWithNoMatchNoHighlight) {
   auto zero = std::make_unique<base::Value>(0.0);
   auto frame_with_zero_matches =
       CreateMainWebFrameWithJsResultForFind(zero.get());
@@ -340,7 +342,7 @@
 
 // Tests that Find in Page responds with index zero after a find when there are
 // two matches in a frame.
-TEST_F(FindInPageManagerImplTest, DidHighlightFirstIndex) {
+TEST_F(JavaScriptFindInPageManagerImplTest, DidHighlightFirstIndex) {
   auto two = std::make_unique<base::Value>(2.0);
   auto frame_with_two_matches =
       CreateMainWebFrameWithJsResultForFind(two.get());
@@ -363,7 +365,8 @@
 
 // Tests that Find in Page responds with index one to a FindInPageNext find
 // after a FindInPageSearch find finishes when there are two matches in a frame.
-TEST_F(FindInPageManagerImplTest, FindDidHighlightSecondIndexAfterNextCall) {
+TEST_F(JavaScriptFindInPageManagerImplTest,
+       FindDidHighlightSecondIndexAfterNextCall) {
   auto two = std::make_unique<base::Value>(2.0);
   auto frame_with_two_matches =
       CreateMainWebFrameWithJsResultForFind(two.get());
@@ -396,7 +399,8 @@
 // Tests that Find in Page selects all matches in a page with one frame with one
 // match and another with two matches when making successive FindInPageNext
 // calls.
-TEST_F(FindInPageManagerImplTest, FindDidSelectAllMatchesWithNextCall) {
+TEST_F(JavaScriptFindInPageManagerImplTest,
+       FindDidSelectAllMatchesWithNextCall) {
   auto one = std::make_unique<base::Value>(1.0);
   auto two = std::make_unique<base::Value>(2.0);
   auto frame_with_one_match = CreateMainWebFrameWithJsResultForFind(one.get());
@@ -448,7 +452,7 @@
 // Tests that Find in Page selects all matches in a page with one frame with one
 // match and another with two matches when making successive FindInPagePrevious
 // calls.
-TEST_F(FindInPageManagerImplTest,
+TEST_F(JavaScriptFindInPageManagerImplTest,
        FindDidLoopThroughAllMatchesWithPreviousCall) {
   auto one = std::make_unique<base::Value>(1.0);
   auto two = std::make_unique<base::Value>(2.0);
@@ -504,7 +508,8 @@
 // Tests that Find in Page responds with index two to a FindInPagePrevious find
 // after a FindInPageSearch find finishes when there are two matches in a
 // frame and one match in another.
-TEST_F(FindInPageManagerImplTest, FindDidHighlightLastIndexAfterPreviousCall) {
+TEST_F(JavaScriptFindInPageManagerImplTest,
+       FindDidHighlightLastIndexAfterPreviousCall) {
   auto one = std::make_unique<base::Value>(1.0);
   auto two = std::make_unique<base::Value>(2.0);
   auto frame_with_one_match = CreateMainWebFrameWithJsResultForFind(one.get());
@@ -541,7 +546,8 @@
 
 // Tests that Find in Page does not respond to a FindInPageNext or a
 // FindInPagePrevious call if no FindInPageSearch find was executed beforehand.
-TEST_F(FindInPageManagerImplTest, FindDidNotRepondToNextOrPrevIfNoSearch) {
+TEST_F(JavaScriptFindInPageManagerImplTest,
+       FindDidNotRepondToNextOrPrevIfNoSearch) {
   auto three = std::make_unique<base::Value>(3.0);
   auto frame_with_three_matches =
       CreateMainWebFrameWithJsResultForFind(three.get());
@@ -561,7 +567,7 @@
 // Tests that Find in Page responds with index one for a successive
 // FindInPageNext after the frame containing the currently selected match is
 // removed.
-TEST_F(FindInPageManagerImplTest,
+TEST_F(JavaScriptFindInPageManagerImplTest,
        FindDidHighlightNextMatchAfterFrameDisappears) {
   auto one = std::make_unique<base::Value>(1.0);
   auto two = std::make_unique<base::Value>(2.0);
@@ -595,7 +601,7 @@
 }
 
 // Tests that Find in Page does not respond when frame is removed
-TEST_F(FindInPageManagerImplTest, FindDidNotRepondAfterFrameRemoved) {
+TEST_F(JavaScriptFindInPageManagerImplTest, FindDidNotRepondAfterFrameRemoved) {
   auto one = std::make_unique<base::Value>(1.0);
   auto frame_with_one_match = CreateMainWebFrameWithJsResultForFind(one.get());
   AddWebFrame(std::move(frame_with_one_match));
@@ -609,7 +615,8 @@
 // Tests that Find in Page responds with a total match count of one to a
 // FindInPageSearch find when there is one match in a frame and then responds
 // with a total match count of zero when that frame is removed.
-TEST_F(FindInPageManagerImplTest, FindInPageUpdateMatchCountAfterFrameRemoved) {
+TEST_F(JavaScriptFindInPageManagerImplTest,
+       FindInPageUpdateMatchCountAfterFrameRemoved) {
   auto one = std::make_unique<base::Value>(1.0);
   auto frame_with_one_match = CreateMainWebFrameWithJsResultForFind(one.get());
   AddWebFrame(std::move(frame_with_one_match));
@@ -630,7 +637,8 @@
 
 // Tests that DidHighlightMatches is not called when a frame with no matches is
 // removed from the page.
-TEST_F(FindInPageManagerImplTest, FindDidNotResponseAfterFrameDisappears) {
+TEST_F(JavaScriptFindInPageManagerImplTest,
+       FindDidNotResponseAfterFrameDisappears) {
   auto zero = std::make_unique<base::Value>(0.0);
   auto two = std::make_unique<base::Value>(2.0);
   auto frame_with_zero_matches =
@@ -654,7 +662,7 @@
 
 // Tests that Find in Page SetContentIsHTML() returns true if the web state's
 // content is HTML and returns false if the web state's content is not HTML.
-TEST_F(FindInPageManagerImplTest, FindInPageCanSearchContent) {
+TEST_F(JavaScriptFindInPageManagerImplTest, FindInPageCanSearchContent) {
   fake_web_state_->SetContentIsHTML(false);
 
   EXPECT_FALSE(GetFindInPageManager()->CanSearchContent());
@@ -666,7 +674,7 @@
 
 // Tests that Find in Page resets the match count to 0 and the query to nil
 // after calling StopFinding().
-TEST_F(FindInPageManagerImplTest, FindInPageCanStopFind) {
+TEST_F(JavaScriptFindInPageManagerImplTest, FindInPageCanStopFind) {
   auto one = std::make_unique<base::Value>(1.0);
   auto frame_with_one_match = CreateMainWebFrameWithJsResultForFind(one.get());
   AddWebFrame(std::move(frame_with_one_match));
@@ -689,7 +697,7 @@
 // FindInPageNext after the visible match count in a frame changes following a
 // FindInPageSearch. This simulates a once hidden match becoming visible between
 // a FindInPageSearch and a FindInPageNext.
-TEST_F(FindInPageManagerImplTest, FindInPageNextUpdatesMatchCount) {
+TEST_F(JavaScriptFindInPageManagerImplTest, FindInPageNextUpdatesMatchCount) {
   auto two = std::make_unique<base::Value>(2.0);
   auto frame_with_hidden_match =
       CreateMainWebFrameWithJsResultForFind(two.get());
@@ -718,7 +726,7 @@
 }
 
 // Tests that Find in Page logs correct UserActions for given API calls.
-TEST_F(FindInPageManagerImplTest, FindUserActions) {
+TEST_F(JavaScriptFindInPageManagerImplTest, FindUserActions) {
   ASSERT_EQ(0, user_action_tester_.GetActionCount(kFindActionName));
   GetFindInPageManager()->Find(@"foo", FindInPageOptions::FindInPageSearch);
   EXPECT_EQ(1, user_action_tester_.GetActionCount(kFindActionName));
diff --git a/ios/web/find_in_page/find_in_page_manager_inttest.mm b/ios/web/find_in_page/java_script_find_in_page_manager_inttest.mm
similarity index 89%
rename from ios/web/find_in_page/find_in_page_manager_inttest.mm
rename to ios/web/find_in_page/java_script_find_in_page_manager_inttest.mm
index 9de9a8fe..0512be2 100644
--- a/ios/web/find_in_page/find_in_page_manager_inttest.mm
+++ b/ios/web/find_in_page/java_script_find_in_page_manager_inttest.mm
@@ -6,7 +6,7 @@
 #import "base/test/ios/wait_util.h"
 #import "ios/testing/embedded_test_server_handlers.h"
 #import "ios/web/find_in_page/find_in_page_java_script_feature.h"
-#import "ios/web/public/find_in_page/find_in_page_manager.h"
+#import "ios/web/public/find_in_page/java_script_find_in_page_manager.h"
 #import "ios/web/public/js_messaging/web_frames_manager.h"
 #import "ios/web/public/test/fakes/fake_find_in_page_manager_delegate.h"
 #import "ios/web/public/test/fakes/fake_web_client.h"
@@ -21,8 +21,8 @@
 #endif
 
 using base::test::ios::kWaitForJSCompletionTimeout;
-using base::test::ios::WaitUntilConditionOrTimeout;
 using base::test::ios::kWaitForPageLoadTimeout;
+using base::test::ios::WaitUntilConditionOrTimeout;
 
 namespace {
 // Page with text "Main frame body" and iframe with src URL equal to the URL
@@ -30,15 +30,14 @@
 const char kFindPageUrl[] = "/iframe?";
 // URL of iframe with text contents "iframe iframe text".
 const char kFindInPageIFrameUrl[] = "/echo-query?iframe iframe text";
-}
+}  // namespace
 
 namespace web {
 
-// Tests the FindInPageManager and verifies that values passed to
+// Tests the JavaScriptFindInPageManager and verifies that values passed to
 // FindInPageManagerDelegate are correct.
-class FindInPageManagerTest : public WebTestWithWebState {
+class JavaScriptFindInPageManagerTest : public WebTestWithWebState {
  protected:
-
   void SetUp() override {
     WebTestWithWebState::SetUp();
 
@@ -52,9 +51,9 @@
     GetFindInPageManager()->SetDelegate(&delegate_);
   }
 
-  // Returns the FindInPageManager associated with `web_state()`.
-  FindInPageManager* GetFindInPageManager() {
-    return web::FindInPageManager::FromWebState(web_state());
+  // Returns the JavaScriptFindInPageManager associated with `web_state()`.
+  JavaScriptFindInPageManager* GetFindInPageManager() {
+    return web::JavaScriptFindInPageManager::FromWebState(web_state());
   }
 
   // Waits until the delegate receives `index` from
@@ -74,7 +73,7 @@
 
 // Tests that find in page returns a single match for text which exists only in
 // the main frame.
-TEST_F(FindInPageManagerTest, FindMatchInMainFrame) {
+TEST_F(JavaScriptFindInPageManagerTest, FindMatchInMainFrame) {
   std::string url_spec =
       kFindPageUrl +
       base::EscapeQueryParamValue(kFindInPageIFrameUrl, /*use_plus=*/true);
@@ -96,7 +95,7 @@
 
 // Checks that find in page finds text that exists within the main frame and
 // an iframe.
-TEST_F(FindInPageManagerTest, FindMatchInMainFrameAndIFrame) {
+TEST_F(JavaScriptFindInPageManagerTest, FindMatchInMainFrameAndIFrame) {
   std::string url_spec =
       kFindPageUrl +
       base::EscapeQueryParamValue(kFindInPageIFrameUrl, /*use_plus=*/true);
@@ -116,7 +115,7 @@
 
 // Checks that find in page returns no matches for text not contained on the
 // page.
-TEST_F(FindInPageManagerTest, FindNoMatch) {
+TEST_F(JavaScriptFindInPageManagerTest, FindNoMatch) {
   std::string url_spec =
       kFindPageUrl +
       base::EscapeQueryParamValue(kFindInPageIFrameUrl, /*use_plus=*/true);
@@ -136,7 +135,7 @@
 
 // Tests FindInPageNext iteration when matches exist in both the main frame and
 // an iframe.
-TEST_F(FindInPageManagerTest, FindForwardIterateThroughAllMatches) {
+TEST_F(JavaScriptFindInPageManagerTest, FindForwardIterateThroughAllMatches) {
   std::string url_spec =
       kFindPageUrl +
       base::EscapeQueryParamValue(kFindInPageIFrameUrl, /*use_plus=*/true);
@@ -165,7 +164,7 @@
 
 // Tests FindInPagePrevious iteration when matches exist in both the main frame
 // and an iframe.
-TEST_F(FindInPageManagerTest, FindBackwardsIterateThroughAllMatches) {
+TEST_F(JavaScriptFindInPageManagerTest, FindBackwardsIterateThroughAllMatches) {
   std::string url_spec =
       kFindPageUrl +
       base::EscapeQueryParamValue(kFindInPageIFrameUrl, /*use_plus=*/true);
@@ -193,7 +192,7 @@
 }
 
 // Tests FindInPageNext iteration when matches exist in only an iframe.
-TEST_F(FindInPageManagerTest, FindIterateThroughIframeMatches) {
+TEST_F(JavaScriptFindInPageManagerTest, FindIterateThroughIframeMatches) {
   std::string url_spec =
       kFindPageUrl +
       base::EscapeQueryParamValue(kFindInPageIFrameUrl, /*use_plus=*/true);
@@ -218,7 +217,7 @@
 
 // Tests FindInPageNext and FindInPagePrevious iteration while passing null
 // query.
-TEST_F(FindInPageManagerTest, FindIterationWithNullQuery) {
+TEST_F(JavaScriptFindInPageManagerTest, FindIterationWithNullQuery) {
   std::string url_spec =
       kFindPageUrl +
       base::EscapeQueryParamValue(kFindInPageIFrameUrl, /*use_plus=*/true);
diff --git a/ios/web/find_in_page/find_in_page_request.h b/ios/web/find_in_page/java_script_find_in_page_request.h
similarity index 93%
rename from ios/web/find_in_page/find_in_page_request.h
rename to ios/web/find_in_page/java_script_find_in_page_request.h
index 0f15f19..16f80ae6 100644
--- a/ios/web/find_in_page/find_in_page_request.h
+++ b/ios/web/find_in_page/java_script_find_in_page_request.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef IOS_WEB_FIND_IN_PAGE_FIND_IN_PAGE_REQUEST_H_
-#define IOS_WEB_FIND_IN_PAGE_FIND_IN_PAGE_REQUEST_H_
+#ifndef IOS_WEB_FIND_IN_PAGE_JAVA_SCRIPT_FIND_IN_PAGE_REQUEST_H_
+#define IOS_WEB_FIND_IN_PAGE_JAVA_SCRIPT_FIND_IN_PAGE_REQUEST_H_
 
 #include <list>
 #include <map>
@@ -17,11 +17,11 @@
 
 class WebFrame;
 
-// Keeps track of the state of a FindInPageManager::Find() request.
-class FindInPageRequest {
+// Keeps track of the state of a JavaScriptFindInPageManager::Find() request.
+class JavaScriptFindInPageRequest {
  public:
-  FindInPageRequest();
-  ~FindInPageRequest();
+  JavaScriptFindInPageRequest();
+  ~JavaScriptFindInPageRequest();
   // Clears properties and sets new `query` and `pending_frame_call_count`.
   void Reset(NSString* query, int pending_frame_call_count);
   int GetTotalMatchCount() const;
diff --git a/ios/web/find_in_page/find_in_page_request.mm b/ios/web/find_in_page/java_script_find_in_page_request.mm
similarity index 75%
rename from ios/web/find_in_page/find_in_page_request.mm
rename to ios/web/find_in_page/java_script_find_in_page_request.mm
index 4f72416..4484552 100644
--- a/ios/web/find_in_page/find_in_page_request.mm
+++ b/ios/web/find_in_page/java_script_find_in_page_request.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/web/find_in_page/find_in_page_request.h"
+#import "ios/web/find_in_page/java_script_find_in_page_request.h"
 
 #import <Foundation/Foundation.h>
 
@@ -14,12 +14,12 @@
 
 namespace web {
 
-FindInPageRequest::FindInPageRequest() {}
+JavaScriptFindInPageRequest::JavaScriptFindInPageRequest() {}
 
-FindInPageRequest::~FindInPageRequest() {}
+JavaScriptFindInPageRequest::~JavaScriptFindInPageRequest() {}
 
-void FindInPageRequest::Reset(NSString* new_query_,
-                              int new_pending_frame_call_count) {
+void JavaScriptFindInPageRequest::Reset(NSString* new_query_,
+                                        int new_pending_frame_call_count) {
   unique_id_++;
   selected_frame_id_ = frame_order_.end();
   selected_match_index_in_selected_frame_ = -1;
@@ -30,7 +30,7 @@
   }
 }
 
-int FindInPageRequest::GetTotalMatchCount() const {
+int JavaScriptFindInPageRequest::GetTotalMatchCount() const {
   int matches = 0;
   for (auto pair : frame_match_count_) {
     matches += pair.second;
@@ -38,14 +38,14 @@
   return matches;
 }
 
-int FindInPageRequest::GetRequestId() const {
+int JavaScriptFindInPageRequest::GetRequestId() const {
   return unique_id_;
 }
-NSString* FindInPageRequest::GetRequestQuery() const {
+NSString* JavaScriptFindInPageRequest::GetRequestQuery() const {
   return query_;
 }
 
-bool FindInPageRequest::GoToFirstMatch() {
+bool JavaScriptFindInPageRequest::GoToFirstMatch() {
   for (auto frame_id = frame_order_.begin(); frame_id != frame_order_.end();
        ++frame_id) {
     if (frame_match_count_[*frame_id] > 0) {
@@ -57,7 +57,7 @@
   return false;
 }
 
-bool FindInPageRequest::GoToNextMatch() {
+bool JavaScriptFindInPageRequest::GoToNextMatch() {
   if (GetTotalMatchCount() == 0) {
     return false;
   }
@@ -89,7 +89,7 @@
   return true;
 }
 
-bool FindInPageRequest::GoToPreviousMatch() {
+bool JavaScriptFindInPageRequest::GoToPreviousMatch() {
   if (GetTotalMatchCount() == 0) {
     return false;
   }
@@ -121,33 +121,36 @@
   return true;
 }
 
-int FindInPageRequest::GetMatchCountForFrame(const std::string& frame_id) {
+int JavaScriptFindInPageRequest::GetMatchCountForFrame(
+    const std::string& frame_id) {
   if (frame_match_count_.find(frame_id) == frame_match_count_.end()) {
     return -1;
   }
   return frame_match_count_[frame_id];
 }
 
-void FindInPageRequest::SetMatchCountForFrame(int match_count,
-                                              const std::string& frame_id) {
+void JavaScriptFindInPageRequest::SetMatchCountForFrame(
+    int match_count,
+    const std::string& frame_id) {
   frame_match_count_[frame_id] = match_count;
 }
 
-int FindInPageRequest::GetMatchCountForSelectedFrame() {
+int JavaScriptFindInPageRequest::GetMatchCountForSelectedFrame() {
   if (selected_frame_id_ == frame_order_.end()) {
     return -1;
   }
   return frame_match_count_[*selected_frame_id_];
 }
 
-void FindInPageRequest::SetMatchCountForSelectedFrame(int match_count) {
+void JavaScriptFindInPageRequest::SetMatchCountForSelectedFrame(
+    int match_count) {
   if (selected_frame_id_ == frame_order_.end()) {
     return;
   }
   frame_match_count_[*selected_frame_id_] = match_count;
 }
 
-int FindInPageRequest::GetCurrentSelectedMatchPageIndex() {
+int JavaScriptFindInPageRequest::GetCurrentSelectedMatchPageIndex() {
   if (selected_match_index_in_selected_frame_ == -1) {
     return -1;
   }
@@ -160,22 +163,22 @@
   return total_match_index;
 }
 
-std::string FindInPageRequest::GetSelectedFrameId() {
+std::string JavaScriptFindInPageRequest::GetSelectedFrameId() {
   if (selected_frame_id_ == frame_order_.end()) {
     return std::string();
   }
   return *selected_frame_id_;
 }
 
-int FindInPageRequest::GetCurrentSelectedMatchFrameIndex() const {
+int JavaScriptFindInPageRequest::GetCurrentSelectedMatchFrameIndex() const {
   return selected_match_index_in_selected_frame_;
 }
 
-void FindInPageRequest::SetCurrentSelectedMatchFrameIndex(int index) {
+void JavaScriptFindInPageRequest::SetCurrentSelectedMatchFrameIndex(int index) {
   selected_match_index_in_selected_frame_ = index;
 }
 
-void FindInPageRequest::RemoveFrame(const std::string& frame_id) {
+void JavaScriptFindInPageRequest::RemoveFrame(const std::string& frame_id) {
   if (IsSelectedFrame(frame_id)) {
     // If currently selecting match in frame that will become unavailable,
     // there will no longer be a selected match. Reset to unselected match
@@ -187,7 +190,7 @@
   frame_match_count_.erase(frame_id);
 }
 
-void FindInPageRequest::AddFrame(WebFrame* web_frame) {
+void JavaScriptFindInPageRequest::AddFrame(WebFrame* web_frame) {
   frame_match_count_[web_frame->GetFrameId()] = 0;
   if (web_frame->IsMainFrame()) {
     // Main frame matches should show up first.
@@ -198,15 +201,15 @@
   }
 }
 
-void FindInPageRequest::DidReceiveFindResponseFromOneFrame() {
+void JavaScriptFindInPageRequest::DidReceiveFindResponseFromOneFrame() {
   pending_frame_call_count_--;
 }
 
-bool FindInPageRequest::AreAllFindResponsesReturned() {
+bool JavaScriptFindInPageRequest::AreAllFindResponsesReturned() {
   return pending_frame_call_count_ == 0;
 }
 
-bool FindInPageRequest::IsSelectedFrame(const std::string& frame_id) {
+bool JavaScriptFindInPageRequest::IsSelectedFrame(const std::string& frame_id) {
   if (selected_frame_id_ == frame_order_.end()) {
     return false;
   }
diff --git a/ios/web/find_in_page/find_in_page_request_unittest.mm b/ios/web/find_in_page/java_script_find_in_page_request_unittest.mm
similarity index 67%
rename from ios/web/find_in_page/find_in_page_request_unittest.mm
rename to ios/web/find_in_page/java_script_find_in_page_request_unittest.mm
index 4234ef1..b697ac1 100644
--- a/ios/web/find_in_page/find_in_page_request_unittest.mm
+++ b/ios/web/find_in_page/java_script_find_in_page_request_unittest.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/web/find_in_page/find_in_page_request.h"
+#import "ios/web/find_in_page/java_script_find_in_page_request.h"
 
 #import "ios/web/public/test/fakes/fake_web_frame.h"
 #import "ios/web/public/test/web_test.h"
@@ -14,9 +14,9 @@
 
 namespace web {
 
-class FindInPageRequestTest : public WebTest {
+class JavaScriptFindInPageRequestTest : public WebTest {
  protected:
-  FindInPageRequestTest() {
+  JavaScriptFindInPageRequestTest() {
     auto main_frame = FakeWebFrame::CreateMainWebFrame(GURL::EmptyGURL());
     request_.AddFrame(main_frame.get());
     auto frame_with_two_matches =
@@ -26,12 +26,12 @@
     request_.SetMatchCountForFrame(1, kMainFakeFrameId);
     request_.SetMatchCountForFrame(2, kChildFakeFrameId);
   }
-  FindInPageRequest request_;
+  JavaScriptFindInPageRequest request_;
 };
 
-// Tests that FindInPageRequest properly clears its properties in respond to a
-// Reset() call.
-TEST_F(FindInPageRequestTest, Reset) {
+// Tests that JavaScriptFindInPageRequest properly clears its properties in
+// respond to a Reset() call.
+TEST_F(JavaScriptFindInPageRequestTest, Reset) {
   EXPECT_EQ(3, request_.GetTotalMatchCount());
 
   EXPECT_TRUE(request_.GoToFirstMatch());
@@ -49,9 +49,9 @@
   EXPECT_EQ(-1, request_.GetMatchCountForSelectedFrame());
 }
 
-// Tests that FindinPageRequest properly decrements `pending_frame_call_count_`
-// properly.
-TEST_F(FindInPageRequestTest, AllFindResponsesReturned) {
+// Tests that JavaScriptFindInPageRequest properly decrements
+// `pending_frame_call_count_` properly.
+TEST_F(JavaScriptFindInPageRequestTest, AllFindResponsesReturned) {
   request_.DidReceiveFindResponseFromOneFrame();
   EXPECT_FALSE(request_.AreAllFindResponsesReturned());
 
@@ -59,9 +59,9 @@
   EXPECT_TRUE(request_.AreAllFindResponsesReturned());
 }
 
-// Tests that FindInPageRequest GoToNextMatch() is able to traverse all matches
-// in multiple frames.
-TEST_F(FindInPageRequestTest, GoToNext) {
+// Tests that JavaScriptFindInPageRequest GoToNextMatch() is able to traverse
+// all matches in multiple frames.
+TEST_F(JavaScriptFindInPageRequestTest, GoToNext) {
   request_.GoToFirstMatch();
 
   EXPECT_EQ(0, request_.GetCurrentSelectedMatchFrameIndex());
@@ -83,9 +83,9 @@
   EXPECT_EQ(0, request_.GetCurrentSelectedMatchPageIndex());
 }
 
-// Tests that FindInPageRequest GoToPreviousMatch() is able to traverse all
-// matches in multiple frames.
-TEST_F(FindInPageRequestTest, GoToPrevious) {
+// Tests that JavaScriptFindInPageRequest GoToPreviousMatch() is able to
+// traverse all matches in multiple frames.
+TEST_F(JavaScriptFindInPageRequestTest, GoToPrevious) {
   request_.GoToFirstMatch();
 
   EXPECT_EQ(0, request_.GetCurrentSelectedMatchFrameIndex());
@@ -107,9 +107,10 @@
   EXPECT_EQ(0, request_.GetCurrentSelectedMatchPageIndex());
 }
 
-// Tests that FindInPageRequest returns the correct relative match count within
-// a frame and total match count when traversing matches in multiple frames.
-TEST_F(FindInPageRequestTest, RelativeMatchCount) {
+// Tests that JavaScriptFindInPageRequest returns the correct relative match
+// count within a frame and total match count when traversing matches in
+// multiple frames.
+TEST_F(JavaScriptFindInPageRequestTest, RelativeMatchCount) {
   request_.GoToFirstMatch();
 
   EXPECT_EQ(3, request_.GetTotalMatchCount());
@@ -121,11 +122,11 @@
   EXPECT_EQ(2, request_.GetMatchCountForSelectedFrame());
 }
 
-// Tests that FindInPageRequest returns the correct relative match count within
-// a frame and total match count when a frame is removed. Also tests that going
-// to the next match after removing the currently selected frame produces the
-// expected relative and total selected match index.
-TEST_F(FindInPageRequestTest, RemoveFrame) {
+// Tests that JavaScriptFindInPageRequest returns the correct relative match
+// count within a frame and total match count when a frame is removed. Also
+// tests that going to the next match after removing the currently selected
+// frame produces the expected relative and total selected match index.
+TEST_F(JavaScriptFindInPageRequestTest, RemoveFrame) {
   request_.GoToFirstMatch();
 
   EXPECT_EQ(3, request_.GetTotalMatchCount());
@@ -141,10 +142,10 @@
   EXPECT_EQ(0, request_.GetCurrentSelectedMatchPageIndex());
 }
 
-// Tests that FindInPageRequest returns the correct relative match count within
-// a frame and total match count when the match count for the currently selected
-// frame changes.
-TEST_F(FindInPageRequestTest, SetMatchCountForSelectedFrame) {
+// Tests that JavaScriptFindInPageRequest returns the correct relative match
+// count within a frame and total match count when the match count for the
+// currently selected frame changes.
+TEST_F(JavaScriptFindInPageRequestTest, SetMatchCountForSelectedFrame) {
   request_.GoToFirstMatch();
   request_.SetMatchCountForSelectedFrame(5);
 
@@ -152,10 +153,10 @@
   EXPECT_EQ(5, request_.GetMatchCountForSelectedFrame());
 }
 
-// Tests that FindInPageRequest returns the currently selected match index
-// relative to the frame and the total are correct when the total matches and
-// the relative match index change.
-TEST_F(FindInPageRequestTest, SetCurrentSelectedMatchIndex) {
+// Tests that JavaScriptFindInPageRequest returns the currently selected match
+// index relative to the frame and the total are correct when the total matches
+// and the relative match index change.
+TEST_F(JavaScriptFindInPageRequestTest, SetCurrentSelectedMatchIndex) {
   request_.GoToFirstMatch();
   request_.SetMatchCountForSelectedFrame(5);
   request_.SetCurrentSelectedMatchFrameIndex(1);
@@ -164,10 +165,10 @@
   EXPECT_EQ(1, request_.GetCurrentSelectedMatchPageIndex());
 }
 
-// Tests that FindInPageRequest returns the correct match count within
+// Tests that JavaScriptFindInPageRequest returns the correct match count within
 // a frame and total match count when the match count for a not currently
 // selected frame changes.
-TEST_F(FindInPageRequestTest, SetMatchCountForFrame) {
+TEST_F(JavaScriptFindInPageRequestTest, SetMatchCountForFrame) {
   request_.GoToFirstMatch();
 
   EXPECT_EQ(3, request_.GetTotalMatchCount());
diff --git a/ios/web/public/find_in_page/BUILD.gn b/ios/web/public/find_in_page/BUILD.gn
index 076fb4ea..c66d9f0 100644
--- a/ios/web/public/find_in_page/BUILD.gn
+++ b/ios/web/public/find_in_page/BUILD.gn
@@ -9,9 +9,9 @@
   ]
 
   sources = [
-    "find_in_page_manager.h",
     "find_in_page_manager_delegate.h",
     "find_in_page_manager_delegate_bridge.h",
+    "java_script_find_in_page_manager.h",
   ]
 
   configs += [ "//build/config/compiler:enable_arc" ]
diff --git a/ios/web/public/find_in_page/find_in_page_manager_delegate_bridge.h b/ios/web/public/find_in_page/find_in_page_manager_delegate_bridge.h
index c31183f..1e29cb5 100644
--- a/ios/web/public/find_in_page/find_in_page_manager_delegate_bridge.h
+++ b/ios/web/public/find_in_page/find_in_page_manager_delegate_bridge.h
@@ -10,7 +10,7 @@
 #import "ios/web/public/find_in_page/find_in_page_manager_delegate.h"
 
 namespace web {
-class FindInPageManager;
+class JavaScriptFindInPageManager;
 }
 
 // Objective-C interface for web::FindInPageManagerDelegate
@@ -22,7 +22,7 @@
 // Will also be called if the total match count in the current page changes.
 // Client should check `query` to ensure that it is processing `match_count`
 // for the correct find.
-- (void)findInPageManager:(web::FindInPageManager*)manager
+- (void)findInPageManager:(web::JavaScriptFindInPageManager*)manager
     didHighlightMatchesOfQuery:(NSString*)query
                 withMatchCount:(NSInteger)matchCount
                    forWebState:(web::WebState*)webState;
@@ -34,7 +34,7 @@
 // FindInPageManager::Find() with any FindInPageOptions to indicate the new
 // match number that was selected. This method is not called if
 // `FindInPageManager::Find` did not find any matches.
-- (void)findInPageManager:(web::FindInPageManager*)manager
+- (void)findInPageManager:(web::JavaScriptFindInPageManager*)manager
     didSelectMatchAtIndex:(NSInteger)index
         withContextString:(NSString*)contextString
               forWebState:(web::WebState*)webState;
diff --git a/ios/web/public/find_in_page/find_in_page_manager.h b/ios/web/public/find_in_page/java_script_find_in_page_manager.h
similarity index 83%
rename from ios/web/public/find_in_page/find_in_page_manager.h
rename to ios/web/public/find_in_page/java_script_find_in_page_manager.h
index dd21923..dd2ee77 100644
--- a/ios/web/public/find_in_page/find_in_page_manager.h
+++ b/ios/web/public/find_in_page/java_script_find_in_page_manager.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef IOS_WEB_PUBLIC_FIND_IN_PAGE_FIND_IN_PAGE_MANAGER_H_
-#define IOS_WEB_PUBLIC_FIND_IN_PAGE_FIND_IN_PAGE_MANAGER_H_
+#ifndef IOS_WEB_PUBLIC_FIND_IN_PAGE_JAVA_SCRIPT_FIND_IN_PAGE_MANAGER_H_
+#define IOS_WEB_PUBLIC_FIND_IN_PAGE_JAVA_SCRIPT_FIND_IN_PAGE_MANAGER_H_
 
 #import "ios/web/public/web_state_user_data.h"
 
@@ -30,7 +30,8 @@
 };
 
 // Manager for searching text on a page. Supports searching within all iframes.
-class FindInPageManager : public web::WebStateUserData<FindInPageManager> {
+class JavaScriptFindInPageManager
+    : public web::WebStateUserData<JavaScriptFindInPageManager> {
  public:
   // Searches for string `query`. Executes new search or traverses results based
   // on `options`. `query` must not be null if `options` is `FindInPageSearch`.
@@ -56,15 +57,16 @@
   virtual FindInPageManagerDelegate* GetDelegate() = 0;
   virtual void SetDelegate(FindInPageManagerDelegate* delegate) = 0;
 
-  FindInPageManager(const FindInPageManager&) = delete;
-  FindInPageManager& operator=(const FindInPageManager&) = delete;
+  JavaScriptFindInPageManager(const JavaScriptFindInPageManager&) = delete;
+  JavaScriptFindInPageManager& operator=(const JavaScriptFindInPageManager&) =
+      delete;
 
-  ~FindInPageManager() override {}
+  ~JavaScriptFindInPageManager() override {}
 
   WEB_STATE_USER_DATA_KEY_DECL();
 
  protected:
-  FindInPageManager() {}
+  JavaScriptFindInPageManager() {}
 };
 
 }  // namespace web
diff --git a/ios/web/public/ui/crw_web_view_proxy.h b/ios/web/public/ui/crw_web_view_proxy.h
index b1a1fbd..91d6000 100644
--- a/ios/web/public/ui/crw_web_view_proxy.h
+++ b/ios/web/public/ui/crw_web_view_proxy.h
@@ -44,6 +44,9 @@
 // Returns the webview's gesture recognizers.
 @property(nonatomic, readonly) NSArray* gestureRecognizers;
 
+// A Boolean value indicating whether or not the web page is in fullscreen mode.
+@property(nonatomic, readonly) BOOL isWebPageInFullscreenMode;
+
 // Adds a webview gesture recognizers.
 - (void)addGestureRecognizer:(UIGestureRecognizer*)gestureRecognizer;
 
diff --git a/ios/web/web_state/ui/crw_web_controller.h b/ios/web/web_state/ui/crw_web_controller.h
index ad70cdc..b92ab54f 100644
--- a/ios/web/web_state/ui/crw_web_controller.h
+++ b/ios/web/web_state/ui/crw_web_controller.h
@@ -92,6 +92,10 @@
 @property(nonatomic, assign, getter=shouldKeepRenderProcessAlive)
     BOOL keepsRenderProcessAlive;
 
+// Whether or not the web page is in fullscreen mode.
+@property(nonatomic, readonly, getter=isWebPageInFullscreenMode)
+    BOOL webPageInFullscreenMode;
+
 // Designated initializer. Initializes web controller with `webState`. The
 // calling code must retain the ownership of `webState`.
 - (instancetype)initWithWebState:(web::WebStateImpl*)webState;
diff --git a/ios/web/web_state/ui/crw_web_controller.mm b/ios/web/web_state/ui/crw_web_controller.mm
index 17f10a0ea..48352f9 100644
--- a/ios/web/web_state/ui/crw_web_controller.mm
+++ b/ios/web/web_state/ui/crw_web_controller.mm
@@ -25,7 +25,7 @@
 #import "ios/web/common/uikit_ui_util.h"
 #import "ios/web/common/url_util.h"
 #import "ios/web/download/crw_web_view_download.h"
-#import "ios/web/find_in_page/find_in_page_manager_impl.h"
+#import "ios/web/find_in_page/java_script_find_in_page_manager_impl.h"
 #import "ios/web/history_state_util.h"
 #import "ios/web/js_features/scroll_helper/scroll_helper_java_script_feature.h"
 #import "ios/web/js_messaging/crw_js_window_id_manager.h"
@@ -293,7 +293,7 @@
     web::BrowserState* browserState = _webStateImpl->GetBrowserState();
     _certVerificationController = [[CRWCertVerificationController alloc]
         initWithBrowserState:browserState];
-    web::FindInPageManagerImpl::CreateForWebState(_webStateImpl);
+    web::JavaScriptFindInPageManagerImpl::CreateForWebState(_webStateImpl);
     web::TextFragmentsManagerImpl::CreateForWebState(_webStateImpl);
 
     if (base::FeatureList::IsEnabled(
@@ -1918,6 +1918,10 @@
     CrFullscreenState fullScreenState =
         CrFullscreenStateFromWKFullscreenState(self.webView.fullscreenState);
     [_containerView updateWebViewContentViewFullscreenState:fullScreenState];
+    // Update state for `fullscreenModeOn` so that we can expose the current
+    // status of fullscreen mode through different interfaces.
+    _webPageInFullscreenMode =
+        fullScreenState == CrFullscreenState::kInFullscreen;
     base::UmaHistogramEnumeration(kFullScreenStateHistogram, fullScreenState);
   }
 #endif  // defined (__IPHONE_16_0)
diff --git a/ios/web/web_state/ui/crw_web_view_proxy_impl.mm b/ios/web/web_state/ui/crw_web_view_proxy_impl.mm
index db97fe2..534e9373 100644
--- a/ios/web/web_state/ui/crw_web_view_proxy_impl.mm
+++ b/ios/web/web_state/ui/crw_web_view_proxy_impl.mm
@@ -207,4 +207,8 @@
   [_webController showMenuWithItems:items rect:rect];
 }
 
+- (BOOL)isWebPageInFullscreenMode {
+  return [_webController isWebPageInFullscreenMode];
+}
+
 @end
diff --git a/pdf/pdf_engine.h b/pdf/pdf_engine.h
index d7fc9b7..aaec6c19 100644
--- a/pdf/pdf_engine.h
+++ b/pdf/pdf_engine.h
@@ -307,7 +307,7 @@
       const std::vector<int>& page_index,
       const blink::WebPrintParams& print_params) = 0;
   virtual void PrintEnd() = 0;
-  virtual void StartFind(const std::string& text, bool case_sensitive) = 0;
+  virtual void StartFind(const std::u16string& text, bool case_sensitive) = 0;
   virtual bool SelectFindResult(bool forward) = 0;
   virtual void StopFind() = 0;
   virtual void ZoomUpdated(double new_zoom_level) = 0;
diff --git a/pdf/pdf_view_web_plugin.cc b/pdf/pdf_view_web_plugin.cc
index f6ea2bb4..8ce806e 100644
--- a/pdf/pdf_view_web_plugin.cc
+++ b/pdf/pdf_view_web_plugin.cc
@@ -693,7 +693,7 @@
                                  int identifier) {
   ResetRecentlySentFindUpdate();
   find_identifier_ = identifier;
-  engine_->StartFind(search_text.Utf8(), case_sensitive);
+  engine_->StartFind(search_text.Utf16(), case_sensitive);
   return true;
 }
 
diff --git a/pdf/pdfium/findtext_unittest.cc b/pdf/pdfium/findtext_unittest.cc
index 8961349e..853d673 100644
--- a/pdf/pdfium/findtext_unittest.cc
+++ b/pdf/pdfium/findtext_unittest.cc
@@ -92,7 +92,7 @@
   ASSERT_TRUE(engine);
 
   ExpectInitialSearchResults(client, 10);
-  engine->StartFind("o", /*case_sensitive=*/true);
+  engine->StartFind(u"o", /*case_sensitive=*/true);
 }
 
 TEST_P(FindTextTest, FindHyphenatedText) {
@@ -102,7 +102,7 @@
   ASSERT_TRUE(engine);
 
   ExpectInitialSearchResults(client, 6);
-  engine->StartFind("application", /*case_sensitive=*/true);
+  engine->StartFind(u"application", /*case_sensitive=*/true);
 }
 
 TEST_P(FindTextTest, FindLineBreakText) {
@@ -112,7 +112,7 @@
   ASSERT_TRUE(engine);
 
   ExpectInitialSearchResults(client, 1);
-  engine->StartFind("is the first system", /*case_sensitive=*/true);
+  engine->StartFind(u"is the first system", /*case_sensitive=*/true);
 }
 
 TEST_P(FindTextTest, FindSimpleQuotationMarkText) {
@@ -122,7 +122,7 @@
   ASSERT_TRUE(engine);
 
   ExpectInitialSearchResults(client, 2);
-  engine->StartFind("don't", /*case_sensitive=*/true);
+  engine->StartFind(u"don't", /*case_sensitive=*/true);
 }
 
 TEST_P(FindTextTest, FindFancyQuotationMarkText) {
@@ -134,8 +134,7 @@
   ExpectInitialSearchResults(client, 2);
 
   // don't, using right apostrophe instead of a single quotation mark
-  std::u16string term = {'d', 'o', 'n', 0x2019, 't'};
-  engine->StartFind(base::UTF16ToUTF8(term), /*case_sensitive=*/true);
+  engine->StartFind(u"don\u2019t", /*case_sensitive=*/true);
 }
 
 TEST_P(FindTextTest, FindHiddenCroppedText) {
@@ -146,7 +145,7 @@
 
   // The word "Hello" is cropped out.
   ExpectInitialSearchResults(client, 0);
-  engine->StartFind("Hello", /*case_sensitive=*/true);
+  engine->StartFind(u"Hello", /*case_sensitive=*/true);
 }
 
 TEST_P(FindTextTest, FindVisibleCroppedText) {
@@ -157,7 +156,7 @@
 
   // Only one instance of the word "world" is visible. The other is cropped out.
   ExpectInitialSearchResults(client, 1);
-  engine->StartFind("world", /*case_sensitive=*/true);
+  engine->StartFind(u"world", /*case_sensitive=*/true);
 }
 
 TEST_P(FindTextTest, FindVisibleCroppedTextRepeatedly) {
@@ -169,9 +168,9 @@
   // Only one instance of the word "world" is visible. The other is cropped out.
   // These 2 find operations should not trigger https://crbug.com/1344057.
   ExpectInitialSearchResults(client, 1);
-  engine->StartFind("worl", /*case_sensitive=*/true);
+  engine->StartFind(u"worl", /*case_sensitive=*/true);
   ExpectInitialSearchResults(client, 1);
-  engine->StartFind("world", /*case_sensitive=*/true);
+  engine->StartFind(u"world", /*case_sensitive=*/true);
 }
 
 TEST_P(FindTextTest, SelectFindResult) {
@@ -181,7 +180,7 @@
   ASSERT_TRUE(engine);
 
   ExpectInitialSearchResults(client, 4);
-  engine->StartFind("world", /*case_sensitive=*/true);
+  engine->StartFind(u"world", /*case_sensitive=*/true);
   ASSERT_TRUE(engine->SelectFindResult(/*forward=*/true));
 
   EXPECT_CALL(client, NotifyNumberOfFindResultsChanged(_, _)).Times(0);
@@ -206,7 +205,7 @@
   ASSERT_TRUE(engine);
 
   ExpectInitialSearchResults(client, 4);
-  engine->StartFind("world", /*case_sensitive=*/false);
+  engine->StartFind(u"world", /*case_sensitive=*/false);
 
   {
     InSequence sequence;
diff --git a/pdf/pdfium/pdfium_engine.cc b/pdf/pdfium/pdfium_engine.cc
index 4553741..ff80660d 100644
--- a/pdf/pdfium/pdfium_engine.cc
+++ b/pdf/pdfium/pdfium_engine.cc
@@ -1757,7 +1757,7 @@
   return rv;
 }
 
-void PDFiumEngine::StartFind(const std::string& text, bool case_sensitive) {
+void PDFiumEngine::StartFind(const std::u16string& text, bool case_sensitive) {
   // If the caller asks StartFind() to search for no text, then this is an
   // error on the part of the caller. The `blink::FindInPage` code guarantees it
   // is not empty, so this should never happen.
@@ -1795,15 +1795,14 @@
   int current_page = next_page_to_search_;
 
   if (pages_[current_page]->available()) {
-    std::u16string str = base::UTF8ToUTF16(text);
     // Don't use PDFium to search for now, since it doesn't support unicode
     // text. Leave the code for now to avoid bit-rot, in case it's fixed later.
     // The extra parens suppress a -Wunreachable-code warning.
     if ((false)) {
-      SearchUsingPDFium(str, case_sensitive, first_search,
+      SearchUsingPDFium(text, case_sensitive, first_search,
                         character_to_start_searching_from, current_page);
     } else {
-      SearchUsingICU(str, case_sensitive, first_search,
+      SearchUsingICU(text, case_sensitive, first_search,
                      character_to_start_searching_from, current_page);
     }
 
@@ -3779,7 +3778,7 @@
 
   // Store the current find index so that we can resume finding at that
   // particular index after we have recomputed the find results.
-  std::string current_find_text = current_find_text_;
+  std::u16string current_find_text = current_find_text_;
   resume_find_index_ = current_find_index_;
 
   // Save the current page.
diff --git a/pdf/pdfium/pdfium_engine.h b/pdf/pdfium/pdfium_engine.h
index e7be29e..329f995 100644
--- a/pdf/pdfium/pdfium_engine.h
+++ b/pdf/pdfium/pdfium_engine.h
@@ -106,7 +106,7 @@
       const std::vector<int>& page_numbers,
       const blink::WebPrintParams& print_params) override;
   void PrintEnd() override;
-  void StartFind(const std::string& text, bool case_sensitive) override;
+  void StartFind(const std::u16string& text, bool case_sensitive) override;
   bool SelectFindResult(bool forward) override;
   void StopFind() override;
   void ZoomUpdated(double new_zoom_level) override;
@@ -723,7 +723,7 @@
   gfx::Point mouse_middle_button_last_position_;
 
   // The current text used for searching.
-  std::string current_find_text_;
+  std::u16string current_find_text_;
   // The results found.
   std::vector<PDFiumRange> find_results_;
   // Whether a search is in progress.
diff --git a/printing/print_settings_conversion.cc b/printing/print_settings_conversion.cc
index 48dd063..8a91c39 100644
--- a/printing/print_settings_conversion.cc
+++ b/printing/print_settings_conversion.cc
@@ -228,6 +228,22 @@
     settings->set_rasterize_pdf_dpi(rasterize_pdf_dpi.value());
   }
 
+  // Set margin type before setting printable area. `SetPrintableAreaIfValid()`
+  // calls `PrintSettings::SetPrinterPrintableArea()`, which requires the margin
+  // type to be set first.
+  mojom::MarginType margin_type = static_cast<mojom::MarginType>(
+      job_settings.FindInt(kSettingMarginsType)
+          .value_or(static_cast<int>(mojom::MarginType::kDefaultMargins)));
+  if (margin_type < mojom::MarginType::kMinValue ||
+      margin_type > mojom::MarginType::kMaxValue) {
+    margin_type = mojom::MarginType::kDefaultMargins;
+  }
+  settings->set_margin_type(margin_type);
+
+  if (margin_type == mojom::MarginType::kCustomMargins) {
+    settings->SetCustomMargins(GetCustomMarginsFromJobSettings(job_settings));
+  }
+
   PrintSettings::RequestedMedia requested_media;
   const base::Value::Dict* media_size_value =
       job_settings.FindDict(kSettingMediaSize);
@@ -251,19 +267,6 @@
   }
   settings->set_requested_media(requested_media);
 
-  mojom::MarginType margin_type = static_cast<mojom::MarginType>(
-      job_settings.FindInt(kSettingMarginsType)
-          .value_or(static_cast<int>(mojom::MarginType::kDefaultMargins)));
-  if (margin_type < mojom::MarginType::kMinValue ||
-      margin_type > mojom::MarginType::kMaxValue) {
-    margin_type = mojom::MarginType::kDefaultMargins;
-  }
-  settings->set_margin_type(margin_type);
-
-  if (margin_type == mojom::MarginType::kCustomMargins) {
-    settings->SetCustomMargins(GetCustomMarginsFromJobSettings(job_settings));
-  }
-
   settings->set_ranges(GetPageRangesFromJobSettings(job_settings));
 
   absl::optional<bool> is_modifiable =
diff --git a/printing/print_settings_conversion_unittest.cc b/printing/print_settings_conversion_unittest.cc
index a296460..ba709001 100644
--- a/printing/print_settings_conversion_unittest.cc
+++ b/printing/print_settings_conversion_unittest.cc
@@ -92,6 +92,13 @@
   "dpiVertical": 300,
 })";
 
+const char kCustomMargins[] = R"({
+  "marginBottom": 10,
+  "marginLeft": 30,
+  "marginRight": 20,
+  "marginTop": 80
+})";
+
 }  // namespace
 
 TEST(PrintSettingsConversionTest, InvalidSettings) {
@@ -200,6 +207,30 @@
   EXPECT_TRUE(settings->page_setup_device_units().printable_area().IsEmpty());
 }
 
+TEST(PrintSettingsConversionTest, WithCustomMarginsAndImageableArea) {
+  // Test imageable area with custom margins.
+#if BUILDFLAG(IS_MAC)
+  static constexpr gfx::Size kExpectedSize{595, 842};
+  static constexpr gfx::Rect kExpectedPrintableArea{0, 0, 510, 839};
+  const PageMargins kExpectedPageMargins(0, 0, 30, 20, 80, 10);
+#else
+  static constexpr gfx::Size kExpectedSize{2480, 3508};
+  static constexpr gfx::Rect kExpectedPrintableArea{0, 0, 2126, 3496};
+  const PageMargins kExpectedPageMargins(0, 0, 125, 83, 333, 42);
+#endif
+
+  base::Value::Dict dict =
+      base::test::ParseJsonDict(kPrinterSettingsWithImageableArea);
+  dict.Set("marginsType", static_cast<int>(mojom::MarginType::kCustomMargins));
+  dict.Set("marginsCustom", base::test::ParseJson(kCustomMargins));
+  std::unique_ptr<PrintSettings> settings = PrintSettingsFromJobSettings(dict);
+  ASSERT_TRUE(settings);
+  const PageSetup& page_setup = settings->page_setup_device_units();
+  EXPECT_EQ(page_setup.physical_size(), kExpectedSize);
+  EXPECT_EQ(page_setup.printable_area(), kExpectedPrintableArea);
+  EXPECT_TRUE(page_setup.effective_margins().Equals(kExpectedPageMargins));
+}
+
 TEST(PrintSettingsConversionTest, MissingDeviceName) {
   base::Value::Dict dict = base::test::ParseJsonDict(kPrinterSettings);
   EXPECT_TRUE(dict.Remove("deviceName"));
diff --git a/sandbox/policy/linux/bpf_libassistant_policy_linux.cc b/sandbox/policy/linux/bpf_libassistant_policy_linux.cc
index 6e71021..8716245 100644
--- a/sandbox/policy/linux/bpf_libassistant_policy_linux.cc
+++ b/sandbox/policy/linux/bpf_libassistant_policy_linux.cc
@@ -63,6 +63,7 @@
     case __NR_getpeername:
     case __NR_getsockname:
     case __NR_listen:
+    case __NR_sync:
       return Allow();
     default:
       if (SyscallSets::IsGoogle3Threading(sysno)) {
diff --git a/sandbox/win/src/eat_resolver.cc b/sandbox/win/src/eat_resolver.cc
index 7aa7c9c..97f77c1 100644
--- a/sandbox/win/src/eat_resolver.cc
+++ b/sandbox/win/src/eat_resolver.cc
@@ -4,6 +4,7 @@
 
 #include "sandbox/win/src/eat_resolver.h"
 
+#include <ntstatus.h>
 #include <stddef.h>
 
 #include "base/win/pe_image.h"
@@ -27,7 +28,7 @@
     return ret;
 
   if (!eat_entry_)
-    return NTSTATUS_INVALID_PARAMETER;
+    return STATUS_INVALID_PARAMETER;
 
 #if defined(_WIN64)
   // We have two thunks, in order: the return path and the forward path.
@@ -62,7 +63,7 @@
                                          void** address) {
   DCHECK_NT(address);
   if (!module)
-    return NTSTATUS_INVALID_PARAMETER;
+    return STATUS_INVALID_PARAMETER;
 
   base::win::PEImage pe(module);
   if (!pe.VerifyMagic())
diff --git a/sandbox/win/src/file_policy_test.cc b/sandbox/win/src/file_policy_test.cc
index 5219b80..d1ec2e3 100644
--- a/sandbox/win/src/file_policy_test.cc
+++ b/sandbox/win/src/file_policy_test.cc
@@ -5,6 +5,7 @@
 #include <algorithm>
 #include <cctype>
 
+#include <ntstatus.h>
 #include <windows.h>
 #include <winioctl.h>
 
diff --git a/sandbox/win/src/filesystem_dispatcher.cc b/sandbox/win/src/filesystem_dispatcher.cc
index 2499f207..742fd66 100644
--- a/sandbox/win/src/filesystem_dispatcher.cc
+++ b/sandbox/win/src/filesystem_dispatcher.cc
@@ -4,6 +4,7 @@
 
 #include "sandbox/win/src/filesystem_dispatcher.h"
 
+#include <ntstatus.h>
 #include <stdint.h>
 
 #include "sandbox/win/src/crosscall_client.h"
diff --git a/sandbox/win/src/filesystem_interception.cc b/sandbox/win/src/filesystem_interception.cc
index 0ef1fdf..5052c78 100644
--- a/sandbox/win/src/filesystem_interception.cc
+++ b/sandbox/win/src/filesystem_interception.cc
@@ -4,6 +4,7 @@
 
 #include "sandbox/win/src/filesystem_interception.h"
 
+#include <ntstatus.h>
 #include <stdint.h>
 
 #include "sandbox/win/src/crosscall_client.h"
diff --git a/sandbox/win/src/filesystem_policy.cc b/sandbox/win/src/filesystem_policy.cc
index c185e01..6d2387f1 100644
--- a/sandbox/win/src/filesystem_policy.cc
+++ b/sandbox/win/src/filesystem_policy.cc
@@ -4,6 +4,7 @@
 
 #include "sandbox/win/src/filesystem_policy.h"
 
+#include <ntstatus.h>
 #include <stdint.h>
 
 #include <string>
diff --git a/sandbox/win/src/ipc_leak_test.cc b/sandbox/win/src/ipc_leak_test.cc
index 9fa2671d..696f001 100644
--- a/sandbox/win/src/ipc_leak_test.cc
+++ b/sandbox/win/src/ipc_leak_test.cc
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <ntstatus.h>
 #include <stdlib.h>
 #include <windows.h>
 
diff --git a/sandbox/win/src/nt_internals.h b/sandbox/win/src/nt_internals.h
index 6f3d6e8..7e933e8 100644
--- a/sandbox/win/src/nt_internals.h
+++ b/sandbox/win/src/nt_internals.h
@@ -15,32 +15,6 @@
 #define NT_SUCCESS(st) (st >= 0)
 #define NT_ERROR(st) ((((ULONG)(st)) >> 30) == 3)
 
-// clang-format off
-#define STATUS_SUCCESS                ((NTSTATUS)0x00000000L)
-#define STATUS_BUFFER_OVERFLOW        ((NTSTATUS)0x80000005L)
-#define STATUS_UNSUCCESSFUL           ((NTSTATUS)0xC0000001L)
-#define STATUS_NOT_IMPLEMENTED        ((NTSTATUS)0xC0000002L)
-#define STATUS_INVALID_INFO_CLASS     ((NTSTATUS)0xC0000003L)
-#define STATUS_INFO_LENGTH_MISMATCH   ((NTSTATUS)0xC0000004L)
-#ifndef STATUS_INVALID_PARAMETER
-// It is now defined in Windows 2008 SDK.
-#define STATUS_INVALID_PARAMETER      ((NTSTATUS)0xC000000DL)
-#endif
-#define STATUS_CONFLICTING_ADDRESSES  ((NTSTATUS)0xC0000018L)
-#define STATUS_ACCESS_DENIED          ((NTSTATUS)0xC0000022L)
-#define STATUS_BUFFER_TOO_SMALL       ((NTSTATUS)0xC0000023L)
-#define STATUS_OBJECT_NAME_NOT_FOUND  ((NTSTATUS)0xC0000034L)
-#define STATUS_OBJECT_NAME_COLLISION  ((NTSTATUS)0xC0000035L)
-#define STATUS_PROCEDURE_NOT_FOUND    ((NTSTATUS)0xC000007AL)
-#define STATUS_INVALID_IMAGE_FORMAT   ((NTSTATUS)0xC000007BL)
-#define STATUS_NO_TOKEN               ((NTSTATUS)0xC000007CL)
-#define STATUS_NOT_SUPPORTED          ((NTSTATUS)0xC00000BBL)
-#define STATUS_INVALID_IMAGE_HASH     ((NTSTATUS)0xC0000428L)
-
-#define NTSTATUS_INVALID_PARAMETER    ((NTSTATUS)0xC000000DL)
-#define NTSTATUS_NO_MEMORY            ((NTSTATUS)0xC0000017L)
-// clang-format on
-
 #define CURRENT_PROCESS ((HANDLE)-1)
 #define CURRENT_THREAD ((HANDLE)-2)
 #define NtCurrentProcess CURRENT_PROCESS
diff --git a/sandbox/win/src/policy_target.cc b/sandbox/win/src/policy_target.cc
index 7c7d8fa..8c866d42 100644
--- a/sandbox/win/src/policy_target.cc
+++ b/sandbox/win/src/policy_target.cc
@@ -4,6 +4,7 @@
 
 #include "sandbox/win/src/policy_target.h"
 
+#include <ntstatus.h>
 #include <stddef.h>
 
 #include "sandbox/win/src/crosscall_client.h"
diff --git a/sandbox/win/src/policy_target_test.cc b/sandbox/win/src/policy_target_test.cc
index 2dbddc6..1edb9d9 100644
--- a/sandbox/win/src/policy_target_test.cc
+++ b/sandbox/win/src/policy_target_test.cc
@@ -2,13 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <ntstatus.h>
+
 #include "base/memory/read_only_shared_memory_region.h"
 #include "base/memory/writable_shared_memory_region.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_piece.h"
 #include "base/strings/string_util.h"
 #include "base/win/scoped_process_information.h"
-#include "build/build_config.h"
+#include "base/win/win_util.h"
 #include "sandbox/win/src/broker_services.h"
 #include "sandbox/win/src/sandbox.h"
 #include "sandbox/win/src/sandbox_factory.h"
@@ -18,10 +20,6 @@
 #include "sandbox/win/tests/common/controller.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-#if BUILDFLAG(IS_WIN)
-#include "base/win/win_util.h"
-#endif
-
 namespace sandbox {
 
 #define BINDNTDLL(name)                                   \
diff --git a/sandbox/win/src/process_mitigations_unittest.cc b/sandbox/win/src/process_mitigations_unittest.cc
index 219aa9b8..7e107e5 100644
--- a/sandbox/win/src/process_mitigations_unittest.cc
+++ b/sandbox/win/src/process_mitigations_unittest.cc
@@ -6,6 +6,7 @@
 
 #include <excpt.h>
 #include <ktmw32.h>
+#include <ntstatus.h>
 #include <windows.h>
 
 #include "base/files/file_util.h"
diff --git a/sandbox/win/src/process_thread_policy.cc b/sandbox/win/src/process_thread_policy.cc
index 84c49ad..88969b0 100644
--- a/sandbox/win/src/process_thread_policy.cc
+++ b/sandbox/win/src/process_thread_policy.cc
@@ -4,6 +4,7 @@
 
 #include "sandbox/win/src/process_thread_policy.h"
 
+#include <ntstatus.h>
 #include <stdint.h>
 
 #include <memory>
diff --git a/sandbox/win/src/resolver.cc b/sandbox/win/src/resolver.cc
index 6dbc5c9..d6126ad 100644
--- a/sandbox/win/src/resolver.cc
+++ b/sandbox/win/src/resolver.cc
@@ -4,6 +4,7 @@
 
 #include "sandbox/win/src/resolver.h"
 
+#include <ntstatus.h>
 #include <stddef.h>
 
 #include "base/win/pe_image.h"
@@ -20,7 +21,7 @@
                              void* thunk_storage,
                              size_t storage_bytes) {
   if (!thunk_storage || 0 == storage_bytes || !target_module || !target_name)
-    return NTSTATUS_INVALID_PARAMETER;
+    return STATUS_INVALID_PARAMETER;
 
   if (storage_bytes < GetThunkSize())
     return STATUS_BUFFER_TOO_SMALL;
@@ -47,7 +48,7 @@
                                            const void** address) {
   DCHECK_NT(address);
   if (!interceptor_module)
-    return NTSTATUS_INVALID_PARAMETER;
+    return STATUS_INVALID_PARAMETER;
 
   base::win::PEImage pe(interceptor_module);
   if (!pe.VerifyMagic())
diff --git a/sandbox/win/src/resolver_64.cc b/sandbox/win/src/resolver_64.cc
index 1cb04bb..e2e0277 100644
--- a/sandbox/win/src/resolver_64.cc
+++ b/sandbox/win/src/resolver_64.cc
@@ -4,6 +4,7 @@
 
 #include "sandbox/win/src/resolver.h"
 
+#include <ntstatus.h>
 #include <stddef.h>
 
 // For placement new. This file must not depend on the CRT at runtime, but
diff --git a/sandbox/win/src/sandbox_nt_util.cc b/sandbox/win/src/sandbox_nt_util.cc
index a6116f295..39b7ead 100644
--- a/sandbox/win/src/sandbox_nt_util.cc
+++ b/sandbox/win/src/sandbox_nt_util.cc
@@ -4,6 +4,7 @@
 
 #include "sandbox/win/src/sandbox_nt_util.h"
 
+#include <ntstatus.h>
 #include <stddef.h>
 #include <stdint.h>
 
@@ -295,7 +296,7 @@
     size_t* out_name_len,
     uint32_t* attributes) {
   if (!InitHeap())
-    return NTSTATUS_NO_MEMORY;
+    return STATUS_NO_MEMORY;
 
   DCHECK_NT(out_name);
   DCHECK_NT(out_name_len);
diff --git a/sandbox/win/src/sandbox_nt_util_unittest.cc b/sandbox/win/src/sandbox_nt_util_unittest.cc
index 40092c5..749a6753 100644
--- a/sandbox/win/src/sandbox_nt_util_unittest.cc
+++ b/sandbox/win/src/sandbox_nt_util_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "sandbox/win/src/sandbox_nt_util.h"
 
+#include <ntstatus.h>
 #include <windows.h>
 
 #include <memory>
diff --git a/sandbox/win/src/service_resolver.cc b/sandbox/win/src/service_resolver.cc
index 40d8100..fbe8366 100644
--- a/sandbox/win/src/service_resolver.cc
+++ b/sandbox/win/src/service_resolver.cc
@@ -4,6 +4,8 @@
 
 #include "sandbox/win/src/service_resolver.h"
 
+#include <ntstatus.h>
+
 #include "base/win/pe_image.h"
 #include "sandbox/win/src/internal_types.h"
 #include "sandbox/win/src/sandbox_nt_util.h"
diff --git a/sandbox/win/src/service_resolver_32.cc b/sandbox/win/src/service_resolver_32.cc
index f0eba39c..0f1fab9 100644
--- a/sandbox/win/src/service_resolver_32.cc
+++ b/sandbox/win/src/service_resolver_32.cc
@@ -4,6 +4,7 @@
 
 #include "sandbox/win/src/service_resolver.h"
 
+#include <ntstatus.h>
 #include <stddef.h>
 
 #include <memory>
diff --git a/sandbox/win/src/service_resolver_64.cc b/sandbox/win/src/service_resolver_64.cc
index 9a80ca32..77bc2f16 100644
--- a/sandbox/win/src/service_resolver_64.cc
+++ b/sandbox/win/src/service_resolver_64.cc
@@ -4,6 +4,7 @@
 
 #include "sandbox/win/src/service_resolver.h"
 
+#include <ntstatus.h>
 #include <stddef.h>
 
 #include <memory>
diff --git a/sandbox/win/src/service_resolver_unittest.cc b/sandbox/win/src/service_resolver_unittest.cc
index 76687f3..ed1ee45 100644
--- a/sandbox/win/src/service_resolver_unittest.cc
+++ b/sandbox/win/src/service_resolver_unittest.cc
@@ -6,6 +6,7 @@
 
 #include "sandbox/win/src/service_resolver.h"
 
+#include <ntstatus.h>
 #include <stddef.h>
 
 #include <memory>
diff --git a/sandbox/win/src/signed_interception.cc b/sandbox/win/src/signed_interception.cc
index f5edf950..25f636f 100644
--- a/sandbox/win/src/signed_interception.cc
+++ b/sandbox/win/src/signed_interception.cc
@@ -4,6 +4,7 @@
 
 #include "sandbox/win/src/signed_interception.h"
 
+#include <ntstatus.h>
 #include <stdint.h>
 
 #include "sandbox/win/src/crosscall_client.h"
diff --git a/sandbox/win/src/signed_policy.cc b/sandbox/win/src/signed_policy.cc
index 65bca661..af8bcb8 100644
--- a/sandbox/win/src/signed_policy.cc
+++ b/sandbox/win/src/signed_policy.cc
@@ -4,6 +4,7 @@
 
 #include "sandbox/win/src/signed_policy.h"
 
+#include <ntstatus.h>
 #include <stdint.h>
 
 #include <string>
diff --git a/sandbox/win/src/target_interceptions.cc b/sandbox/win/src/target_interceptions.cc
index 7fc7acd6..0befe25 100644
--- a/sandbox/win/src/target_interceptions.cc
+++ b/sandbox/win/src/target_interceptions.cc
@@ -4,6 +4,8 @@
 
 #include "sandbox/win/src/target_interceptions.h"
 
+#include <ntstatus.h>
+
 #include "base/win/static_constants.h"
 #include "sandbox/win/src/interception_agent.h"
 #include "sandbox/win/src/sandbox_factory.h"
diff --git a/sandbox/win/src/win_utils.cc b/sandbox/win/src/win_utils.cc
index ebff9930..8e66f18 100644
--- a/sandbox/win/src/win_utils.cc
+++ b/sandbox/win/src/win_utils.cc
@@ -6,6 +6,7 @@
 
 #include <windows.h>
 
+#include <ntstatus.h>
 #include <psapi.h>
 #include <stddef.h>
 #include <stdint.h>
@@ -154,7 +155,7 @@
                          std::vector<uint8_t>& buffer,
                          PULONG reqd) {
   if (handle == nullptr || handle == INVALID_HANDLE_VALUE)
-    return NTSTATUS_INVALID_PARAMETER;
+    return STATUS_INVALID_PARAMETER;
   NtQueryObjectFunction NtQueryObject = sandbox::GetNtExports()->QueryObject;
   ULONG size = static_cast<ULONG>(buffer.size());
   __try {
@@ -162,7 +163,7 @@
   } __except (GetExceptionCode() == STATUS_INVALID_HANDLE
                   ? EXCEPTION_EXECUTE_HANDLER
                   : EXCEPTION_CONTINUE_SEARCH) {
-    return NTSTATUS_INVALID_PARAMETER;
+    return STATUS_INVALID_PARAMETER;
   }
 }
 
diff --git a/sandbox/win/src/win_utils_unittest.cc b/sandbox/win/src/win_utils_unittest.cc
index 136d1d4..30eb661 100644
--- a/sandbox/win/src/win_utils_unittest.cc
+++ b/sandbox/win/src/win_utils_unittest.cc
@@ -6,6 +6,7 @@
 
 #include <windows.h>
 
+#include <ntstatus.h>
 #include <psapi.h>
 
 #include <vector>
diff --git a/sandbox/win/tests/integration_tests/integration_tests_common.h b/sandbox/win/tests/integration_tests/integration_tests_common.h
index 805e34a8..d780519 100644
--- a/sandbox/win/tests/integration_tests/integration_tests_common.h
+++ b/sandbox/win/tests/integration_tests/integration_tests_common.h
@@ -46,8 +46,8 @@
   explicit ScopedTestMutex(const wchar_t* name)
     : mutex_(::CreateMutexW(nullptr, false, name)) {
     EXPECT_TRUE(mutex_);
-    EXPECT_EQ(WAIT_OBJECT_0,
-      ::WaitForSingleObject(mutex_, SboxTestEventTimeout()));
+    EXPECT_EQ(DWORD{WAIT_OBJECT_0},
+              ::WaitForSingleObject(mutex_, SboxTestEventTimeout()));
   }
 
   ScopedTestMutex(const ScopedTestMutex&) = delete;
diff --git a/services/network/web_transport.cc b/services/network/web_transport.cc
index e8b4772..ad2f680 100644
--- a/services/network/web_transport.cc
+++ b/services/network/web_transport.cc
@@ -286,13 +286,13 @@
         }
         DCHECK_EQ(result, MOJO_RESULT_OK);
 
-        read_result =
-            incoming_->Read(reinterpret_cast<char*>(buffer), available);
+        read_result = incoming_->Read(
+            absl::Span<char>(reinterpret_cast<char*>(buffer), available));
         writable_->EndWriteData(read_result.bytes_read);
       } else {
         // Even if ReadableBytes() == 0, we may need to read the FIN at the end
         // of the stream.
-        read_result = incoming_->Read(nullptr, 0);
+        read_result = incoming_->Read(absl::Span<char>());
         if (!read_result.fin) {
           return;
         }
@@ -409,11 +409,8 @@
 
   datagram_callbacks_.emplace(std::move(callback));
 
-  quiche::QuicheBuffer buffer(quiche::SimpleBufferAllocator::Get(),
-                              data.size());
-  memcpy(buffer.data(), data.data(), data.size());
-  quiche::QuicheMemSlice slice(std::move(buffer));
-  transport_->session()->SendOrQueueDatagram(std::move(slice));
+  transport_->session()->SendOrQueueDatagram(absl::string_view(
+      reinterpret_cast<const char*>(data.data()), data.size()));
 }
 
 void WebTransport::CreateStream(
@@ -512,7 +509,7 @@
   }
 
   transport_->session()->SetDatagramMaxTimeInQueue(
-      quic::QuicTime::Delta::FromMicroseconds(duration.InMicroseconds()));
+      absl::Microseconds(duration.InMicroseconds()));
 }
 
 void WebTransport::Close(mojom::WebTransportCloseInfoPtr close_info) {
diff --git a/services/viz/privileged/mojom/compositing/display_private.mojom b/services/viz/privileged/mojom/compositing/display_private.mojom
index 52f44a3..83c153d 100644
--- a/services/viz/privileged/mojom/compositing/display_private.mojom
+++ b/services/viz/privileged/mojom/compositing/display_private.mojom
@@ -39,6 +39,10 @@
   SetDisplayColorSpaces(gfx.mojom.DisplayColorSpaces display_color_spaces);
   SetOutputIsSecure(bool secure);
 
+  // Sets CGDirectDisplayID for GPU begin frame source.
+  [EnableIf=is_mac]
+  SetVSyncDisplayID(int64 display_id);
+
   // Updates vsync parameters used to generate BeginFrames for this display.
   // This will do nothing if the display is using an external BeginFrame source.
   SetDisplayVSyncParameters(
diff --git a/services/viz/privileged/mojom/compositing/renderer_settings.mojom b/services/viz/privileged/mojom/compositing/renderer_settings.mojom
index cd780528..bf7dcbe 100644
--- a/services/viz/privileged/mojom/compositing/renderer_settings.mojom
+++ b/services/viz/privileged/mojom/compositing/renderer_settings.mojom
@@ -8,6 +8,9 @@
 import "ui/gfx/geometry/mojom/geometry.mojom";
 import "ui/gfx/mojom/color_space.mojom";
 
+// same kInvalidDisplayId defined in ui/display/types/display_constants.h
+const int64 kInvalidDisplayId = -1;
+
 struct RendererSettings {
   bool apply_simple_frame_rate_throttling;
   bool allow_antialiasing;
@@ -29,6 +32,9 @@
 
   [EnableIf=use_ozone]
   array<OverlayStrategy> overlay_strategies;
+
+  [EnableIf=is_mac]
+  int64 display_id = kInvalidDisplayId;
 };
 
 struct DebugRendererSettings {
diff --git a/services/viz/privileged/mojom/compositing/renderer_settings_mojom_traits.cc b/services/viz/privileged/mojom/compositing/renderer_settings_mojom_traits.cc
index 14909e5..4eb370a2 100644
--- a/services/viz/privileged/mojom/compositing/renderer_settings_mojom_traits.cc
+++ b/services/viz/privileged/mojom/compositing/renderer_settings_mojom_traits.cc
@@ -59,6 +59,10 @@
     return false;
 #endif
 
+#if BUILDFLAG(IS_MAC)
+  out->display_id = data.display_id();
+#endif
+
   return true;
 }
 
diff --git a/services/viz/privileged/mojom/compositing/renderer_settings_mojom_traits.h b/services/viz/privileged/mojom/compositing/renderer_settings_mojom_traits.h
index 45ae364..aae01622 100644
--- a/services/viz/privileged/mojom/compositing/renderer_settings_mojom_traits.h
+++ b/services/viz/privileged/mojom/compositing/renderer_settings_mojom_traits.h
@@ -114,6 +114,12 @@
   }
 #endif
 
+#if BUILDFLAG(IS_MAC)
+  static int64_t display_id(const viz::RendererSettings& input) {
+    return input.display_id;
+  }
+#endif
+
   static bool Read(viz::mojom::RendererSettingsDataView data,
                    viz::RendererSettings* out);
 };
diff --git a/skia/ext/SkMemory_new_handler.cpp b/skia/ext/SkMemory_new_handler.cpp
index 3ac0dd7..88a25b5 100644
--- a/skia/ext/SkMemory_new_handler.cpp
+++ b/skia/ext/SkMemory_new_handler.cpp
@@ -11,7 +11,7 @@
 #include "base/process/memory.h"
 #include "build/build_config.h"
 #include "third_party/skia/include/core/SkTypes.h"
-#include "third_party/skia/include/private/SkMalloc.h"
+#include "third_party/skia/include/private/base/SkMalloc.h"
 
 #if BUILDFLAG(IS_WIN)
 #include <windows.h>
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index 6e943a4a..e0d0302b 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -1552,2117 +1552,6 @@
       }
     ]
   },
-  "Linux ChromiumOS MSan Focal": {
-    "gtest_tests": [
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 7200,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "absl_hardening_tests",
-        "test_id_prefix": "ninja://third_party/abseil-cpp:absl_hardening_tests/"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 7200,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "accessibility_unittests",
-        "test_id_prefix": "ninja://ui/accessibility:accessibility_unittests/"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 7200,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "angle_unittests",
-        "test_id_prefix": "ninja://third_party/angle/src/tests:angle_unittests/",
-        "use_isolated_scripts_api": true
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 7200,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "app_shell_unittests",
-        "test_id_prefix": "ninja://extensions/shell:app_shell_unittests/"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 7200,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "ash_components_unittests",
-        "test_id_prefix": "ninja://ash/components:ash_components_unittests/"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 7200,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 5
-        },
-        "test": "ash_unittests",
-        "test_id_prefix": "ninja://ash:ash_unittests/"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 7200,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "ash_webui_unittests",
-        "test_id_prefix": "ninja://ash/webui:ash_webui_unittests/"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 7200,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "aura_unittests",
-        "test_id_prefix": "ninja://ui/aura:aura_unittests/"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 7200,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "base_unittests",
-        "test_id_prefix": "ninja://base:base_unittests/"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 7200,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "blink_common_unittests",
-        "test_id_prefix": "ninja://third_party/blink/common:blink_common_unittests/"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 7200,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "blink_fuzzer_unittests",
-        "test_id_prefix": "ninja://third_party/blink/renderer/platform:blink_fuzzer_unittests/"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 7200,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "blink_heap_unittests",
-        "test_id_prefix": "ninja://third_party/blink/renderer/platform/heap:blink_heap_unittests/"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 7200,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "blink_platform_unittests",
-        "test_id_prefix": "ninja://third_party/blink/renderer/platform:blink_platform_unittests/"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "webkit_unit_tests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 7200,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "blink_unittests",
-        "test_id_prefix": "ninja://third_party/blink/renderer/controller:blink_unittests/"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 7200,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "boringssl_crypto_tests",
-        "test_id_prefix": "ninja://third_party/boringssl:boringssl_crypto_tests/"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 7200,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "boringssl_ssl_tests",
-        "test_id_prefix": "ninja://third_party/boringssl:boringssl_ssl_tests/"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always",
-          "--test-launcher-filter-file=../../testing/buildbot/filters/chromeos.msan.browser_tests.oobe_negative.filter"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 7200,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 40
-        },
-        "test": "browser_tests",
-        "test_id_prefix": "ninja://chrome/test:browser_tests/"
-      },
-      {
-        "args": [
-          "--test-launcher-filter-file=../../testing/buildbot/filters/chromeos.msan.browser_tests.oobe_positive.filter",
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "experiment_percentage": 100,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "oobe_only_browser_tests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 7200,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 20
-        },
-        "test": "browser_tests",
-        "test_id_prefix": "ninja://chrome/test:browser_tests/"
-      },
-      {
-        "args": [
-          "--gtest_filter=-*UsingRealWebcam*",
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 7200,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "capture_unittests",
-        "test_id_prefix": "ninja://media/capture:capture_unittests/"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 7200,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "cast_unittests",
-        "test_id_prefix": "ninja://media/cast:cast_unittests/"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 7200,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "cc_unittests",
-        "test_id_prefix": "ninja://cc:cc_unittests/"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 7200,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "chrome_app_unittests",
-        "test_id_prefix": "ninja://chrome/test:chrome_app_unittests/"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 7200,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "chromedriver_unittests",
-        "test_id_prefix": "ninja://chrome/test/chromedriver:chromedriver_unittests/"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 7200,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "chromeos_components_unittests",
-        "test_id_prefix": "ninja://chromeos/components:chromeos_components_unittests/"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 7200,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "chromeos_unittests",
-        "test_id_prefix": "ninja://chromeos:chromeos_unittests/"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 7200,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "color_unittests",
-        "test_id_prefix": "ninja://ui/color:color_unittests/"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 7200,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "components_browsertests",
-        "test_id_prefix": "ninja://components:components_browsertests/"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 7200,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "components_unittests",
-        "test_id_prefix": "ninja://components:components_unittests/"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 7200,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "compositor_unittests",
-        "test_id_prefix": "ninja://ui/compositor:compositor_unittests/"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 7200,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 8
-        },
-        "test": "content_browsertests",
-        "test_id_prefix": "ninja://content/test:content_browsertests/"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 7200,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "content_unittests",
-        "test_id_prefix": "ninja://content/test:content_unittests/"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 7200,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "crashpad_tests",
-        "test_id_prefix": "ninja://third_party/crashpad/crashpad:crashpad_tests/"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 7200,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "crypto_unittests",
-        "test_id_prefix": "ninja://crypto:crypto_unittests/"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 7200,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "dbus_unittests",
-        "test_id_prefix": "ninja://dbus:dbus_unittests/"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 7200,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "device_unittests",
-        "test_id_prefix": "ninja://device:device_unittests/"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 7200,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "display_unittests",
-        "test_id_prefix": "ninja://ui/display:display_unittests/"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 7200,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "events_unittests",
-        "test_id_prefix": "ninja://ui/events:events_unittests/"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 7200,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "exo_unittests",
-        "test_id_prefix": "ninja://components/exo:exo_unittests/"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 7200,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "extensions_browsertests",
-        "test_id_prefix": "ninja://extensions:extensions_browsertests/"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 7200,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "extensions_unittests",
-        "test_id_prefix": "ninja://extensions:extensions_unittests/"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 7200,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "filesystem_service_unittests",
-        "test_id_prefix": "ninja://components/services/filesystem:filesystem_service_unittests/"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 7200,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "gcm_unit_tests",
-        "test_id_prefix": "ninja://google_apis/gcm:gcm_unit_tests/"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 7200,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "gfx_unittests",
-        "test_id_prefix": "ninja://ui/gfx:gfx_unittests/"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 7200,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "gin_unittests",
-        "test_id_prefix": "ninja://gin:gin_unittests/"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 7200,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "google_apis_unittests",
-        "test_id_prefix": "ninja://google_apis:google_apis_unittests/"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 7200,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "gpu_unittests",
-        "test_id_prefix": "ninja://gpu:gpu_unittests/"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 7200,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "gwp_asan_unittests",
-        "test_id_prefix": "ninja://components/gwp_asan:gwp_asan_unittests/"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 7200,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 5
-        },
-        "test": "interactive_ui_tests",
-        "test_id_prefix": "ninja://chrome/test:interactive_ui_tests/"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 7200,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "ipc_tests",
-        "test_id_prefix": "ninja://ipc:ipc_tests/"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 7200,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "keyboard_unittests",
-        "test_id_prefix": "ninja://ash/keyboard/ui:keyboard_unittests/"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 7200,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "latency_unittests",
-        "test_id_prefix": "ninja://ui/latency:latency_unittests/"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 7200,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "libjingle_xmpp_unittests",
-        "test_id_prefix": "ninja://third_party/libjingle_xmpp:libjingle_xmpp_unittests/"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 7200,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "liburlpattern_unittests",
-        "test_id_prefix": "ninja://third_party/liburlpattern:liburlpattern_unittests/"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 7200,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "media_unittests",
-        "test_id_prefix": "ninja://media:media_unittests/"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 7200,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "message_center_unittests",
-        "test_id_prefix": "ninja://ui/message_center:message_center_unittests/"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 7200,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "midi_unittests",
-        "test_id_prefix": "ninja://media/midi:midi_unittests/"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 7200,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "mojo_core_unittests",
-        "test_id_prefix": "ninja://mojo/core:mojo_core_unittests/"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 7200,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "mojo_unittests",
-        "test_id_prefix": "ninja://mojo:mojo_unittests/"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 7200,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "nacl_loader_unittests",
-        "test_id_prefix": "ninja://components/nacl/loader:nacl_loader_unittests/"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 7200,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "native_theme_unittests",
-        "test_id_prefix": "ninja://ui/native_theme:native_theme_unittests/"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 7200,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "net_unittests",
-        "test_id_prefix": "ninja://net:net_unittests/"
-      },
-      {
-        "args": [
-          "--ozone-platform=headless",
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 7200,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "ozone_gl_unittests",
-        "test_id_prefix": "ninja://ui/ozone/gl:ozone_gl_unittests/"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 7200,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "ozone_unittests",
-        "test_id_prefix": "ninja://ui/ozone:ozone_unittests/"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 7200,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "ozone_x11_unittests",
-        "test_id_prefix": "ninja://ui/ozone:ozone_x11_unittests/"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 7200,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "pdf_unittests",
-        "test_id_prefix": "ninja://pdf:pdf_unittests/"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 7200,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "perfetto_unittests",
-        "test_id_prefix": "ninja://third_party/perfetto:perfetto_unittests/"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 7200,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "ppapi_unittests",
-        "test_id_prefix": "ninja://ppapi:ppapi_unittests/"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 7200,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "printing_unittests",
-        "test_id_prefix": "ninja://printing:printing_unittests/"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 7200,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "remoting_unittests",
-        "test_id_prefix": "ninja://remoting:remoting_unittests/"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 7200,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "sandbox_linux_unittests",
-        "test_id_prefix": "ninja://sandbox/linux:sandbox_linux_unittests/"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 7200,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "service_manager_unittests",
-        "test_id_prefix": "ninja://services/service_manager/tests:service_manager_unittests/"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 7200,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "shell_dialogs_unittests",
-        "test_id_prefix": "ninja://ui/shell_dialogs:shell_dialogs_unittests/"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 7200,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "shell_encryption_unittests",
-        "test_id_prefix": "ninja://third_party/shell-encryption:shell_encryption_unittests/"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 7200,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "skia_unittests",
-        "test_id_prefix": "ninja://skia:skia_unittests/"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 7200,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "snapshot_unittests",
-        "test_id_prefix": "ninja://ui/snapshot:snapshot_unittests/"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 7200,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "sql_unittests",
-        "test_id_prefix": "ninja://sql:sql_unittests/"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 7200,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "storage_unittests",
-        "test_id_prefix": "ninja://storage:storage_unittests/"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 7200,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "sync_integration_tests",
-        "test_id_prefix": "ninja://chrome/test:sync_integration_tests/"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 7200,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "ui_base_unittests",
-        "test_id_prefix": "ninja://ui/base:ui_base_unittests/"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 7200,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "ui_chromeos_unittests",
-        "test_id_prefix": "ninja://ui/chromeos:ui_chromeos_unittests/"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 7200,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "ui_touch_selection_unittests",
-        "test_id_prefix": "ninja://ui/touch_selection:ui_touch_selection_unittests/"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 7200,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "unit_tests",
-        "test_id_prefix": "ninja://chrome/test:unit_tests/"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 7200,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "url_unittests",
-        "test_id_prefix": "ninja://url:url_unittests/"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "experiment_percentage": 100,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 7200,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "usage_time_limit_unittests",
-        "test_id_prefix": "ninja://chrome/test:usage_time_limit_unittests/"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 7200,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "views_unittests",
-        "test_id_prefix": "ninja://ui/views:views_unittests/"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 7200,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "viz_unittests",
-        "test_id_prefix": "ninja://components/viz:viz_unittests/"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 7200,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "wayland_client_perftests",
-        "test_id_prefix": "ninja://components/exo/wayland:wayland_client_perftests/"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 7200,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "wayland_client_tests",
-        "test_id_prefix": "ninja://components/exo/wayland:wayland_client_tests/"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 7200,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "wm_unittests",
-        "test_id_prefix": "ninja://ui/wm:wm_unittests/"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 7200,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "wtf_unittests",
-        "test_id_prefix": "ninja://third_party/blink/renderer/platform/wtf:wtf_unittests/"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 7200,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "zlib_unittests",
-        "test_id_prefix": "ninja://third_party/zlib:zlib_unittests/"
-      }
-    ]
-  },
   "Linux Viz": {
     "additional_compile_targets": [
       "all"
diff --git a/testing/buildbot/chromium.memory.json b/testing/buildbot/chromium.memory.json
index ef31f4b..b2afe4e 100644
--- a/testing/buildbot/chromium.memory.json
+++ b/testing/buildbot/chromium.memory.json
@@ -5228,7 +5228,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-20.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -5249,7 +5249,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-20.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -5270,7 +5270,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-20.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -5292,7 +5292,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-20.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -5313,7 +5313,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-20.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -5334,7 +5334,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-20.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
@@ -5356,7 +5356,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-20.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -5377,7 +5377,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-20.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -5398,7 +5398,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-20.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -5419,7 +5419,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-20.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -5440,7 +5440,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-20.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -5461,7 +5461,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-20.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -5482,7 +5482,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-20.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -5504,7 +5504,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-20.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -5525,7 +5525,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-20.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -5546,7 +5546,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-20.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -5568,7 +5568,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-20.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
@@ -5593,7 +5593,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-20.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
@@ -5616,7 +5616,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-20.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -5637,7 +5637,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-20.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -5658,7 +5658,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-20.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -5679,7 +5679,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-20.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -5700,7 +5700,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-20.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -5721,7 +5721,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-20.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -5742,7 +5742,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-20.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -5763,7 +5763,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-20.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -5784,7 +5784,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-20.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -5805,7 +5805,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-20.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -5826,7 +5826,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-20.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -5847,7 +5847,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-20.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
@@ -5869,7 +5869,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-20.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
@@ -5891,7 +5891,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-20.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -5912,7 +5912,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-20.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -5933,7 +5933,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-20.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -5954,7 +5954,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-20.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -5975,7 +5975,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-20.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -5996,7 +5996,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-20.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -6017,7 +6017,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-20.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -6038,7 +6038,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-20.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -6059,7 +6059,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-20.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -6080,7 +6080,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-20.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -6101,7 +6101,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-20.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -6122,7 +6122,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-20.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -6143,7 +6143,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-20.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -6164,7 +6164,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-20.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -6185,7 +6185,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-20.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -6206,7 +6206,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-20.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -6227,7 +6227,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-20.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
@@ -6270,7 +6270,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-20.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -6291,7 +6291,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-20.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -6312,7 +6312,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-20.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -6333,7 +6333,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-20.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -6354,7 +6354,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-20.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -6375,7 +6375,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-20.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -6396,7 +6396,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-20.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -6417,7 +6417,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-20.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -6438,7 +6438,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-20.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -6459,7 +6459,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-20.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -6480,7 +6480,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-20.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -6501,7 +6501,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-20.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
@@ -6524,7 +6524,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-20.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -6545,7 +6545,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-20.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -6566,7 +6566,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-20.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -6587,7 +6587,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-20.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -6608,7 +6608,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-20.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -6629,7 +6629,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-20.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -6650,7 +6650,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-20.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -6671,7 +6671,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-20.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -6692,7 +6692,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-20.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -6713,7 +6713,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-20.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -6734,7 +6734,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-20.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -6755,7 +6755,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-20.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -6776,7 +6776,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-20.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -6797,7 +6797,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-20.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -6818,7 +6818,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-20.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -6839,7 +6839,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-20.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -6860,7 +6860,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-20.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -6881,7 +6881,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-20.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -6902,7 +6902,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-20.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -6923,7 +6923,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-20.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -6944,7 +6944,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-20.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
@@ -6966,7 +6966,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-20.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -6988,7 +6988,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-20.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -7009,7 +7009,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-20.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -7030,7 +7030,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-20.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -7051,7 +7051,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-20.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -7072,7 +7072,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-20.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -7093,7 +7093,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-20.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -7114,7 +7114,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-20.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -7135,7 +7135,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-20.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
diff --git a/testing/buildbot/chromium.reclient.fyi.json b/testing/buildbot/chromium.reclient.fyi.json
index 29c9c54d..b90b2f8 100644
--- a/testing/buildbot/chromium.reclient.fyi.json
+++ b/testing/buildbot/chromium.reclient.fyi.json
@@ -6,16 +6,6 @@
       "all"
     ]
   },
-  "Comparison Linux (reclient vs reclient remote links)(large)": {
-    "additional_compile_targets": [
-      "all"
-    ]
-  },
-  "Comparison Linux (reclient vs reclient remote links)(medium)": {
-    "additional_compile_targets": [
-      "all"
-    ]
-  },
   "Comparison Linux (reclient vs reclient remote links)(small)": {
     "additional_compile_targets": [
       "all"
diff --git a/testing/buildbot/filters/linux.linux-rel-cft.browser_tests.filter b/testing/buildbot/filters/linux.linux-rel-cft.browser_tests.filter
index 2d996d4..02ddd075 100644
--- a/testing/buildbot/filters/linux.linux-rel-cft.browser_tests.filter
+++ b/testing/buildbot/filters/linux.linux-rel-cft.browser_tests.filter
@@ -1,15 +1,26 @@
 # Filter out failing infobar tests from Chrome for Testing trybots
 
 # Chrome for Testing (CfT) adds a global info bar.
-# As a side effect, this fails all tests that explicitly assert the
-# infobar count, as they always differ by one.
+# As a side effect, this fails tests that explicitly assert:
+#   - the infobar count
+#   - the number of ax::mojom::Role::kAlert fired events
+#   - height of DOM elements and viewport size
+#   - fullscreen mode and desktop web app custom title bar styling
+#   - default focused elements or focusable elements count
 
 # Since those tests are already covered by non-cft trybots, we just disable
 # them for cft trybots.
 
 # https://ci.chromium.org/ui/p/chromium/builders/try/linux-rel/1222923
+# https://ci.chromium.org/ui/p/chromium/builders/ci/linux-rel-cft/2264
+-All/GetDisplayMediaChangeSourceBrowserTest.*
+-All/PageSpecificSiteDataDialogBrowserTest.*
+-All/TabSharingUIViewsBrowserTest.*
 -BackForwardCachePageLoadMetricsObserverBrowserTest.CumulativeLayoutShiftAfterBackForwardCacheRestore
 -BackForwardCachePageLoadMetricsObserverBrowserTest.LayoutShiftNormalization_AfterBackForwardCacheRestore
+-BreadcrumbManagerTabHelperSecurityStateBrowserTest.BrokenAuthentication
+-BrowserFocusTest.AppLocationBar
+-BrowserTest.GetSizeForNewRenderView
 -CollectedCookiesViewsTest.ChangeAndCloseDialog
 -CollectedCookiesViewsTest.ChangeAndNavigateAway
 -CollectedCookiesViewsTest.CloseDialog
@@ -25,37 +36,20 @@
 -InfoBarsTest.TestInfoBarsCloseOnNewTheme
 -KnownInterceptionDisclosureInfobarTest.CooldownResetsOnBrowserRestartDesktop
 -KnownInterceptionDisclosureInfobarTest.OnlyShowDisclosureOncePerSession
+-LayoutInstabilityTest.Sources_Enclosure
 -MultipleTabSharingUIViewsBrowserTest.CloseTabs
 -MultipleTabSharingUIViewsBrowserTest.StopSharing
 -MultipleTabSharingUIViewsBrowserTest.VerifyUi
+-PopupBlockerBrowserTest.WindowFeatures
+-SaveCardBubbleViewsFullFormBrowserTest.AlertAccessibleEvent
+-SessionRestoreTest.RestoredTabsHaveCorrectInitialSize
+-SitePerProcessInteractiveBrowserTest.FullscreenElementInABAAndExitViaEscapeKey
+-SitePerProcessInteractiveBrowserTest.FullscreenElementInSubframe
 -TabSharingUIViewsPreferCurrentTabBrowserTest.VerifyUiWhenCapturingAnotherTab
--WebAppIntegration.ToggleWindowControlsOverlay
--WebAppIntegration.WindowControlsOverlayStatePreservesBetweenLaunches
+-TabSharingUIViewsPreferCurrentTabBrowserTest.VerifyUiWhenSelfCapturing
+-WebAppFrameToolbarBrowserTest_Borderless.*
+-WebAppFrameToolbarBrowserTest_WindowControlsOverlay.*
+-WebAppIntegration.*
+-WebContentsInteractionTestUtilInteractiveUiTest.NavigateMenuAndBringUpDownloadsPageThenOpenMoreActionsMenu
 -WebRtcDesktopCaptureBrowserTest.CloseAndReopenNonSharedTab
 -WebRtcDesktopCaptureBrowserTest.SwitchSharedTabBackAndForth
--All/PageSpecificSiteDataDialogBrowserTest.AllowMenuItem/1
--All/PageSpecificSiteDataDialogBrowserTest.BlockMenuItem/1
--All/PageSpecificSiteDataDialogBrowserTest.ClearOnExitMenuItem/1
--All/PageSpecificSiteDataDialogBrowserTest.CloseDialog/0
--All/PageSpecificSiteDataDialogBrowserTest.CloseDialog/1
--All/PageSpecificSiteDataDialogBrowserTest.DeleteMenuItem/1
--All/TabSharingUIViewsBrowserTest.ChangeCapturedTabFavicon/0
--All/TabSharingUIViewsBrowserTest.ChangeCapturedTabFavicon/1
--All/TabSharingUIViewsBrowserTest.ChangeCapturingTabFavicon/0
--All/TabSharingUIViewsBrowserTest.ChangeCapturingTabFavicon/1
--All/TabSharingUIViewsBrowserTest.ChangeOtherTabFavicon/0
--All/TabSharingUIViewsBrowserTest.ChangeOtherTabFavicon/1
--All/TabSharingUIViewsBrowserTest.CloseTab/0
--All/TabSharingUIViewsBrowserTest.CloseTab/1
--All/TabSharingUIViewsBrowserTest.CloseTabInIncognitoBrowser/0
--All/TabSharingUIViewsBrowserTest.CloseTabInIncognitoBrowser/1
--All/TabSharingUIViewsBrowserTest.InfobarLabelUpdatedOnNavigation/0
--All/TabSharingUIViewsBrowserTest.InfobarLabelUpdatedOnNavigation/1
--All/TabSharingUIViewsBrowserTest.KillSharedTab/0
--All/TabSharingUIViewsBrowserTest.KillSharedTab/1
--All/TabSharingUIViewsBrowserTest.StartSharing/0
--All/TabSharingUIViewsBrowserTest.StartSharing/1
--All/TabSharingUIViewsBrowserTest.StopSharing/0
--All/TabSharingUIViewsBrowserTest.StopSharing/1
--All/TabSharingUIViewsBrowserTest.SwitchSharedTab/0
--All/TabSharingUIViewsBrowserTest.SwitchSharedTab/1
diff --git a/testing/buildbot/filters/mac.mac-rel-cft.browser_tests.filter b/testing/buildbot/filters/mac.mac-rel-cft.browser_tests.filter
index 2d996d4..fcf85e46 100644
--- a/testing/buildbot/filters/mac.mac-rel-cft.browser_tests.filter
+++ b/testing/buildbot/filters/mac.mac-rel-cft.browser_tests.filter
@@ -1,15 +1,26 @@
 # Filter out failing infobar tests from Chrome for Testing trybots
 
 # Chrome for Testing (CfT) adds a global info bar.
-# As a side effect, this fails all tests that explicitly assert the
-# infobar count, as they always differ by one.
+# As a side effect, this fails tests that explicitly assert:
+#   - the infobar count
+#   - the number of ax::mojom::Role::kAlert fired events
+#   - height of DOM elements and viewport size
+#   - fullscreen mode and desktop web app custom title bar styling
+#   - default focused elements or focusable elements count
 
 # Since those tests are already covered by non-cft trybots, we just disable
 # them for cft trybots.
 
 # https://ci.chromium.org/ui/p/chromium/builders/try/linux-rel/1222923
+# https://ci.chromium.org/ui/p/chromium/builders/ci/mac-rel-cft/473
+-All/GetDisplayMediaChangeSourceBrowserTest.*
+-All/PageSpecificSiteDataDialogBrowserTest.*
+-All/TabSharingUIViewsBrowserTest.*
 -BackForwardCachePageLoadMetricsObserverBrowserTest.CumulativeLayoutShiftAfterBackForwardCacheRestore
 -BackForwardCachePageLoadMetricsObserverBrowserTest.LayoutShiftNormalization_AfterBackForwardCacheRestore
+-BreadcrumbManagerTabHelperSecurityStateBrowserTest.BrokenAuthentication
+-BrowserFocusTest.AppLocationBar
+-BrowserTest.GetSizeForNewRenderView
 -CollectedCookiesViewsTest.ChangeAndCloseDialog
 -CollectedCookiesViewsTest.ChangeAndNavigateAway
 -CollectedCookiesViewsTest.CloseDialog
@@ -25,37 +36,20 @@
 -InfoBarsTest.TestInfoBarsCloseOnNewTheme
 -KnownInterceptionDisclosureInfobarTest.CooldownResetsOnBrowserRestartDesktop
 -KnownInterceptionDisclosureInfobarTest.OnlyShowDisclosureOncePerSession
+-LayoutInstabilityTest.Sources_Enclosure
 -MultipleTabSharingUIViewsBrowserTest.CloseTabs
 -MultipleTabSharingUIViewsBrowserTest.StopSharing
 -MultipleTabSharingUIViewsBrowserTest.VerifyUi
+-PopupBlockerBrowserTest.WindowFeatures
+-SaveCardBubbleViewsFullFormBrowserTest.AlertAccessibleEvent
+-SessionRestoreTest.RestoredTabsHaveCorrectInitialSize
+-SitePerProcessInteractiveBrowserTest.FullscreenElementInABAAndExitViaEscapeKey
+-SitePerProcessInteractiveBrowserTest.FullscreenElementInSubframe
 -TabSharingUIViewsPreferCurrentTabBrowserTest.VerifyUiWhenCapturingAnotherTab
--WebAppIntegration.ToggleWindowControlsOverlay
--WebAppIntegration.WindowControlsOverlayStatePreservesBetweenLaunches
+-TabSharingUIViewsPreferCurrentTabBrowserTest.VerifyUiWhenSelfCapturing
+-WebAppFrameToolbarBrowserTest_Borderless.*
+-WebAppFrameToolbarBrowserTest_WindowControlsOverlay.*
+-WebAppIntegration.*
+-WebContentsInteractionTestUtilInteractiveUiTest.NavigateMenuAndBringUpDownloadsPageThenOpenMoreActionsMenu
 -WebRtcDesktopCaptureBrowserTest.CloseAndReopenNonSharedTab
 -WebRtcDesktopCaptureBrowserTest.SwitchSharedTabBackAndForth
--All/PageSpecificSiteDataDialogBrowserTest.AllowMenuItem/1
--All/PageSpecificSiteDataDialogBrowserTest.BlockMenuItem/1
--All/PageSpecificSiteDataDialogBrowserTest.ClearOnExitMenuItem/1
--All/PageSpecificSiteDataDialogBrowserTest.CloseDialog/0
--All/PageSpecificSiteDataDialogBrowserTest.CloseDialog/1
--All/PageSpecificSiteDataDialogBrowserTest.DeleteMenuItem/1
--All/TabSharingUIViewsBrowserTest.ChangeCapturedTabFavicon/0
--All/TabSharingUIViewsBrowserTest.ChangeCapturedTabFavicon/1
--All/TabSharingUIViewsBrowserTest.ChangeCapturingTabFavicon/0
--All/TabSharingUIViewsBrowserTest.ChangeCapturingTabFavicon/1
--All/TabSharingUIViewsBrowserTest.ChangeOtherTabFavicon/0
--All/TabSharingUIViewsBrowserTest.ChangeOtherTabFavicon/1
--All/TabSharingUIViewsBrowserTest.CloseTab/0
--All/TabSharingUIViewsBrowserTest.CloseTab/1
--All/TabSharingUIViewsBrowserTest.CloseTabInIncognitoBrowser/0
--All/TabSharingUIViewsBrowserTest.CloseTabInIncognitoBrowser/1
--All/TabSharingUIViewsBrowserTest.InfobarLabelUpdatedOnNavigation/0
--All/TabSharingUIViewsBrowserTest.InfobarLabelUpdatedOnNavigation/1
--All/TabSharingUIViewsBrowserTest.KillSharedTab/0
--All/TabSharingUIViewsBrowserTest.KillSharedTab/1
--All/TabSharingUIViewsBrowserTest.StartSharing/0
--All/TabSharingUIViewsBrowserTest.StartSharing/1
--All/TabSharingUIViewsBrowserTest.StopSharing/0
--All/TabSharingUIViewsBrowserTest.StopSharing/1
--All/TabSharingUIViewsBrowserTest.SwitchSharedTab/0
--All/TabSharingUIViewsBrowserTest.SwitchSharedTab/1
diff --git a/testing/buildbot/filters/win.win-rel-cft.browser_tests.filter b/testing/buildbot/filters/win.win-rel-cft.browser_tests.filter
index fde040dd4..26be4f0 100644
--- a/testing/buildbot/filters/win.win-rel-cft.browser_tests.filter
+++ b/testing/buildbot/filters/win.win-rel-cft.browser_tests.filter
@@ -1,15 +1,26 @@
 # Filter out failing infobar tests from Chrome for Testing trybots
 
 # Chrome for Testing (CfT) adds a global info bar.
-# As a side effect, this fails all tests that explicitly assert the
-# infobar count, as they always differ by one.
+# As a side effect, this fails tests that explicitly assert:
+#   - the infobar count
+#   - the number of ax::mojom::Role::kAlert fired events
+#   - height of DOM elements and viewport size
+#   - fullscreen mode and desktop web app custom title bar styling
+#   - default focused elements or focusable elements count
 
 # Since those tests are already covered by non-cft trybots, we just disable
 # them for cft trybots.
 
 # https://ci.chromium.org/ui/p/chromium/builders/try/linux-rel/1222923
+# https://ci.chromium.org/ui/p/chromium/builders/ci/win-rel-cft/1705
+-All/GetDisplayMediaChangeSourceBrowserTest.*
+-All/PageSpecificSiteDataDialogBrowserTest.*
+-All/TabSharingUIViewsBrowserTest.*
 -BackForwardCachePageLoadMetricsObserverBrowserTest.CumulativeLayoutShiftAfterBackForwardCacheRestore
 -BackForwardCachePageLoadMetricsObserverBrowserTest.LayoutShiftNormalization_AfterBackForwardCacheRestore
+-BreadcrumbManagerTabHelperSecurityStateBrowserTest.BrokenAuthentication
+-BrowserFocusTest.AppLocationBar
+-BrowserTest.GetSizeForNewRenderView
 -CollectedCookiesViewsTest.ChangeAndCloseDialog
 -CollectedCookiesViewsTest.ChangeAndNavigateAway
 -CollectedCookiesViewsTest.CloseDialog
@@ -19,47 +30,28 @@
 -DevToolsProtocolTest.AutomationOverrideAddsOneInfoBarOnly
 -DevToolsProtocolTest.AutomationOverrideShowsAndRemovesInfoBar
 -ExtensionInstallUIBrowserTest.TestThemeInstallUndoResetsToPreviousTheme
+-FindInPageControllerTest.FindMovesWhenObscuring
 -GlobalConfirmInfoBarTest.CreateAndCloseInfobar
 -GlobalConfirmInfoBarTest.UserInteraction
 -GlobalConfirmInfoBarTest.VerifyInfobarNonDefaultProperties
 -InfoBarsTest.TestInfoBarsCloseOnNewTheme
+-InteractionTestUtilBrowserTest.CompareScreenshot_WebPage
 -KnownInterceptionDisclosureInfobarTest.CooldownResetsOnBrowserRestartDesktop
 -KnownInterceptionDisclosureInfobarTest.OnlyShowDisclosureOncePerSession
+-LayoutInstabilityTest.Sources_Enclosure
 -MultipleTabSharingUIViewsBrowserTest.CloseTabs
 -MultipleTabSharingUIViewsBrowserTest.StopSharing
 -MultipleTabSharingUIViewsBrowserTest.VerifyUi
+-PopupBlockerBrowserTest.WindowFeatures
+-SaveCardBubbleViewsFullFormBrowserTest.AlertAccessibleEvent
+-SessionRestoreTest.RestoredTabsHaveCorrectInitialSize
+-SitePerProcessInteractiveBrowserTest.FullscreenElementInABAAndExitViaEscapeKey
+-SitePerProcessInteractiveBrowserTest.FullscreenElementInSubframe
 -TabSharingUIViewsPreferCurrentTabBrowserTest.VerifyUiWhenCapturingAnotherTab
--WebAppIntegration.ToggleWindowControlsOverlay
--WebAppIntegration.WindowControlsOverlayStatePreservesBetweenLaunches
+-TabSharingUIViewsPreferCurrentTabBrowserTest.VerifyUiWhenSelfCapturing
+-WebAppFrameToolbarBrowserTest_Borderless.*
+-WebAppFrameToolbarBrowserTest_WindowControlsOverlay.*
+-WebAppIntegration.*
+-WebContentsInteractionTestUtilInteractiveUiTest.NavigateMenuAndBringUpDownloadsPageThenOpenMoreActionsMenu
 -WebRtcDesktopCaptureBrowserTest.CloseAndReopenNonSharedTab
 -WebRtcDesktopCaptureBrowserTest.SwitchSharedTabBackAndForth
--All/PageSpecificSiteDataDialogBrowserTest.AllowMenuItem/1
--All/PageSpecificSiteDataDialogBrowserTest.BlockMenuItem/1
--All/PageSpecificSiteDataDialogBrowserTest.ClearOnExitMenuItem/1
--All/PageSpecificSiteDataDialogBrowserTest.CloseDialog/0
--All/PageSpecificSiteDataDialogBrowserTest.CloseDialog/1
--All/PageSpecificSiteDataDialogBrowserTest.DeleteMenuItem/1
--All/TabSharingUIViewsBrowserTest.ChangeCapturedTabFavicon/0
--All/TabSharingUIViewsBrowserTest.ChangeCapturedTabFavicon/1
--All/TabSharingUIViewsBrowserTest.ChangeCapturingTabFavicon/0
--All/TabSharingUIViewsBrowserTest.ChangeCapturingTabFavicon/1
--All/TabSharingUIViewsBrowserTest.ChangeOtherTabFavicon/0
--All/TabSharingUIViewsBrowserTest.ChangeOtherTabFavicon/1
--All/TabSharingUIViewsBrowserTest.CloseTab/0
--All/TabSharingUIViewsBrowserTest.CloseTab/1
--All/TabSharingUIViewsBrowserTest.CloseTabInIncognitoBrowser/0
--All/TabSharingUIViewsBrowserTest.CloseTabInIncognitoBrowser/1
--All/TabSharingUIViewsBrowserTest.InfobarLabelUpdatedOnNavigation/0
--All/TabSharingUIViewsBrowserTest.InfobarLabelUpdatedOnNavigation/1
--All/TabSharingUIViewsBrowserTest.KillSharedTab/0
--All/TabSharingUIViewsBrowserTest.KillSharedTab/1
--All/TabSharingUIViewsBrowserTest.StartSharing/0
--All/TabSharingUIViewsBrowserTest.StartSharing/1
--All/TabSharingUIViewsBrowserTest.StopSharing/0
--All/TabSharingUIViewsBrowserTest.StopSharing/1
--All/TabSharingUIViewsBrowserTest.SwitchSharedTab/0
--All/TabSharingUIViewsBrowserTest.SwitchSharedTab/1
-
-# https://ci.chromium.org/ui/p/chromium/builders/try/win-rel/25038/test-results
--FindInPageControllerTest.FindMovesWhenObscuring
--InteractionTestUtilBrowserTest.CompareScreenshot_WebPage
diff --git a/testing/buildbot/test_suite_exceptions.pyl b/testing/buildbot/test_suite_exceptions.pyl
index 3a163272..c045d8f 100644
--- a/testing/buildbot/test_suite_exceptions.pyl
+++ b/testing/buildbot/test_suite_exceptions.pyl
@@ -127,7 +127,6 @@
       # Disabling MSan tests since linux-chromeos chrome crashes on MSan build.
       # TODO(crbug.com/1376064): Re-enable.
       'Linux ChromiumOS MSan Tests',
-      'Linux ChromiumOS MSan Focal',
     ]
   },
   'aura_unittests amd64-generic': {
@@ -901,16 +900,6 @@
           'shards': 60,
         },
       },
-      'Linux ChromiumOS MSan Focal': {
-        # These are very slow on the Chrome OS MSAN trybot for some reason.
-        # crbug.com/865455
-        'swarming': {
-          'shards': 40,
-        },
-        'args': [
-          '--test-launcher-filter-file=../../testing/buildbot/filters/chromeos.msan.browser_tests.oobe_negative.filter',
-        ],
-      },
       'Linux ChromiumOS MSan Tests': {
         # These are very slow on the Chrome OS MSAN trybot, most likely because browser_tests on cros has ~40% more tests. Also, these tests
         # run on ash, which means every test starts and shuts down ash, which most likely explains why it takes longer than on other platforms.
@@ -2290,7 +2279,6 @@
       # Can't run on MSAN because gl_unittests_ozone uses the hardware driver,
       # which isn't instrumented.
       'Linux ChromiumOS MSan Tests',
-      'Linux ChromiumOS MSan Focal',
     ],
   },
   'gold_common_pytype': {
@@ -2447,13 +2435,6 @@
           'quickrun_shards': 12,
         },
       },
-      'Linux ChromiumOS MSan Focal': {
-        # These are very slow on the Chrome OS MSAN trybot for some reason.
-        # crbug.com/865455
-        'swarming': {
-          'shards': 5,
-        },
-      },
       'Linux ChromiumOS MSan Tests': {
         # These are very slow on the Chrome OS MSAN trybot for some reason.
         # crbug.com/865455
@@ -3200,7 +3181,6 @@
       # The face and barcode detection tests fail on the Mac Pros.
       'Mac Pro FYI Release (AMD)',
       'Linux ChromiumOS MSan Tests',  # https://crbug.com/831676
-      'Linux ChromiumOS MSan Focal',
       'Linux MSan Tests',  # https://crbug.com/831676
     ],
     'replacements': {
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl
index 86e99b0..313a1f74 100644
--- a/testing/buildbot/waterfalls.pyl
+++ b/testing/buildbot/waterfalls.pyl
@@ -2798,22 +2798,6 @@
           'gtest_tests': 'chromium_linux_and_gl_gtests',
         },
       },
-      'Linux ChromiumOS MSan Focal': {
-        'mixins': [
-          'linux-focal',
-          'x86-64',
-        ],
-        'swarming': {
-          'expiration': 10800,
-          'hard_timeout': 7200,
-        },
-        'test_suites': {
-          'gtest_tests': 'linux_chromeos_gtests_oobe',
-        },
-        'args': [
-          '--test-launcher-print-test-stdio=always',
-        ],
-      },
       'Linux Viz': {
         'mixins': [
           'linux-bionic',
@@ -5121,7 +5105,7 @@
       },
       'Linux ChromiumOS MSan Tests': {
         'mixins': [
-          'linux-xenial',
+          'linux-focal',
           'x86-64',
         ],
         'test_suites': {
@@ -5290,22 +5274,6 @@
           'all'
         ],
       },
-      'Comparison Linux (reclient vs reclient remote links)(large)': {
-        'mixins': [
-          'isolate_profile_data',
-        ],
-        'additional_compile_targets': [
-          'all'
-        ],
-      },
-      'Comparison Linux (reclient vs reclient remote links)(medium)': {
-        'mixins': [
-          'isolate_profile_data',
-        ],
-        'additional_compile_targets': [
-          'all'
-        ],
-      },
       'Comparison Linux (reclient vs reclient remote links)(small)': {
         'mixins': [
           'isolate_profile_data',
diff --git a/testing/scripts/wpt_common.py b/testing/scripts/wpt_common.py
index d978012f..9f341465 100644
--- a/testing/scripts/wpt_common.py
+++ b/testing/scripts/wpt_common.py
@@ -51,26 +51,6 @@
         self._include_filename = None
         self.layout_test_results_subdir = 'layout-test-results'
 
-    @property
-    def wpt_binary(self):
-        default_wpt_binary = os.path.join(
-            common.SRC_DIR, "third_party", "wpt_tools", "wpt", "wpt")
-        return os.environ.get("WPT_BINARY", default_wpt_binary)
-
-    @property
-    def wpt_root_dir(self):
-        return self.path_finder.path_from_web_tests(
-            self.path_finder.wpt_prefix())
-
-    @property
-    def output_directory(self):
-        return self.path_finder.path_from_chromium_base('out',
-                                                        self.options.target)
-
-    @property
-    def mojo_js_directory(self):
-        return self.fs.join(self.output_directory, 'gen')
-
     def add_extra_arguments(self, parser):
         parser.add_argument(
             '-t',
@@ -364,10 +344,6 @@
                             self.wptreport])
         return common.run_command(command)
 
-    def clean_up_after_test_run(self):
-        if self._include_filename:
-            self.fs.remove(self._include_filename)
-
     def wpt_product_name(self):
         raise NotImplementedError
 
diff --git a/testing/unexpected_passes_common/expectations.py b/testing/unexpected_passes_common/expectations.py
index 9a60c4d4..634db3a 100644
--- a/testing/unexpected_passes_common/expectations.py
+++ b/testing/unexpected_passes_common/expectations.py
@@ -623,6 +623,21 @@
       if skip_to_next_expectation:
         continue
 
+      # Remove all instances of tags that are shared between all sets other than
+      # the tags that were used by the expectation, as they are redundant.
+      common_tags = set()
+      for ts in pass_tag_sets:
+        common_tags |= ts
+        # We only need one initial tag set, but sets do not have a way of
+        # retrieving a single element other than pop(), which removes the
+        # element, which we don't want.
+        break
+      for ts in pass_tag_sets | fail_tag_sets:
+        common_tags &= ts
+      common_tags -= e.tags
+      pass_tag_sets = {ts - common_tags for ts in pass_tag_sets}
+      fail_tag_sets = {ts - common_tags for ts in fail_tag_sets}
+
       # Calculate new tag sets that should be functionally equivalent to the
       # single, more broad tag set that we are replacing. This is done by
       # checking if the intersection between any pairs of fail tag sets are
diff --git a/testing/unexpected_passes_common/expectations_unittest.py b/testing/unexpected_passes_common/expectations_unittest.py
index bd270f7..414a06c 100755
--- a/testing/unexpected_passes_common/expectations_unittest.py
+++ b/testing/unexpected_passes_common/expectations_unittest.py
@@ -1528,6 +1528,46 @@
                        FAKE_EXPECTATION_FILE_CONTENTS_WITH_COMPLEX_TAGS)
     self.assertEqual(urls, set())
 
+  def testRemoveCommonTags(self) -> None:
+    """Tests that scope narrowing removes common/redundant tags."""
+    amd_stats = data_types.BuildStats()
+    amd_stats.AddPassedBuild(frozenset(['win', 'amd', 'desktop']))
+    intel_stats = data_types.BuildStats()
+    intel_stats.AddFailedBuild('1', frozenset(['win', 'intel', 'desktop']))
+    # yapf: disable
+    test_expectation_map = data_types.TestExpectationMap({
+        self.filename:
+        data_types.ExpectationBuilderMap({
+            data_types.Expectation(
+                'foo/test', ['win'], 'Failure', 'crbug.com/1234'):
+            data_types.BuilderStepMap({
+                'win_builder':
+                data_types.StepBuildStatsMap({
+                    'amd': amd_stats,
+                    'intel': intel_stats,
+                }),
+            }),
+        }),
+    })
+    # yapf: enable
+    urls = self.instance.NarrowSemiStaleExpectationScope(test_expectation_map)
+    expected_contents = """\
+# tags: [ win win10
+#         linux
+#         mac ]
+# tags: [ nvidia nvidia-0x1111
+#         intel intel-0x2222
+#         amd amd-0x3333]
+# tags: [ release debug ]
+# results: [ Failure RetryOnFailure ]
+
+crbug.com/1234 [ intel win ] foo/test [ Failure ]
+crbug.com/2345 [ linux ] foo/test [ RetryOnFailure ]
+"""
+    with open(self.filename) as infile:
+      self.assertEqual(infile.read(), expected_contents)
+    self.assertEqual(urls, {'crbug.com/1234'})
+
   def testConsolidateKnownOverlappingTags(self) -> None:
     """Tests that scope narrowing consolidates known overlapping tags."""
 
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index b3070a8..86de20f 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -2160,6 +2160,21 @@
             ]
         }
     ],
+    "CacheMacSandboxProfiles": [
+        {
+            "platforms": [
+                "mac"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "CacheMacSandboxProfiles"
+                    ]
+                }
+            ]
+        }
+    ],
     "CacheTransparency": [
         {
             "platforms": [
diff --git a/third_party/blink/public/mojom/screen_enumeration/DIR_METADATA b/third_party/blink/public/mojom/screen_enumeration/DIR_METADATA
deleted file mode 100644
index e7483ec7..0000000
--- a/third_party/blink/public/mojom/screen_enumeration/DIR_METADATA
+++ /dev/null
@@ -1,5 +0,0 @@
-monorail {
-  component: "Blink>Storage"
-}
-
-team_email: "storage-dev@chromium.org"
diff --git a/third_party/blink/public/mojom/screen_enumeration/OWNERS b/third_party/blink/public/mojom/screen_enumeration/OWNERS
deleted file mode 100644
index 505334c1..0000000
--- a/third_party/blink/public/mojom/screen_enumeration/OWNERS
+++ /dev/null
@@ -1,4 +0,0 @@
-file://content/browser/screen_enumeration/OWNERS
-
-per-file *.mojom=set noparent
-per-file *.mojom=file://ipc/SECURITY_OWNERS
diff --git a/third_party/blink/renderer/bindings/idl_in_modules.gni b/third_party/blink/renderer/bindings/idl_in_modules.gni
index 1d2251f..1281c5b 100644
--- a/third_party/blink/renderer/bindings/idl_in_modules.gni
+++ b/third_party/blink/renderer/bindings/idl_in_modules.gni
@@ -656,9 +656,9 @@
           "//third_party/blink/renderer/modules/scheduler/task_priority_change_event_init.idl",
           "//third_party/blink/renderer/modules/scheduler/task_signal.idl",
           "//third_party/blink/renderer/modules/scheduler/window_or_worker_scheduler.idl",
-          "//third_party/blink/renderer/modules/screen_enumeration/screen_detailed.idl",
-          "//third_party/blink/renderer/modules/screen_enumeration/screen_details.idl",
-          "//third_party/blink/renderer/modules/screen_enumeration/window_screens.idl",
+          "//third_party/blink/renderer/modules/screen_details/screen_detailed.idl",
+          "//third_party/blink/renderer/modules/screen_details/screen_details.idl",
+          "//third_party/blink/renderer/modules/screen_details/window_screen_details.idl",
           "//third_party/blink/renderer/modules/screen_orientation/screen_orientation.idl",
           "//third_party/blink/renderer/modules/screen_orientation/screen_screen_orientation.idl",
           "//third_party/blink/renderer/modules/sensor/absolute_orientation_sensor.idl",
diff --git a/third_party/blink/renderer/modules/BUILD.gn b/third_party/blink/renderer/modules/BUILD.gn
index c4325d1..ce00a77 100644
--- a/third_party/blink/renderer/modules/BUILD.gn
+++ b/third_party/blink/renderer/modules/BUILD.gn
@@ -134,7 +134,7 @@
     "//third_party/blink/renderer/modules/remoteplayback",
     "//third_party/blink/renderer/modules/sanitizer_api",
     "//third_party/blink/renderer/modules/scheduler",
-    "//third_party/blink/renderer/modules/screen_enumeration",
+    "//third_party/blink/renderer/modules/screen_details",
     "//third_party/blink/renderer/modules/screen_orientation",
     "//third_party/blink/renderer/modules/sensor",
     "//third_party/blink/renderer/modules/service_worker",
diff --git a/third_party/blink/renderer/modules/mediastream/BUILD.gn b/third_party/blink/renderer/modules/mediastream/BUILD.gn
index 0f83d20..e4110c2c 100644
--- a/third_party/blink/renderer/modules/mediastream/BUILD.gn
+++ b/third_party/blink/renderer/modules/mediastream/BUILD.gn
@@ -120,7 +120,7 @@
     "//services/viz/public/cpp/gpu:gpu",
     "//third_party/blink/public/common",
     "//third_party/blink/renderer/modules/imagecapture:imagecapture",
-    "//third_party/blink/renderer/modules/screen_enumeration:screen_enumeration",
+    "//third_party/blink/renderer/modules/screen_details:screen_details",
     "//third_party/libyuv:libyuv",
   ]
   allow_circular_includes_from =
diff --git a/third_party/blink/renderer/modules/mediastream/DEPS b/third_party/blink/renderer/modules/mediastream/DEPS
index 3d22823..cfd2875 100644
--- a/third_party/blink/renderer/modules/mediastream/DEPS
+++ b/third_party/blink/renderer/modules/mediastream/DEPS
@@ -52,8 +52,8 @@
     "+third_party/blink/renderer/modules/mediastream",
     "+third_party/blink/renderer/modules/modules_export.h",
     "+third_party/blink/renderer/modules/peerconnection",
-    "+third_party/blink/renderer/modules/screen_enumeration/screen_detailed.h",
-    "+third_party/blink/renderer/modules/screen_enumeration/screen_details.h",
+    "+third_party/blink/renderer/modules/screen_details/screen_detailed.h",
+    "+third_party/blink/renderer/modules/screen_details/screen_details.h",
     "+third_party/blink/renderer/modules/webrtc",
     "+ui/display/types/display_constants.h",
     "+ui/gfx/geometry/size.h",
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream_set.cc b/third_party/blink/renderer/modules/mediastream/media_stream_set.cc
index 1b51c525..97b5e3532 100644
--- a/third_party/blink/renderer/modules/mediastream/media_stream_set.cc
+++ b/third_party/blink/renderer/modules/mediastream/media_stream_set.cc
@@ -10,8 +10,8 @@
 #include "third_party/blink/renderer/modules/mediastream/screen_capture_media_stream_track.h"
 #include "third_party/blink/renderer/modules/mediastream/user_media_request.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
-#include "third_party/blink/renderer/modules/screen_enumeration/screen_detailed.h"
-#include "third_party/blink/renderer/modules/screen_enumeration/screen_details.h"
+#include "third_party/blink/renderer/modules/screen_details/screen_detailed.h"
+#include "third_party/blink/renderer/modules/screen_details/screen_details.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
 #include "third_party/blink/renderer/platform/wtf/vector.h"
 #include "third_party/blink/renderer/platform/wtf/wtf_size_t.h"
diff --git a/third_party/blink/renderer/modules/mediastream/screen_capture_media_stream_track.cc b/third_party/blink/renderer/modules/mediastream/screen_capture_media_stream_track.cc
index 7e18ac7..f862814 100644
--- a/third_party/blink/renderer/modules/mediastream/screen_capture_media_stream_track.cc
+++ b/third_party/blink/renderer/modules/mediastream/screen_capture_media_stream_track.cc
@@ -9,8 +9,8 @@
 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/modules/mediastream/user_media_request.h"
-#include "third_party/blink/renderer/modules/screen_enumeration/screen_detailed.h"
-#include "third_party/blink/renderer/modules/screen_enumeration/screen_details.h"
+#include "third_party/blink/renderer/modules/screen_details/screen_detailed.h"
+#include "third_party/blink/renderer/modules/screen_details/screen_details.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/bindings/script_state.h"
 
diff --git a/third_party/blink/renderer/modules/modules_initializer.cc b/third_party/blink/renderer/modules/modules_initializer.cc
index 3c7f557..47fa43f 100644
--- a/third_party/blink/renderer/modules/modules_initializer.cc
+++ b/third_party/blink/renderer/modules/modules_initializer.cc
@@ -79,8 +79,8 @@
 #include "third_party/blink/renderer/modules/remoteplayback/html_media_element_remote_playback.h"
 #include "third_party/blink/renderer/modules/remoteplayback/remote_playback.h"
 #include "third_party/blink/renderer/modules/scheduler/task_attribution_tracker_impl.h"
-#include "third_party/blink/renderer/modules/screen_enumeration/screen_details.h"
-#include "third_party/blink/renderer/modules/screen_enumeration/window_screens.h"
+#include "third_party/blink/renderer/modules/screen_details/screen_details.h"
+#include "third_party/blink/renderer/modules/screen_details/window_screen_details.h"
 #include "third_party/blink/renderer/modules/screen_orientation/screen_orientation_controller.h"
 #include "third_party/blink/renderer/modules/service_worker/navigator_service_worker.h"
 #include "third_party/blink/renderer/modules/speech/speech_synthesis.h"
@@ -403,7 +403,7 @@
     const display::ScreenInfos& screen_infos) {
   auto* window = frame.DomWindow();
   if (auto* supplement =
-          Supplement<LocalDOMWindow>::From<WindowScreens>(window)) {
+          Supplement<LocalDOMWindow>::From<WindowScreenDetails>(window)) {
     // screen_details() may be null if permission has not been granted.
     if (auto* screen_details = supplement->screen_details()) {
       screen_details->UpdateScreenInfos(window, screen_infos);
diff --git a/third_party/blink/renderer/modules/screen_enumeration/BUILD.gn b/third_party/blink/renderer/modules/screen_details/BUILD.gn
similarity index 82%
rename from third_party/blink/renderer/modules/screen_enumeration/BUILD.gn
rename to third_party/blink/renderer/modules/screen_details/BUILD.gn
index 4314913..94deae1b 100644
--- a/third_party/blink/renderer/modules/screen_enumeration/BUILD.gn
+++ b/third_party/blink/renderer/modules/screen_details/BUILD.gn
@@ -4,14 +4,14 @@
 
 import("//third_party/blink/renderer/modules/modules.gni")
 
-blink_modules_sources("screen_enumeration") {
+blink_modules_sources("screen_details") {
   sources = [
     "screen_detailed.cc",
     "screen_detailed.h",
     "screen_details.cc",
     "screen_details.h",
-    "window_screens.cc",
-    "window_screens.h",
+    "window_screen_details.cc",
+    "window_screen_details.h",
   ]
 
   public_deps = [
diff --git a/third_party/blink/renderer/modules/screen_enumeration/DEPS b/third_party/blink/renderer/modules/screen_details/DEPS
similarity index 100%
rename from third_party/blink/renderer/modules/screen_enumeration/DEPS
rename to third_party/blink/renderer/modules/screen_details/DEPS
diff --git a/third_party/blink/renderer/modules/screen_enumeration/DIR_METADATA b/third_party/blink/renderer/modules/screen_details/DIR_METADATA
similarity index 100%
rename from third_party/blink/renderer/modules/screen_enumeration/DIR_METADATA
rename to third_party/blink/renderer/modules/screen_details/DIR_METADATA
diff --git a/third_party/blink/renderer/modules/screen_details/OWNERS b/third_party/blink/renderer/modules/screen_details/OWNERS
new file mode 100644
index 0000000..d007f522
--- /dev/null
+++ b/third_party/blink/renderer/modules/screen_details/OWNERS
@@ -0,0 +1 @@
+file://content/browser/screen_details/OWNERS
diff --git a/third_party/blink/renderer/modules/screen_enumeration/screen_detailed.cc b/third_party/blink/renderer/modules/screen_details/screen_detailed.cc
similarity index 97%
rename from third_party/blink/renderer/modules/screen_enumeration/screen_detailed.cc
rename to third_party/blink/renderer/modules/screen_details/screen_detailed.cc
index 9cbd3caa..e1a503c2 100644
--- a/third_party/blink/renderer/modules/screen_enumeration/screen_detailed.cc
+++ b/third_party/blink/renderer/modules/screen_details/screen_detailed.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "third_party/blink/renderer/modules/screen_enumeration/screen_detailed.h"
+#include "third_party/blink/renderer/modules/screen_details/screen_detailed.h"
 
 #include "base/numerics/safe_conversions.h"
 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
diff --git a/third_party/blink/renderer/modules/screen_enumeration/screen_detailed.h b/third_party/blink/renderer/modules/screen_details/screen_detailed.h
similarity index 83%
rename from third_party/blink/renderer/modules/screen_enumeration/screen_detailed.h
rename to third_party/blink/renderer/modules/screen_details/screen_detailed.h
index 58c17c0d..dc8b1d0 100644
--- a/third_party/blink/renderer/modules/screen_enumeration/screen_detailed.h
+++ b/third_party/blink/renderer/modules/screen_details/screen_detailed.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_SCREEN_ENUMERATION_SCREEN_DETAILED_H_
-#define THIRD_PARTY_BLINK_RENDERER_MODULES_SCREEN_ENUMERATION_SCREEN_DETAILED_H_
+#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_SCREEN_DETAILS_SCREEN_DETAILED_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_SCREEN_DETAILS_SCREEN_DETAILED_H_
 
 #include "third_party/blink/renderer/core/frame/screen.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
@@ -37,7 +37,7 @@
   String label() const;
 
   // Attributes exposed for HDR canvas.
-  // https://github.com/w3c/ColorWeb-CG/blob/master/hdr_html_canvas_element.md
+  // https://github.com/w3c/ColorWeb-CG/blob/main/hdr_html_canvas_element.md
   float highDynamicRangeHeadroom() const;
   float redPrimaryX() const;
   float redPrimaryY() const;
@@ -58,4 +58,4 @@
 
 }  // namespace blink
 
-#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_SCREEN_ENUMERATION_SCREEN_DETAILED_H_
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_SCREEN_DETAILS_SCREEN_DETAILED_H_
diff --git a/third_party/blink/renderer/modules/screen_enumeration/screen_detailed.idl b/third_party/blink/renderer/modules/screen_details/screen_detailed.idl
similarity index 93%
rename from third_party/blink/renderer/modules/screen_enumeration/screen_detailed.idl
rename to third_party/blink/renderer/modules/screen_details/screen_detailed.idl
index 888373d3..3c51d25 100644
--- a/third_party/blink/renderer/modules/screen_enumeration/screen_detailed.idl
+++ b/third_party/blink/renderer/modules/screen_details/screen_detailed.idl
@@ -33,13 +33,13 @@
   // The maximum luminance that the screen is capable of displaying across
   // the full area of the screen, as a multiple of the luminance of SDR white.
   // This will have a value of 1.0 for screens that are not HDR capable.
-  // https://github.com/w3c/ColorWeb-CG/blob/master/hdr_html_canvas_element.md
+  // https://github.com/w3c/ColorWeb-CG/blob/main/hdr_html_canvas_element.md
   [RuntimeEnabled=CanvasHDR] readonly attribute float highDynamicRangeHeadroom;
 
   // The color primaries and white point of the screen, in CIE 1931 xy
   // coordinates. These define the color gamut that the screen is capable of
   // displaying.
-  // https://github.com/w3c/ColorWeb-CG/blob/master/hdr_html_canvas_element.md
+  // https://github.com/w3c/ColorWeb-CG/blob/main/hdr_html_canvas_element.md
   [RuntimeEnabled=CanvasHDR] readonly attribute float redPrimaryX;
   [RuntimeEnabled=CanvasHDR] readonly attribute float redPrimaryY;
   [RuntimeEnabled=CanvasHDR] readonly attribute float greenPrimaryX;
diff --git a/third_party/blink/renderer/modules/screen_enumeration/screen_details.cc b/third_party/blink/renderer/modules/screen_details/screen_details.cc
similarity index 97%
rename from third_party/blink/renderer/modules/screen_enumeration/screen_details.cc
rename to third_party/blink/renderer/modules/screen_details/screen_details.cc
index 4e940e2..b4120b4 100644
--- a/third_party/blink/renderer/modules/screen_enumeration/screen_details.cc
+++ b/third_party/blink/renderer/modules/screen_details/screen_details.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "third_party/blink/renderer/modules/screen_enumeration/screen_details.h"
+#include "third_party/blink/renderer/modules/screen_details/screen_details.h"
 
 #include "base/containers/contains.h"
 #include "third_party/blink/renderer/core/dom/events/event.h"
@@ -10,7 +10,7 @@
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/page/chrome_client.h"
 #include "third_party/blink/renderer/modules/event_target_modules.h"
-#include "third_party/blink/renderer/modules/screen_enumeration/screen_detailed.h"
+#include "third_party/blink/renderer/modules/screen_details/screen_detailed.h"
 #include "ui/display/screen_info.h"
 
 namespace blink {
diff --git a/third_party/blink/renderer/modules/screen_enumeration/screen_details.h b/third_party/blink/renderer/modules/screen_details/screen_details.h
similarity index 91%
rename from third_party/blink/renderer/modules/screen_enumeration/screen_details.h
rename to third_party/blink/renderer/modules/screen_details/screen_details.h
index 39321af..050960f8 100644
--- a/third_party/blink/renderer/modules/screen_enumeration/screen_details.h
+++ b/third_party/blink/renderer/modules/screen_details/screen_details.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_SCREEN_ENUMERATION_SCREEN_DETAILS_H_
-#define THIRD_PARTY_BLINK_RENDERER_MODULES_SCREEN_ENUMERATION_SCREEN_DETAILS_H_
+#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_SCREEN_DETAILS_SCREEN_DETAILS_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_SCREEN_DETAILS_SCREEN_DETAILS_H_
 
 #include "third_party/blink/renderer/core/dom/events/event_target.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context_lifecycle_observer.h"
@@ -72,4 +72,4 @@
 
 }  // namespace blink
 
-#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_SCREEN_ENUMERATION_SCREENS_H_
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_SCREEN_DETAILS_SCREEN_DETAILS_H_
diff --git a/third_party/blink/renderer/modules/screen_enumeration/screen_details.idl b/third_party/blink/renderer/modules/screen_details/screen_details.idl
similarity index 100%
rename from third_party/blink/renderer/modules/screen_enumeration/screen_details.idl
rename to third_party/blink/renderer/modules/screen_details/screen_details.idl
diff --git a/third_party/blink/renderer/modules/screen_enumeration/window_screens.cc b/third_party/blink/renderer/modules/screen_details/window_screen_details.cc
similarity index 75%
rename from third_party/blink/renderer/modules/screen_enumeration/window_screens.cc
rename to third_party/blink/renderer/modules/screen_details/window_screen_details.cc
index dbd476bb..0fb84060 100644
--- a/third_party/blink/renderer/modules/screen_enumeration/window_screens.cc
+++ b/third_party/blink/renderer/modules/screen_details/window_screen_details.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "third_party/blink/renderer/modules/screen_enumeration/window_screens.h"
+#include "third_party/blink/renderer/modules/screen_details/window_screen_details.h"
 
 #include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
@@ -11,32 +11,33 @@
 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/modules/permissions/permission_utils.h"
-#include "third_party/blink/renderer/modules/screen_enumeration/screen_details.h"
+#include "third_party/blink/renderer/modules/screen_details/screen_details.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/bindings/script_state.h"
 
 namespace blink {
 
 // static
-const char WindowScreens::kSupplementName[] = "WindowScreens";
+const char WindowScreenDetails::kSupplementName[] = "WindowScreenDetails";
 
-WindowScreens::WindowScreens(LocalDOMWindow* window)
+WindowScreenDetails::WindowScreenDetails(LocalDOMWindow* window)
     : ExecutionContextLifecycleObserver(window),
       Supplement<LocalDOMWindow>(*window),
       permission_service_(window) {}
 
 // static
-ScriptPromise WindowScreens::getScreenDetails(ScriptState* script_state,
-                                              LocalDOMWindow& window,
-                                              ExceptionState& exception_state) {
+ScriptPromise WindowScreenDetails::getScreenDetails(
+    ScriptState* script_state,
+    LocalDOMWindow& window,
+    ExceptionState& exception_state) {
   return From(&window)->GetScreenDetails(script_state, exception_state);
 }
 
-void WindowScreens::ContextDestroyed() {
+void WindowScreenDetails::ContextDestroyed() {
   screen_details_.Clear();
 }
 
-void WindowScreens::Trace(Visitor* visitor) const {
+void WindowScreenDetails::Trace(Visitor* visitor) const {
   visitor->Trace(screen_details_);
   visitor->Trace(permission_service_);
   ExecutionContextLifecycleObserver::Trace(visitor);
@@ -44,17 +45,19 @@
 }
 
 // static
-WindowScreens* WindowScreens::From(LocalDOMWindow* window) {
-  auto* supplement = Supplement<LocalDOMWindow>::From<WindowScreens>(window);
+WindowScreenDetails* WindowScreenDetails::From(LocalDOMWindow* window) {
+  auto* supplement =
+      Supplement<LocalDOMWindow>::From<WindowScreenDetails>(window);
   if (!supplement) {
-    supplement = MakeGarbageCollected<WindowScreens>(window);
+    supplement = MakeGarbageCollected<WindowScreenDetails>(window);
     Supplement<LocalDOMWindow>::ProvideTo(*window, supplement);
   }
   return supplement;
 }
 
-ScriptPromise WindowScreens::GetScreenDetails(ScriptState* script_state,
-                                              ExceptionState& exception_state) {
+ScriptPromise WindowScreenDetails::GetScreenDetails(
+    ScriptState* script_state,
+    ExceptionState& exception_state) {
   if (!script_state->ContextIsValid()) {
     exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
                                       "The execution context is not valid.");
@@ -73,8 +76,9 @@
   auto permission_descriptor = CreatePermissionDescriptor(
       mojom::blink::PermissionName::WINDOW_MANAGEMENT);
   auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
-  auto callback = WTF::BindOnce(&WindowScreens::OnPermissionRequestComplete,
-                                WrapPersistent(this), WrapPersistent(resolver));
+  auto callback =
+      WTF::BindOnce(&WindowScreenDetails::OnPermissionRequestComplete,
+                    WrapPersistent(this), WrapPersistent(resolver));
 
   // Only allow the user prompts when the frame has a transient activation.
   // Otherwise, resolve or reject the promise with the current permission state.
@@ -92,7 +96,7 @@
   return resolver->Promise();
 }
 
-void WindowScreens::OnPermissionRequestComplete(
+void WindowScreenDetails::OnPermissionRequestComplete(
     ScriptPromiseResolver* resolver,
     mojom::blink::PermissionStatus status) {
   if (!resolver->GetScriptState()->ContextIsValid())
diff --git a/third_party/blink/renderer/modules/screen_enumeration/window_screens.h b/third_party/blink/renderer/modules/screen_details/window_screen_details.h
similarity index 75%
rename from third_party/blink/renderer/modules/screen_enumeration/window_screens.h
rename to third_party/blink/renderer/modules/screen_details/window_screen_details.h
index 1016e3e..4f537e4 100644
--- a/third_party/blink/renderer/modules/screen_enumeration/window_screens.h
+++ b/third_party/blink/renderer/modules/screen_details/window_screen_details.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_SCREEN_ENUMERATION_WINDOW_SCREENS_H_
-#define THIRD_PARTY_BLINK_RENDERER_MODULES_SCREEN_ENUMERATION_WINDOW_SCREENS_H_
+#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_SCREEN_DETAILS_WINDOW_SCREEN_DETAILS_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_SCREEN_DETAILS_WINDOW_SCREEN_DETAILS_H_
 
 #include "third_party/blink/public/mojom/permissions/permission.mojom-blink.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context_lifecycle_observer.h"
@@ -22,13 +22,13 @@
 
 // Supplements LocalDOMWindow with a ScreenDetails interface.
 // https://w3c.github.io/window-placement/
-class WindowScreens final : public GarbageCollected<WindowScreens>,
-                            public ExecutionContextLifecycleObserver,
-                            public Supplement<LocalDOMWindow> {
+class WindowScreenDetails final : public GarbageCollected<WindowScreenDetails>,
+                                  public ExecutionContextLifecycleObserver,
+                                  public Supplement<LocalDOMWindow> {
  public:
   static const char kSupplementName[];
 
-  explicit WindowScreens(LocalDOMWindow* window);
+  explicit WindowScreenDetails(LocalDOMWindow* window);
 
   // Web-exposed interface:
   static ScriptPromise getScreenDetails(ScriptState* script_state,
@@ -44,7 +44,7 @@
 
  private:
   // Returns the supplement, creating one as needed.
-  static WindowScreens* From(LocalDOMWindow* window);
+  static WindowScreenDetails* From(LocalDOMWindow* window);
 
   // Requests permission to resolve the returned Screens interface promise.
   ScriptPromise GetScreenDetails(ScriptState* script_state,
@@ -60,4 +60,4 @@
 
 }  // namespace blink
 
-#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_SCREEN_ENUMERATION_WINDOW_SCREENS_H_
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_SCREEN_DETAILS_WINDOW_SCREEN_DETAILS_H_
diff --git a/third_party/blink/renderer/modules/screen_enumeration/window_screens.idl b/third_party/blink/renderer/modules/screen_details/window_screen_details.idl
similarity index 90%
rename from third_party/blink/renderer/modules/screen_enumeration/window_screens.idl
rename to third_party/blink/renderer/modules/screen_details/window_screen_details.idl
index 7605458..930388e 100644
--- a/third_party/blink/renderer/modules/screen_enumeration/window_screens.idl
+++ b/third_party/blink/renderer/modules/screen_details/window_screen_details.idl
@@ -6,7 +6,7 @@
 // https://w3c.github.io/window-placement/
 [
     SecureContext,
-    ImplementedAs=WindowScreens
+    ImplementedAs=WindowScreenDetails
 ] partial interface Window {
   [CallWith=ScriptState, Measure, RaisesException] Promise<ScreenDetails> getScreenDetails();
 };
diff --git a/third_party/blink/renderer/modules/screen_enumeration/OWNERS b/third_party/blink/renderer/modules/screen_enumeration/OWNERS
deleted file mode 100644
index d3c2ed3..0000000
--- a/third_party/blink/renderer/modules/screen_enumeration/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-file://content/browser/screen_enumeration/OWNERS
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index a1d57146..0b595706 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -1039,6 +1039,14 @@
       status: "stable",
       base_feature: "DisableTextDecorationColorOverride",
     },
+    // This feature should only be enabled if ThirdPartyStoragePartitioning
+    // is enabled, but as ThirdPartyStoragePartitioning will be linked to a
+    // deprecation trial it isn't possible to set a `depends_on`.
+    {
+      name: "DisableThirdPartySessionStoragePartitioningAfterGeneralPartitioning",
+      status: "experimental",
+      browser_process_read_write_access: true,
+    },
     {
       name: "DisplayCutoutAPI",
       public: true,
diff --git a/third_party/blink/tools/run_wpt_tests.py b/third_party/blink/tools/run_wpt_tests.py
index 295a5d9..dd1fd2d 100755
--- a/third_party/blink/tools/run_wpt_tests.py
+++ b/third_party/blink/tools/run_wpt_tests.py
@@ -130,13 +130,25 @@
     def wpt_binary(self):
         if self.options.use_upstream_wpt:
             return os.path.join(self._upstream_dir, "wpt")
-        return super().wpt_binary
+        default_wpt_binary = os.path.join(common.SRC_DIR, "third_party",
+                                          "wpt_tools", "wpt", "wpt")
+        return os.environ.get("WPT_BINARY", default_wpt_binary)
 
     @property
     def wpt_root_dir(self):
         if self.options.use_upstream_wpt:
             return self._upstream_dir
-        return super().wpt_root_dir
+        return self.path_finder.path_from_web_tests(
+            self.path_finder.wpt_prefix())
+
+    @property
+    def output_directory(self):
+        return self.path_finder.path_from_chromium_base(
+            'out', self.options.target)
+
+    @property
+    def mojo_js_directory(self):
+        return self.fs.join(self.output_directory, 'gen')
 
     @property
     def rest_args(self):
@@ -271,7 +283,7 @@
         process_return = self.process_and_upload_results()
 
         if (process_return != exit_codes.INTERRUPTED_EXIT_STATUS
-                and self.options.show_results_in_browser):
+                and self.options.show_results):
             self.show_results_in_browser()
 
     def show_results_in_browser(self):
@@ -281,7 +293,8 @@
         self.port.show_results_html_file(results_file)
 
     def clean_up_after_test_run(self):
-        super().clean_up_after_test_run()
+        if self._include_filename:
+            self.fs.remove(self._include_filename)
         # Avoid having a dangling reference to the temp directory
         # which was deleted
         self._tmp_dir = None
@@ -335,9 +348,12 @@
                             default=True,
                             help=('Use this tag to not run wptrunner in'
                                   'headless mode'))
-        parser.add_argument('--show-results-in-browser',
-                            action='store_true',
-                            help='Open the results viewer in a browser.')
+        parser.add_argument('--no-show-results',
+                            dest="show_results",
+                            action='store_false',
+                            default=True,
+                            help=("Don't launch a browser with results after"
+                                  "the tests are done"))
         parser.add_argument(
             '--enable-sanitizer',
             action='store_true',
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 5805665..52afc416 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -6839,6 +6839,7 @@
 crbug.com/1404951 external/wpt/credential-management/fedcm-network-requests.https.html [ Pass Timeout ]
 
 # Sheriff 2023-01-05
-# Previously also linked with crbug.com/1249176
-crbug.com/1361956 [ Mac12 ] fast/dom/Element/scrollTop-scrollLeft-body.html [ Pass Failure Timeout ]
-crbug.com/1354433 [ Win ] external/wpt/html/browsers/the-window-object/window-properties.https.html [ Pass Failure ]
+# fast/dom/Element/scrollTop-scrollLeft-body.html previously also linked with crbug.com/1249176
+crbug.com/1361956 [ Mac12 ] fast/dom/Element/scrollTop-scrollLeft-body.html [ Failure Pass Timeout ]
+crbug.com/1354433 [ Win ] external/wpt/html/browsers/the-window-object/window-properties.https.html [ Failure Pass ]
+crbug.com/1404252 [ Mac11-arm64 Release ] external/wpt/resource-timing/response-status-code.html [ Failure Pass ]
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
index 0cdbe18d..c83857e 100644
--- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
+++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
@@ -261350,16 +261350,6 @@
    }
   },
   "support": {
-   ".cache": {
-    "gitignore2.json": [
-     "27f4d9f77894fe950db7a1b9c4449701188f7fbb",
-     []
-    ],
-    "mtime.json": [
-     "9c35ba7006d075ba14237be87266fc9f1bab8b36",
-     []
-    ]
-   },
    ".gitignore": [
     "d93e645d547894b50149d3726de2654957b6e06f",
     []
@@ -366887,6 +366877,10 @@
      "d4074f647dab2b1af158a4f544ccd4737164b32c",
      []
     ],
+    "webcodecs-flac-codec-registration.idl": [
+     "0f7e13a53db8758b223ab42c0bd5816312fa2968",
+     []
+    ],
     "webcodecs-hevc-codec-registration.idl": [
      "b767db89e6fa6d6f527209b79c8bbd960fa87d9f",
      []
@@ -366916,7 +366910,7 @@
      []
     ],
     "webgpu.idl": [
-     "5b4fcc1aa8c42aedf132d877b4f78ef1fc0ec03d",
+     "b00e2374e586a63df1e09e36b5cbc008e3539772",
      []
     ],
     "webhid.idl": [
@@ -588479,10 +588473,10 @@
       }
      ]
     ],
-    "screen_enumeration_permission.https.window.js": [
+    "permission.https.window.js": [
      "32596fac9ea3bdbbcdad3d6fd6f15a0e9b0664b7",
      [
-      "screen-details/screen_enumeration_permission.https.window.html",
+      "screen-details/permission.https.window.html",
       {
        "script_metadata": [
         [
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/webcodecs-flac-codec-registration.idl b/third_party/blink/web_tests/external/wpt/interfaces/webcodecs-flac-codec-registration.idl
new file mode 100644
index 0000000..0f7e13a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/interfaces/webcodecs-flac-codec-registration.idl
@@ -0,0 +1,13 @@
+// GENERATED CONTENT - DO NOT EDIT
+// Content was automatically extracted by Reffy into webref
+// (https://github.com/w3c/webref)
+// Source: FLAC WebCodecs Registration (https://w3c.github.io/webcodecs/flac_codec_registration.html)
+
+partial dictionary AudioEncoderConfig {
+  FlacEncoderConfig flac;
+};
+
+dictionary FlacEncoderConfig {
+  [EnforceRange] unsigned long blockSize = 0;
+  [EnforceRange] unsigned long compressLevel = 5;
+};
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/webgpu.idl b/third_party/blink/web_tests/external/wpt/interfaces/webgpu.idl
index 5b4fcc1..b00e237 100644
--- a/third_party/blink/web_tests/external/wpt/interfaces/webgpu.idl
+++ b/third_party/blink/web_tests/external/wpt/interfaces/webgpu.idl
@@ -106,7 +106,6 @@
     "timestamp-query",
     "indirect-first-instance",
     "shader-f16",
-    "bgra8unorm-storage",
     "rg11b10ufloat-renderable"
 };
 
@@ -585,6 +584,21 @@
     readonly attribute FrozenArray<GPUCompilationMessage> messages;
 };
 
+[Exposed=(Window, DedicatedWorker), SecureContext, Serializable]
+interface GPUPipelineError : DOMException {
+    constructor(DOMString message, GPUPipelineErrorInit options);
+    readonly attribute GPUPipelineErrorReason reason;
+};
+
+dictionary GPUPipelineErrorInit {
+    required GPUPipelineErrorReason reason;
+};
+
+enum GPUPipelineErrorReason {
+    "validation",
+    "internal"
+};
+
 enum GPUAutoLayoutMode {
     "auto"
 };
diff --git a/third_party/blink/web_tests/external/wpt/screen-details/screen_enumeration_permission.https.window.js b/third_party/blink/web_tests/external/wpt/screen-details/permission.https.window.js
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/screen-details/screen_enumeration_permission.https.window.js
rename to third_party/blink/web_tests/external/wpt/screen-details/permission.https.window.js
diff --git a/third_party/blink/web_tests/resources/check-layout-th.js b/third_party/blink/web_tests/resources/check-layout-th.js
index 9cd8abc..f14ca32 100644
--- a/third_party/blink/web_tests/resources/check-layout-th.js
+++ b/third_party/blink/web_tests/resources/check-layout-th.js
@@ -26,7 +26,11 @@
 }
 
 function checkDataKeys(node) {
+  // The purpose of this list of data-* attributes is simply to ensure typos
+  // in tests are caught. It is therefore "ok" to add to this list for
+  // specific tests.
     var validData = new Set([
+        "data-anchor-polyfill",
         "data-expected-width",
         "data-expected-height",
         "data-offset-x",
diff --git a/third_party/blink/web_tests/resources/testdriver.js b/third_party/blink/web_tests/resources/testdriver.js
index ba59c3e..76ae2834 100644
--- a/third_party/blink/web_tests/resources/testdriver.js
+++ b/third_party/blink/web_tests/resources/testdriver.js
@@ -398,11 +398,11 @@
          *
          * @example
          * await test_driver.set_permission({ name: "background-fetch" }, "denied");
-         * await test_driver.set_permission({ name: "push", userVisibleOnly: true }, "granted", true);
+         * await test_driver.set_permission({ name: "push", userVisibleOnly: true }, "granted");
          *
-         * @param {Object} descriptor - a `PermissionDescriptor
-         *                              <https://w3c.github.io/permissions/#dictdef-permissiondescriptor>`_
-         *                              object
+         * @param {PermissionDescriptor} descriptor - a `PermissionDescriptor
+         *                              <https://w3c.github.io/permissions/#dom-permissiondescriptor>`_
+         *                              dictionary.
          * @param {String} state - the state of the permission
          * @param {WindowProxy} context - Browsing context in which
          *                                to run the call, or null for the current
diff --git a/third_party/crc32c/BUILD.gn b/third_party/crc32c/BUILD.gn
index c745a5f..39c151a 100644
--- a/third_party/crc32c/BUILD.gn
+++ b/third_party/crc32c/BUILD.gn
@@ -122,6 +122,10 @@
         "-target-feature",
         "-Xclang",
         "+crypto",
+        "-Xclang",
+        "-target-feature",
+        "-Xclang",
+        "+aes",
       ]
     } else {
       cflags = [ "-march=armv8-a+crc+crypto" ]
diff --git a/third_party/freetype/README.chromium b/third_party/freetype/README.chromium
index d8481e2..b4da6277 100644
--- a/third_party/freetype/README.chromium
+++ b/third_party/freetype/README.chromium
@@ -1,7 +1,7 @@
 Name: FreeType
 URL: http://www.freetype.org/
-Version: VER-2-12-1-148-g63f371367
-Revision: 63f371367aeefa73541617edfb1dcef9428796fb
+Version: VER-2-12-1-149-gc0b4f6a86
+Revision: c0b4f6a8625a39ecd4c323d74ddec0e94fca214d
 CPEPrefix: cpe:/a:freetype:freetype:2.12.1
 License: Custom license "inspired by the BSD, Artistic, and IJG (Independent
          JPEG Group) licenses"
diff --git a/tools/autotest.py b/tools/autotest.py
index 296089e..ab1e2164 100755
--- a/tools/autotest.py
+++ b/tools/autotest.py
@@ -49,6 +49,7 @@
 # Those test suites should be manually added here.
 _OTHER_TEST_TARGETS = [
     '//chrome/test:browser_tests',
+    '//chrome/test:interactive_ui_tests',
     '//chrome/test:unit_tests',
 ]
 
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl
index b39d0c7b..cadde54 100644
--- a/tools/mb/mb_config.pyl
+++ b/tools/mb/mb_config.pyl
@@ -369,7 +369,6 @@
       'Linux Builder (j-500) (reclient)': 'gpu_tests_release_bot_reclient',
       'Linux Builder (reclient compare)': 'gpu_tests_release_bot_reclient',
       'Linux CFI (reclient shadow)': 'cfi_full_cfi_icall_cfi_diag_thin_lto_release_static_dcheck_always_on_reclient',
-      'Linux ChromiumOS MSan Focal': 'chromeos_msan_focal_release_bot_reclient',
       'Linux Viz': 'release_trybot_minimal_symbols_reclient',
       # TODO(crbug.com/1260232): remove this after the migration.
       'Mac Builder (reclient compare)': 'gpu_tests_release_bot_minimal_symbols_reclient',
@@ -627,7 +626,7 @@
       'Linux ASan LSan Builder': 'asan_lsan_release_trybot_reclient',
       'Linux CFI': 'cfi_full_cfi_icall_cfi_diag_thin_lto_release_static_dcheck_always_on_reclient',
       'Linux Chromium OS ASan LSan Builder': 'asan_lsan_chromeos_release_bot_dcheck_always_on_reclient',
-      'Linux ChromiumOS MSan Builder': 'chromeos_msan_release_bot_reclient',
+      'Linux ChromiumOS MSan Builder': 'chromeos_msan_focal_release_bot_reclient',
       'Linux MSan Builder': 'msan_focal_release_bot_reclient',
       'Linux TSan Builder': 'tsan_disable_nacl_release_bot_reclient',
       'Mac ASan 64 Builder': 'asan_minimal_symbols_disable_nacl_release_bot_dcheck_always_on_reclient',
@@ -685,14 +684,6 @@
         'build1': 'gpu_tests_release_bot_reclient',
         'build2': 'gpu_tests_release_bot_remote_links_reclient',
       },
-      'Comparison Linux (reclient vs reclient remote links)(large)': {
-        'build1': 'gpu_tests_release_bot_no_nacl_reclient',
-        'build2': 'gpu_tests_release_bot_remote_links_large_reclient',
-      },
-      'Comparison Linux (reclient vs reclient remote links)(medium)': {
-        'build1': 'gpu_tests_release_bot_no_nacl_reclient',
-        'build2': 'gpu_tests_release_bot_remote_links_medium_reclient',
-      },
       'Comparison Linux (reclient vs reclient remote links)(small)': {
         'build1': 'gpu_tests_release_bot_no_nacl_reclient',
         'build2': 'gpu_tests_release_bot_remote_links_small_reclient',
@@ -1208,8 +1199,7 @@
       'linux_chromium_asan_rel_ng': 'asan_lsan_release_trybot_reclient',
       'linux_chromium_cfi_rel_ng': 'cfi_full_cfi_icall_cfi_diag_thin_lto_release_static_dcheck_always_on_reclient',
       'linux_chromium_chromeos_asan_rel_ng': 'asan_lsan_chromeos_release_bot_dcheck_always_on_reclient',
-      'linux_chromium_chromeos_msan_focal': 'chromeos_msan_focal_release_bot_reclient',
-      'linux_chromium_chromeos_msan_rel_ng': 'chromeos_msan_release_bot_reclient',
+      'linux_chromium_chromeos_msan_rel_ng': 'chromeos_msan_focal_release_bot_reclient',
       'linux_chromium_clobber_deterministic': 'release_trybot',
       'linux_chromium_clobber_rel_ng': 'release_trybot',
       'linux_chromium_compile_dbg_ng': 'debug_bot_reclient',
@@ -2244,10 +2234,6 @@
       'chromeos', 'msan_focal', 'release_bot_reclient',
     ],
 
-    'chromeos_msan_release_bot_reclient': [
-      'chromeos', 'msan', 'release_bot_reclient',
-    ],
-
     'chromeos_octopus_dchecks': [
       'chromeos_octopus', 'dcheck_always_on',
     ],
@@ -2763,14 +2749,6 @@
       'gpu_tests', 'release_bot_reclient',
     ],
 
-    'gpu_tests_release_bot_remote_links_large_reclient': [
-      'gpu_tests', 'release_bot_reclient', 'use_remoteexec_links_large', 'disable_nacl',
-    ],
-
-    'gpu_tests_release_bot_remote_links_medium_reclient': [
-      'gpu_tests', 'release_bot_reclient', 'use_remoteexec_links_medium', 'disable_nacl',
-    ],
-
     'gpu_tests_release_bot_remote_links_reclient': [
       'gpu_tests', 'release_bot_reclient', 'use_remoteexec_links',
     ],
@@ -4779,16 +4757,6 @@
     },
 
     # Temporary configuration for testing remote linking on different worker sizes
-    'use_remoteexec_links_large': {
-      'gn_args': 'use_remoteexec_links=true rbe_link_cfg_file="../../buildtools/reclient_cfgs/chromium-browser-clang/rewrapper_linux_link_large.cfg" concurrent_links=50',
-    },
-
-    # Temporary configuration for testing remote linking on different worker sizes
-    'use_remoteexec_links_medium': {
-      'gn_args': 'use_remoteexec_links=true rbe_link_cfg_file="../../buildtools/reclient_cfgs/chromium-browser-clang/rewrapper_linux_link_medium.cfg" concurrent_links=50',
-    },
-
-    # Temporary configuration for testing remote linking on different worker sizes
     'use_remoteexec_links_small': {
       'gn_args': 'use_remoteexec_links=true rbe_link_cfg_file="../../buildtools/reclient_cfgs/chromium-browser-clang/rewrapper_linux_link_small.cfg" concurrent_links=50',
     },
diff --git a/tools/mb/mb_config_expectations/chromium.fyi.json b/tools/mb/mb_config_expectations/chromium.fyi.json
index fa29075e..33aea4f 100644
--- a/tools/mb/mb_config_expectations/chromium.fyi.json
+++ b/tools/mb/mb_config_expectations/chromium.fyi.json
@@ -424,18 +424,6 @@
       "use_thin_lto": true
     }
   },
-  "Linux ChromiumOS MSan Focal": {
-    "gn_args": {
-      "dcheck_always_on": false,
-      "instrumented_libraries_release": "focal",
-      "is_component_build": false,
-      "is_debug": false,
-      "is_msan": true,
-      "msan_track_origins": 2,
-      "target_os": "chromeos",
-      "use_remoteexec": true
-    }
-  },
   "Linux Viz": {
     "gn_args": {
       "dcheck_always_on": true,
diff --git a/tools/mb/mb_config_expectations/chromium.memory.json b/tools/mb/mb_config_expectations/chromium.memory.json
index ab0caaff..2f33792c 100644
--- a/tools/mb/mb_config_expectations/chromium.memory.json
+++ b/tools/mb/mb_config_expectations/chromium.memory.json
@@ -38,7 +38,7 @@
   "Linux ChromiumOS MSan Builder": {
     "gn_args": {
       "dcheck_always_on": false,
-      "instrumented_libraries_release": "xenial",
+      "instrumented_libraries_release": "focal",
       "is_component_build": false,
       "is_debug": false,
       "is_msan": true,
diff --git a/tools/mb/mb_config_expectations/chromium.reclient.fyi.json b/tools/mb/mb_config_expectations/chromium.reclient.fyi.json
index 947d6c64..9131e372 100644
--- a/tools/mb/mb_config_expectations/chromium.reclient.fyi.json
+++ b/tools/mb/mb_config_expectations/chromium.reclient.fyi.json
@@ -22,60 +22,6 @@
       }
     }
   },
-  "Comparison Linux (reclient vs reclient remote links)(large)": {
-    "build1": {
-      "gn_args": {
-        "dcheck_always_on": false,
-        "enable_nacl": false,
-        "ffmpeg_branding": "Chrome",
-        "is_component_build": false,
-        "is_debug": false,
-        "proprietary_codecs": true,
-        "use_remoteexec": true
-      }
-    },
-    "build2": {
-      "gn_args": {
-        "concurrent_links": 50,
-        "dcheck_always_on": false,
-        "enable_nacl": false,
-        "ffmpeg_branding": "Chrome",
-        "is_component_build": false,
-        "is_debug": false,
-        "proprietary_codecs": true,
-        "rbe_link_cfg_file": "../../buildtools/reclient_cfgs/chromium-browser-clang/rewrapper_linux_link_large.cfg",
-        "use_remoteexec": true,
-        "use_remoteexec_links": true
-      }
-    }
-  },
-  "Comparison Linux (reclient vs reclient remote links)(medium)": {
-    "build1": {
-      "gn_args": {
-        "dcheck_always_on": false,
-        "enable_nacl": false,
-        "ffmpeg_branding": "Chrome",
-        "is_component_build": false,
-        "is_debug": false,
-        "proprietary_codecs": true,
-        "use_remoteexec": true
-      }
-    },
-    "build2": {
-      "gn_args": {
-        "concurrent_links": 50,
-        "dcheck_always_on": false,
-        "enable_nacl": false,
-        "ffmpeg_branding": "Chrome",
-        "is_component_build": false,
-        "is_debug": false,
-        "proprietary_codecs": true,
-        "rbe_link_cfg_file": "../../buildtools/reclient_cfgs/chromium-browser-clang/rewrapper_linux_link_medium.cfg",
-        "use_remoteexec": true,
-        "use_remoteexec_links": true
-      }
-    }
-  },
   "Comparison Linux (reclient vs reclient remote links)(small)": {
     "build1": {
       "gn_args": {
diff --git a/tools/mb/mb_config_expectations/tryserver.chromium.linux.json b/tools/mb/mb_config_expectations/tryserver.chromium.linux.json
index 14a8e8f..8e1e0d2e 100644
--- a/tools/mb/mb_config_expectations/tryserver.chromium.linux.json
+++ b/tools/mb/mb_config_expectations/tryserver.chromium.linux.json
@@ -656,22 +656,10 @@
       "use_remoteexec": true
     }
   },
-  "linux_chromium_chromeos_msan_focal": {
-    "gn_args": {
-      "dcheck_always_on": false,
-      "instrumented_libraries_release": "focal",
-      "is_component_build": false,
-      "is_debug": false,
-      "is_msan": true,
-      "msan_track_origins": 2,
-      "target_os": "chromeos",
-      "use_remoteexec": true
-    }
-  },
   "linux_chromium_chromeos_msan_rel_ng": {
     "gn_args": {
       "dcheck_always_on": false,
-      "instrumented_libraries_release": "xenial",
+      "instrumented_libraries_release": "focal",
       "is_component_build": false,
       "is_debug": false,
       "is_msan": true,
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 6baf1b8..c001f34 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -28686,6 +28686,13 @@
   <int value="7" label="Migration from in-progress cache failed"/>
 </enum>
 
+<enum name="DownloadInputStreamReadErrorType">
+  <int value="0" label="Invalid argument"/>
+  <int value="1" label="Out of range"/>
+  <int value="2" label="Busy"/>
+  <int value="3" label="Unknown"/>
+</enum>
+
 <enum name="DownloadInterruptedUnknownSizeType">
   <int value="0" label="Size Known"/>
   <int value="1" label="Size Unknown"/>
@@ -57341,6 +57348,8 @@
   <int value="-2032940137" label="VariableCOLRV1:enabled"/>
   <int value="-2032013236"
       label="AutofillEnforceDelaysInStrikeDatabase:disabled"/>
+  <int value="-2030972455"
+      label="AutofillSuggestServerCardInsteadOfLocalCard:enabled"/>
   <int value="-2030255112" label="Bruschetta:disabled"/>
   <int value="-2030217301" label="password-export:disabled"/>
   <int value="-2029912304" label="StaleWhileRevalidate2:enabled"/>
@@ -57368,6 +57377,8 @@
   <int value="-2017953534" label="enable-hosted-app-shim-creation"/>
   <int value="-2017821808" label="PrivacySandboxAdsAPIsOverride:disabled"/>
   <int value="-2017778637" label="PrintSaveToDrive:disabled"/>
+  <int value="-2017250992"
+      label="AutofillSuggestServerCardInsteadOfLocalCard:disabled"/>
   <int value="-2015293660" label="AccessibilityExposeDisplayNone:disabled"/>
   <int value="-2014948560" label="TabGroupsAutoCreate:enabled"/>
   <int value="-2013551096" label="ViewsSimplifiedFullscreenUI:disabled"/>
@@ -75753,6 +75764,8 @@
   <int value="27" label="Cellular: Remove Network eSIM Profile"/>
   <int value="28" label="Cellular: Rename Network eSIM Profile"/>
   <int value="29" label="Wi-Fi: Hidden Network"/>
+  <int value="30" label="Hotspot: On/Off"/>
+  <int value="31" label="Hotspot: Automatically Turn Off Hotspot"/>
   <int value="100" label="Bluetooth: On/Off"/>
   <int value="101" label="Bluetooth: Connect To Device"/>
   <int value="102" label="Bluetooth: Disconnect From Device"/>
diff --git a/tools/metrics/histograms/generate_flag_enums.py b/tools/metrics/histograms/generate_flag_enums.py
index 41d092a..629d0ee 100755
--- a/tools/metrics/histograms/generate_flag_enums.py
+++ b/tools/metrics/histograms/generate_flag_enums.py
@@ -30,7 +30,7 @@
   """
   subprocess.run(['autoninja', '-C', outdir, 'unit_tests'])
   run_test_command = subprocess.run([
-      'out/Default/unit_tests',
+      os.path.join(outdir, unit_tests),
       '--gtest_filter=AboutFlagsHistogramTest.CheckHistograms'
   ],
                                     capture_output=True,
diff --git a/tools/metrics/histograms/metadata/chromeos/histograms.xml b/tools/metrics/histograms/metadata/chromeos/histograms.xml
index eebd31ca..e5e46b28 100644
--- a/tools/metrics/histograms/metadata/chromeos/histograms.xml
+++ b/tools/metrics/histograms/metadata/chromeos/histograms.xml
@@ -545,7 +545,7 @@
 </histogram>
 
 <histogram name="ChromeOS.CertProvisioning.CsrSignTime" units="ms"
-    expires_after="2023-02-01">
+    expires_after="2024-02-01">
   <owner>miersh@google.com</owner>
   <owner>pmarko@chromium.org</owner>
   <summary>
@@ -555,7 +555,7 @@
 </histogram>
 
 <histogram name="ChromeOS.CertProvisioning.Event" enum="CertProvisioningEvent"
-    expires_after="2023-02-01">
+    expires_after="2024-02-01">
   <owner>miersh@google.com</owner>
   <owner>pmarko@chromium.org</owner>
   <summary>
@@ -565,7 +565,7 @@
 </histogram>
 
 <histogram name="ChromeOS.CertProvisioning.KeypairGenerationTime" units="ms"
-    expires_after="2023-02-01">
+    expires_after="2024-02-01">
   <owner>miersh@google.com</owner>
   <owner>pmarko@chromium.org</owner>
   <summary>
@@ -575,7 +575,7 @@
 </histogram>
 
 <histogram name="ChromeOS.CertProvisioning.Result"
-    enum="CertProvisioningWorkerState" expires_after="2023-02-01">
+    enum="CertProvisioningWorkerState" expires_after="2024-02-01">
   <owner>miersh@google.com</owner>
   <owner>pmarko@chromium.org</owner>
   <summary>
@@ -586,7 +586,7 @@
 </histogram>
 
 <histogram name="ChromeOS.CertProvisioning.VaTime" units="ms"
-    expires_after="2023-02-01">
+    expires_after="2024-02-01">
   <owner>miersh@google.com</owner>
   <owner>pmarko@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/content/histograms.xml b/tools/metrics/histograms/metadata/content/histograms.xml
index 2fd0d51..c51e9e9 100644
--- a/tools/metrics/histograms/metadata/content/histograms.xml
+++ b/tools/metrics/histograms/metadata/content/histograms.xml
@@ -1426,19 +1426,6 @@
   </summary>
 </histogram>
 
-<histogram name="ContentSuggestions.Feed.Scheduler.TimeSinceLastFetchOnClear"
-    units="ms" expires_after="2020-10-01">
-  <owner>carlosk@chromium.org</owner>
-  <owner>harringtond@chromium.org</owner>
-  <owner>feed@chromium.org</owner>
-  <summary>
-    When the previous last fetch attempt time is cleared, log the time since the
-    last fetch attempt. This typically occurs when suggestions are cleared. A
-    client that has never made a successful fetch (at least since it was last
-    cleared) will report the time since epoch.
-  </summary>
-</histogram>
-
 <histogram name="ContentSuggestions.Feed.SendFeedback"
     enum="FeedSendFeedbackType" expires_after="2023-03-01">
   <owner>carlosk@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/download/histograms.xml b/tools/metrics/histograms/metadata/download/histograms.xml
index f2d8076..9abee1ef 100644
--- a/tools/metrics/histograms/metadata/download/histograms.xml
+++ b/tools/metrics/histograms/metadata/download/histograms.xml
@@ -467,6 +467,16 @@
   </summary>
 </histogram>
 
+<histogram name="Download.InputStreamReadError"
+    enum="DownloadInputStreamReadErrorType" expires_after="2023-08-31">
+  <owner>qinmin@chromium.org</owner>
+  <owner>shaktisahu@chromium.org</owner>
+  <summary>
+    Records errors when reading from the mojo input stream before a download
+    completes.
+  </summary>
+</histogram>
+
 <histogram base="true" name="Download.InsecureBlocking.Extensions"
     enum="InsecureDownloadExtensions" expires_after="2023-08-21">
   <owner>jdeblasio@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/webapps/histograms.xml b/tools/metrics/histograms/metadata/webapps/histograms.xml
index 3f06226..e20993b 100644
--- a/tools/metrics/histograms/metadata/webapps/histograms.xml
+++ b/tools/metrics/histograms/metadata/webapps/histograms.xml
@@ -1059,6 +1059,45 @@
   </summary>
 </histogram>
 
+<histogram name="WebApp.ProtocolHandlers.Registration.Result"
+    enum="BooleanSuccess" expires_after="2024-09-10">
+  <owner>dibyapal@chromium.org</owner>
+  <owner>desktop-pwas-team@google.com</owner>
+  <summary>
+    Records the result of registering protocol handlers for PWAs. This is
+    triggered during OS integration synchronization whenever new protocol
+    handlers need to be registered for a recently installed app, when an user
+    allows an app as a protocol handler from chrome://settings/handlers or
+    during a manifest update process when the app previously did not have any
+    protocol handlers defined for it.
+  </summary>
+</histogram>
+
+<histogram name="WebApp.ProtocolHandlers.Unregistration.Result"
+    enum="BooleanSuccess" expires_after="2024-09-10">
+  <owner>dibyapal@chromium.org</owner>
+  <owner>desktop-pwas-team@google.com</owner>
+  <summary>
+    Records the result of unregistering protocol handlers for PWAs. This is
+    triggered during OS integration synchronization whenever an app is being
+    uninstalled, when a manifest update removes the approved protocol handlers
+    from an app or when an user has disallowed the app from its protocol
+    handlers from chrome://settings/handlers.
+  </summary>
+</histogram>
+
+<histogram name="WebApp.ProtocolHandlers.Update.Result" enum="BooleanSuccess"
+    expires_after="2024-09-10">
+  <owner>dibyapal@chromium.org</owner>
+  <owner>desktop-pwas-team@google.com</owner>
+  <summary>
+    Records the result of updating protocol handlers for PWAs. This is triggered
+    during OS integration synchronization whenever the protocol handlers for an
+    existing app needs to be updated as part of the manifest update process or
+    user approval changes from chrome://settings/handlers.
+  </summary>
+</histogram>
+
 <histogram name="WebApp.RunOnOsLogin.CommandCompletionState"
     enum="RunOnOSLoginCommandCompletionState" expires_after="2025-07-15">
   <owner>dibyapal@chromium.org</owner>
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index d94c88b..38608095 100644
--- a/tools/perf/core/perfetto_binary_roller/binary_deps.json
+++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -6,15 +6,15 @@
         },
         "win": {
             "hash": "09ca89530c264dfa4999fd4fd8d5187b67ccb2f7",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/1f3b5b40211b34322fe31a43556c8e4a028d0275/trace_processor_shell.exe"
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/efbbbd9ccae3a3c7eb5194848bd16aab2424fe00/trace_processor_shell.exe"
         },
         "linux_arm": {
             "hash": "6373f26144aad58f230d11d6a91efda5a09c9873",
             "full_remote_path": "perfetto-luci-artifacts/v31.0/linux-arm/trace_processor_shell"
         },
         "mac": {
-            "hash": "7afdabd804ab07ea5a481b682eeeacc95a270e0f",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/mac/1f3b5b40211b34322fe31a43556c8e4a028d0275/trace_processor_shell"
+            "hash": "0052abb3d69821310c4ac399476d0a24bcc4f148",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/mac/efbbbd9ccae3a3c7eb5194848bd16aab2424fe00/trace_processor_shell"
         },
         "mac_arm64": {
             "hash": "5f47ee79e59d00bf3889d30ca52315522c158040",
diff --git a/tools/rust/build_rust.py b/tools/rust/build_rust.py
index 1dacf526..428757fd 100755
--- a/tools/rust/build_rust.py
+++ b/tools/rust/build_rust.py
@@ -50,7 +50,7 @@
     os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', 'clang',
                  'scripts'))
 
-from build import (AddCMakeToPath, RunCommand)
+from build import (AddCMakeToPath, AddZlibToPath, GetLibXml2Dirs, RunCommand)
 from update import (CLANG_REVISION, CLANG_SUB_REVISION, LLVM_BUILD_DIR,
                     GetDefaultHostOs, RmTree, UpdatePackage)
 import build
@@ -78,6 +78,11 @@
     os.path.dirname(os.path.abspath(__file__)), 'config.toml.template')
 RUST_SRC_VENDOR_DIR = os.path.join(RUST_SRC_DIR, 'vendor')
 
+if sys.platform == 'win32':
+    LD_PATH_FLAG = '/LIBPATH:'
+else:
+    LD_PATH_FLAG = '-L'
+
 # Desired tools and libraries in our Rust toolchain.
 DISTRIBUTION_ARTIFACTS = [
     'cargo', 'clippy', 'compiler/rustc', 'library/std', 'rust-analyzer',
@@ -127,7 +132,8 @@
 
     subs = {}
     subs['INSTALL_DIR'] = quote_string(str(RUST_TOOLCHAIN_OUT_DIR))
-    subs['LLVM_ROOT'] = quote_string(str(llvm_libs_root))
+    subs['LLVM_LIB_ROOT'] = quote_string(str(llvm_libs_root))
+    subs['LLVM_BIN'] = quote_string(str(os.path.join(LLVM_BUILD_DIR, 'bin')))
     subs['PACKAGE_VERSION'] = GetPackageVersionForBuild()
 
     # ...and apply substitutions, writing to config.toml in Rust tree.
@@ -135,48 +141,81 @@
         output.write(template.substitute(subs))
 
 
-def RunXPy(sub, args, build_mac_arm, gcc_toolchain_path, verbose):
+def RunXPy(sub, args, zlib_path, libxml2_dirs, build_mac_arm,
+           gcc_toolchain_path, verbose):
     ''' Run x.py, Rust's build script'''
+    # We append to these flags, make sure they exist.
+    ENV_FLAGS = [
+        'CFLAGS',
+        'CXXFLAGS',
+        'LDFLAGS',
+        'RUSTFLAGS_BOOTSTRAP',
+        'RUSTFLAGS_NOT_BOOTSTRAP',
+    ]
+
     RUSTENV = collections.defaultdict(str, os.environ)
+    for f in ENV_FLAGS:
+        RUSTENV.setdefault(f, '')
 
-    ##### For C/C++ compilation steps #####
-    # The Rust toolchain does include some C/C++ code, and these env vars
-    # control the compilation and library making for that code.
-
-    clang_path = os.path.join(LLVM_BUILD_DIR, 'bin')
+    # The TARGET is consumed by the cc crate for building C/C++ sources, the
+    # CARGO_BUILD_TARGET is used for building Rust sources.
     if sys.platform == 'win32':
-        # The Rust toolchain build requires the use of link.exe already (it is
-        # the well-lit path, and we don't override the Rust linker when building
-        # the toolchain here), so we could use it for linking the small bits of
-        # C/C++ inside the Rust build too.
-        #
-        # That said, the `config.toml.template` file can override the linker for
-        # the Rust toolchain build.
-        #
-        # The `cc` crate wants to use link.exe by default for making static
-        # libs (the `AR` env var) but if clang-cl is being used to compile,
-        # then it wants to use llvm-lib.
-        #
-        # It's not totally clear if we ship llvm-lib, but it seems in practice
-        # that we have it available on the LLVM toolchain builders. Otherwise,
-        # we would need to use `lld-link /lib` and that doesn't work
-        # at the moment as the `cc` crate doesn't consume `ARFLAGS`:
-        # https://github.com/rust-lang/cc-rs/issues/762
-        #
-        # So, since we're using clang-cl, we point to `llvm-lib`, though we
-        # could equally let the `cc` crate find it (it looks in the same path
-        # as the compiler on Windows as of the time of this writing).
-        #
-        # We don't set a final linker with `LD`, as either the default works,
-        # or there are C executables or shared libs compiled during the Rust
-        # toolchain build.
-        RUSTENV['AR'] = os.path.join(clang_path, 'llvm-lib')
-        RUSTENV['CC'] = os.path.join(clang_path, 'clang-cl')
-        RUSTENV['CXX'] = os.path.join(clang_path, 'clang-cl')
+        RUSTENV['TARGET'] = 'x86_64-pc-windows-msvc'
+        RUSTENV['CARGO_BUILD_TARGET'] = 'x86_64-pc-windows-msvc'
+    elif sys.platform == 'darwin':
+        if build_mac_arm or platform.machine() == 'arm64':
+            RUSTENV['TARGET'] = 'arm64-apple-darwin'
+            RUSTENV['CARGO_BUILD_TARGET'] = 'arm64-apple-darwin'
+        else:
+            RUSTENV['TARGET'] = 'amd64-apple-darwin'
+            RUSTENV['CARGO_BUILD_TARGET'] = 'amd64-apple-darwin'
     else:
-        RUSTENV['AR'] = os.path.join(clang_path, 'llvm-ar')
-        RUSTENV['CC'] = os.path.join(clang_path, 'clang')
-        RUSTENV['CXX'] = os.path.join(clang_path, 'clang++')
+        RUSTENV['TARGET'] = 'x86_64-unknown-linux-gnu'
+        RUSTENV['CARGO_BUILD_TARGET'] = 'x86_64-unknown-linux-gnu'
+
+    # The AR, CC, CXX flags control the C/C++ compiler used through the `cc`
+    # crate. There are also C/C++ targets that are part of the Rust toolchain
+    # build for which the tool is controlled from `config.toml`, so these must
+    # be duplicated there.
+
+    clang_bin = os.path.join(LLVM_BUILD_DIR, 'bin')
+    if sys.platform == 'win32':
+        RUSTENV['AR'] = os.path.join(clang_bin, 'llvm-lib.exe')
+        RUSTENV['CC'] = os.path.join(clang_bin, 'clang-cl.exe')
+        RUSTENV['CXX'] = os.path.join(clang_bin, 'clang-cl.exe')
+    else:
+        RUSTENV['AR'] = os.path.join(clang_bin, 'llvm-ar')
+        RUSTENV['CC'] = os.path.join(clang_bin, 'clang')
+        RUSTENV['CXX'] = os.path.join(clang_bin, 'clang++')
+
+    if RUSTENV['CARGO_BUILD_TARGET'].endswith('apple-darwin'):
+        # The system/xcode compiler would find system headers correctly, but
+        # the Clang we've built does not. See
+        # https://github.com/llvm/llvm-project/issues/45225
+        sdk_path = subprocess.check_output(['xcrun', '--show-sdk-path'],
+                                           text=True).rstrip()
+        RUSTENV['CFLAGS'] += f' -isysroot {sdk_path}'
+        RUSTENV['CXXFLAGS'] += f' -isysroot {sdk_path}'
+
+    if zlib_path:
+        RUSTENV['CFLAGS'] += f' -I{zlib_path}'
+        RUSTENV['CXXFLAGS'] += f' -I{zlib_path}'
+        RUSTENV['LDFLAGS'] += f' {LD_PATH_FLAG}{zlib_path}'
+        RUSTENV['RUSTFLAGS_BOOTSTRAP'] += (f' -Clink-arg='
+                                           f'{LD_PATH_FLAG}{zlib_path}')
+        RUSTENV['RUSTFLAGS_NOT_BOOTSTRAP'] += (f' -Clink-arg='
+                                               f'{LD_PATH_FLAG}{zlib_path}')
+
+    if libxml2_dirs:
+        RUSTENV['CFLAGS'] += f' -I{libxml2_dirs.include_dir}'
+        RUSTENV['CXXFLAGS'] += f' -I{libxml2_dirs.include_dir}'
+        RUSTENV['LDFLAGS'] += f' {LD_PATH_FLAG}{libxml2_dirs.lib_dir}'
+        RUSTENV['RUSTFLAGS_BOOTSTRAP'] += (
+            f' -Clink-arg='
+            f'{LD_PATH_FLAG}{libxml2_dirs.lib_dir}')
+        RUSTENV['RUSTFLAGS_NOT_BOOTSTRAP'] += (
+            f' -Clink-arg='
+            f'{LD_PATH_FLAG}{libxml2_dirs.lib_dir}')
 
     if gcc_toolchain_path:
         # We use these flags to avoid linking with the system libstdc++.
@@ -184,21 +223,14 @@
         RUSTENV['CFLAGS'] += f' {gcc_toolchain_flag}'
         RUSTENV['CXXFLAGS'] += f' {gcc_toolchain_flag}'
         RUSTENV['LDFLAGS'] += f' {gcc_toolchain_flag}'
-
-    ##### For Rust compilation steps #####
-    # These env vars set arguments passed to the rust compiler when building
-    # Rust target.
-    #
-    # A `-Clink-arg=<foo>` arg passes `foo`` to the linker invovation.
-
-    RUSTENV['RUSTFLAGS_BOOTSTRAP'] = ''
-    if gcc_toolchain_path:
-        RUSTENV['RUSTFLAGS_BOOTSTRAP'] += f' -Clink-arg={gcc_toolchain_flag} '
+        # A `-Clink-arg=<foo>` arg passes `foo`` to the linker invovation.
+        RUSTENV['RUSTFLAGS_BOOTSTRAP'] += f' -Clink-arg={gcc_toolchain_flag}'
+        RUSTENV[
+            'RUSTFLAGS_NOT_BOOTSTRAP'] += f' -Clink-arg={gcc_toolchain_flag}'
         RUSTENV['RUSTFLAGS_BOOTSTRAP'] += (
             f' -L native={gcc_toolchain_path}/lib64')
-    RUSTENV['RUSTFLAGS_NOT_BOOTSTRAP'] = RUSTENV['RUSTFLAGS_BOOTSTRAP']
-
-    ##### Do the build now #####
+        RUSTENV['RUSTFLAGS_NOT_BOOTSTRAP'] += (
+            f' -L native={gcc_toolchain_path}/lib64')
 
     # Cargo normally stores files in $HOME. Override this.
     RUSTENV['CARGO_HOME'] = CARGO_HOME_DIR
@@ -313,25 +345,37 @@
 
     AddCMakeToPath()
 
+    # Require zlib compression.
+    if sys.platform == 'win32':
+        zlib_path = AddZlibToPath()
+    else:
+        zlib_path = None
+
+    # libxml2 is built when building LLVM, so we use that.
+    if sys.platform == 'win32':
+        libxml2_dirs = GetLibXml2Dirs()
+    else:
+        libxml2_dirs = None
+
     if args.run_xpy:
         if rest[0] == '--':
             rest = rest[1:]
-        RunXPy(rest[0], rest[1:], args.build_mac_arm, args.gcc_toolchain,
-               args.verbose)
+        RunXPy(rest[0], rest[1:], zlib_path, libxml2_dirs, args.build_mac_arm,
+               args.gcc_toolchain, args.verbose)
         return 0
     else:
         assert not rest
 
     if not args.skip_clean:
         print('Cleaning build artifacts...')
-        RunXPy('clean', [], args.build_mac_arm, args.gcc_toolchain,
-               args.verbose)
+        RunXPy('clean', [], zlib_path, libxml2_dirs, args.build_mac_arm,
+               args.gcc_toolchain, args.verbose)
 
     if not args.skip_test:
         print('Running stage 2 tests...')
         # Run a subset of tests. Tell x.py to keep the rustc we already built.
-        RunXPy('test', GetTestArgs(), args.build_mac_arm, args.gcc_toolchain,
-               args.verbose)
+        RunXPy('test', GetTestArgs(), zlib_path, libxml2_dirs,
+               args.build_mac_arm, args.gcc_toolchain, args.verbose)
 
     targets = [
         'library/proc_macro', 'library/std', 'src/tools/cargo',
@@ -341,8 +385,8 @@
     # Build stage 2 compiler, tools, and libraries. This should reuse earlier
     # stages from the test command (if run).
     print('Building stage 2 artifacts...')
-    RunXPy('build', ['--stage', '2'] + targets, args.build_mac_arm,
-           args.gcc_toolchain, args.verbose)
+    RunXPy('build', ['--stage', '2'] + targets, zlib_path, libxml2_dirs,
+           args.build_mac_arm, args.gcc_toolchain, args.verbose)
 
     if args.skip_install:
         # Rust is fully built. We can quit.
@@ -353,7 +397,8 @@
     if os.path.exists(RUST_TOOLCHAIN_OUT_DIR):
         shutil.rmtree(RUST_TOOLCHAIN_OUT_DIR)
 
-    RunXPy('install', DISTRIBUTION_ARTIFACTS, args.gcc_toolchain, args.verbose)
+    RunXPy('install', DISTRIBUTION_ARTIFACTS, zlib_path, libxml2_dirs,
+           args.gcc_toolchain, args.verbose)
 
     with open(VERSION_STAMP_PATH, 'w') as stamp:
         stamp.write(GetVersionStamp())
diff --git a/tools/rust/config.toml.template b/tools/rust/config.toml.template
index e7b8302..476ed5e7 100644
--- a/tools/rust/config.toml.template
+++ b/tools/rust/config.toml.template
@@ -33,16 +33,38 @@
 # with the rust compiler we build.
 profiler = true
 
-target = ["x86_64-unknown-linux-gnu"]
-
 [install]
 prefix = "$INSTALL_DIR"
 sysconfdir = "etc"
 
+[target.x86_64-pc-windows-msvc]
+llvm-config = "$LLVM_LIB_ROOT/bin/llvm-config"
+ranlib = "$LLVM_BIN/llvm-ranlib"
+ar = "$LLVM_BIN/llvm-lib"
+cc = "$LLVM_BIN/clang-cl"
+cxx = "$LLVM_BIN/clang-cl"
+# linker will be the system link.exe.
+
+[target.arm64-apple-darwin]
+llvm-config = "$LLVM_LIB_ROOT/bin/llvm-config"
+ranlib = "$LLVM_BIN/llvm-ranlib"
+ar = "$LLVM_BIN/llvm-ar"
+cc = "$LLVM_BIN/clang"
+cxx = "$LLVM_BIN/clang++"
+linker = "$LLVM_BIN/clang"
+
+[target.amd64-apple-darwin]
+llvm-config = "$LLVM_LIB_ROOT/bin/llvm-config"
+ranlib = "$LLVM_BIN/llvm-ranlib"
+ar = "$LLVM_BIN/llvm-ar"
+cc = "$LLVM_BIN/clang"
+cxx = "$LLVM_BIN/clang++"
+linker = "$LLVM_BIN/clang"
+
 [target.x86_64-unknown-linux-gnu]
-cc = "$LLVM_ROOT/bin/clang"
-cxx = "$LLVM_ROOT/bin/clang++"
-ar = "$LLVM_ROOT/bin/llvm-ar"
-ranlib = "$LLVM_ROOT/bin/llvm-ranlib"
-linker = "$LLVM_ROOT/bin/clang"
-llvm-config = "$LLVM_ROOT/bin/llvm-config"
+llvm-config = "$LLVM_LIB_ROOT/bin/llvm-config"
+ranlib = "$LLVM_BIN/llvm-ranlib"
+ar = "$LLVM_BIN/llvm-ar"
+cc = "$LLVM_BIN/clang"
+cxx = "$LLVM_BIN/clang++"
+linker = "$LLVM_BIN/clang"
diff --git a/tools/typescript/definitions/bookmarks.d.ts b/tools/typescript/definitions/bookmarks.d.ts
index 07fb946..2c27a8f 100644
--- a/tools/typescript/definitions/bookmarks.d.ts
+++ b/tools/typescript/definitions/bookmarks.d.ts
@@ -36,47 +36,41 @@
       export const MAX_WRITE_OPERATIONS_PER_HOUR: number;
       export const MAX_SUSTAINED_WRITE_OPERATIONS_PER_MINUTE: number;
 
-      export function get(
-          idOrIdList: string|string[],
-          callback: (p1: BookmarkTreeNode[]) => void): void;
+      export function get(idOrIdList: string|
+                          string[]): Promise<BookmarkTreeNode[]>;
 
-      export function getChildren(
-          id: string, callback: (p1: BookmarkTreeNode[]) => void): void;
+      export function getChildren(id: string): Promise<BookmarkTreeNode[]>;
 
-      export function getRecent(
-          numberOfItems: number,
-          callback: (p1: BookmarkTreeNode[]) => void): void;
+      export function getRecent(numberOfItems: number):
+          Promise<BookmarkTreeNode[]>;
 
-      export function getTree(callback: (p1: BookmarkTreeNode[]) => void): void;
+      export function getTree(): Promise<BookmarkTreeNode[]>;
 
-      export function getSubTree(
-          id: string, callback: (p1: BookmarkTreeNode[]) => void): void;
+      export function getSubTree(id: string): Promise<BookmarkTreeNode[]>;
 
-      export function search(
-          query: string|{
-            query?: string,
-            url?: string,
-            title?: string,
-          },
-          callback: (p1: BookmarkTreeNode[]) => void): void;
+      export function search(query: string|{
+        query?: string,
+        url?: string,
+        title?: string,
+      }): Promise<BookmarkTreeNode[]>;
 
-      export function create(
-          bookmark: CreateDetails,
-          callback?: (p1: BookmarkTreeNode) => void): void;
+      export function create(bookmark: CreateDetails):
+          Promise<BookmarkTreeNode>;
 
-      export function move(
-          id: string,
-          destination: {parentId: string|undefined, index: number|undefined},
-          callback?: (p1: BookmarkTreeNode) => void): void;
+      export function move(id: string, destination: {
+        parentId?: string,
+        index?: number,
+      }): Promise<BookmarkTreeNode>;
 
-      export function update(
-          id: string, changes: {title?: string, url?: string},
-          callback?: (p1: BookmarkTreeNode) => void): void;
+      export function update(id: string, changes: {
+        title?: string,
+        url?: string,
+      }): Promise<BookmarkTreeNode>;
 
-      export function remove(id: string, callback?: () => void): void;
-      export function removeTree(id: string, callback?: () => void): void;
-      function importAlias(callback?: () => void): void;
-      function exportAlias(callback?: () => void): void;
+      export function remove(id: string): Promise<void>;
+      export function removeTree(id: string): Promise<void>;
+      function importAlias(): Promise<void>;
+      function exportAlias(): Promise<void>;
       export {importAlias as import};
       export {exportAlias as export};
 
diff --git a/tools/typescript/ts_library.gni b/tools/typescript/ts_library.gni
index 9d014f7b..08a0be08 100644
--- a/tools/typescript/ts_library.gni
+++ b/tools/typescript/ts_library.gni
@@ -123,6 +123,12 @@
       "chrome://resources/cr_components/managed_dialog/managed_dialog.js|" + rebase_path(
               "$root_gen_dir/ui/webui/resources/preprocessed/cr_components/managed_dialog/managed_dialog.d.ts",
               target_gen_dir),
+      "chrome://resources/cr_components/localized_link/localized_link.js|" + rebase_path(
+              "$root_gen_dir/ui/webui/resources/preprocessed/cr_components/localized_link/localized_link.d.ts",
+              target_gen_dir),
+      "//resources/cr_components/localized_link/localized_link.js|" + rebase_path(
+              "$root_gen_dir/ui/webui/resources/preprocessed/cr_components/localized_link/localized_link.d.ts",
+              target_gen_dir),
 
       "chrome://resources/*|" +
           rebase_path("$root_gen_dir/ui/webui/resources/preprocessed/*",
diff --git a/ui/base/ime/ash/extension_ime_util.cc b/ui/base/ime/ash/extension_ime_util.cc
index 6c648b28..7f8b2ae 100644
--- a/ui/base/ime/ash/extension_ime_util.cc
+++ b/ui/base/ime/ash/extension_ime_util.cc
@@ -49,7 +49,7 @@
 #endif
 
 const char kBrailleImeExtensionId[] = "jddehjeebkoimngcbdkaahpobgicbffp";
-const char kBrailleImeExtensionPath[] = "chromeos/accessibility/braille_ime";
+const char kBrailleImeExtensionPath[] = "chromeos/accessibility";
 const char kBrailleImeEngineId[] =
     "_comp_ime_jddehjeebkoimngcbdkaahpobgicbffpbraille";
 
diff --git a/ui/chromeos/events/keyboard_capability.h b/ui/chromeos/events/keyboard_capability.h
index 7d1c053e..58ab677 100644
--- a/ui/chromeos/events/keyboard_capability.h
+++ b/ui/chromeos/events/keyboard_capability.h
@@ -15,12 +15,12 @@
 inline constexpr auto kLayout2TopRowKeyToFKeyMap =
     base::MakeFixedFlatMap<KeyboardCode, KeyboardCode>({
         {KeyboardCode::VKEY_BROWSER_BACK, KeyboardCode::VKEY_F1},
-        {KeyboardCode::VKEY_BROWSER_FORWARD, KeyboardCode::VKEY_F2},
-        {KeyboardCode::VKEY_BROWSER_REFRESH, KeyboardCode::VKEY_F3},
-        {KeyboardCode::VKEY_ZOOM, KeyboardCode::VKEY_F4},
-        {KeyboardCode::VKEY_MEDIA_LAUNCH_APP1, KeyboardCode::VKEY_F5},
-        {KeyboardCode::VKEY_BRIGHTNESS_DOWN, KeyboardCode::VKEY_F6},
-        {KeyboardCode::VKEY_BRIGHTNESS_UP, KeyboardCode::VKEY_F7},
+        {KeyboardCode::VKEY_BROWSER_REFRESH, KeyboardCode::VKEY_F2},
+        {KeyboardCode::VKEY_ZOOM, KeyboardCode::VKEY_F3},
+        {KeyboardCode::VKEY_MEDIA_LAUNCH_APP1, KeyboardCode::VKEY_F4},
+        {KeyboardCode::VKEY_BRIGHTNESS_DOWN, KeyboardCode::VKEY_F5},
+        {KeyboardCode::VKEY_BRIGHTNESS_UP, KeyboardCode::VKEY_F6},
+        {KeyboardCode::VKEY_MEDIA_PLAY_PAUSE, KeyboardCode::VKEY_F7},
         {KeyboardCode::VKEY_VOLUME_MUTE, KeyboardCode::VKEY_F8},
         {KeyboardCode::VKEY_VOLUME_DOWN, KeyboardCode::VKEY_F9},
         {KeyboardCode::VKEY_VOLUME_UP, KeyboardCode::VKEY_F10},
diff --git a/ui/compositor/DEPS b/ui/compositor/DEPS
index ab83408..28acae1 100644
--- a/ui/compositor/DEPS
+++ b/ui/compositor/DEPS
@@ -11,6 +11,7 @@
   "+ui/base",
   "+ui/accelerated_widget_mac",
   "+ui/display/display_switches.h",
+  "+ui/display/types/display_constants.h",
   "+ui/gfx",
   "+ui/gl",
 ]
diff --git a/ui/compositor/compositor.cc b/ui/compositor/compositor.cc
index b70819f..fdb11e7 100644
--- a/ui/compositor/compositor.cc
+++ b/ui/compositor/compositor.cc
@@ -500,6 +500,24 @@
     display_private_->SetDisplayColorSpaces(display_color_spaces_);
 }
 
+#if BUILDFLAG(IS_MAC)
+void Compositor::SetVSyncDisplayID(const int64_t display_id) {
+  if (display_id_ == display_id) {
+    return;
+  }
+
+  display_id_ = display_id;
+
+  if (display_private_) {
+    display_private_->SetVSyncDisplayID(display_id);
+  }
+}
+
+int64_t Compositor::display_id() const {
+  return display_id_;
+}
+#endif
+
 void Compositor::SetDisplayTransformHint(gfx::OverlayTransform hint) {
   host_->set_display_transform_hint(hint);
 }
diff --git a/ui/compositor/compositor.h b/ui/compositor/compositor.h
index 50cea82c..4bd8c466 100644
--- a/ui/compositor/compositor.h
+++ b/ui/compositor/compositor.h
@@ -47,6 +47,7 @@
 #include "ui/compositor/layer_animator_collection.h"
 #include "ui/compositor/throughput_tracker.h"
 #include "ui/compositor/throughput_tracker_host.h"
+#include "ui/display/types/display_constants.h"
 #include "ui/gfx/display_color_spaces.h"
 #include "ui/gfx/geometry/size.h"
 #include "ui/gfx/geometry/vector2d.h"
@@ -246,6 +247,12 @@
   void SetDisplayColorSpaces(
       const gfx::DisplayColorSpaces& display_color_spaces);
 
+#if BUILDFLAG(IS_MAC)
+  // Set the current CGDirectDisplayID and update the private client.
+  void SetVSyncDisplayID(const int64_t display_id);
+  int64_t display_id() const;
+#endif
+
   const gfx::DisplayColorSpaces& display_color_spaces() const {
     return display_color_spaces_;
   }
@@ -514,6 +521,11 @@
   // A sequence number of a current compositor frame for use with metrics.
   int activated_frame_count_ = 0;
 
+#if BUILDFLAG(IS_MAC)
+  // Current CGDirectDisplayID for the screen.
+  int64_t display_id_ = display::kInvalidDisplayId;
+#endif
+
   // Current vsync refresh rate per second. Initialized to 60hz as a reasonable
   // value until first begin frame arrives with the real refresh rate.
   float refresh_rate_ = 60.f;
diff --git a/ui/compositor/test/in_process_context_factory.cc b/ui/compositor/test/in_process_context_factory.cc
index 501e2ba..9764634 100644
--- a/ui/compositor/test/in_process_context_factory.cc
+++ b/ui/compositor/test/in_process_context_factory.cc
@@ -96,6 +96,9 @@
     vsync_interval_ = interval;
   }
   void SetOutputIsSecure(bool secure) override {}
+#if BUILDFLAG(IS_MAC)
+  void SetVSyncDisplayID(int64_t display_id) override {}
+#endif
   void ForceImmediateDrawAndSwapIfPossible() override {}
   void AddVSyncParameterObserver(
       mojo::PendingRemote<viz::mojom::VSyncParameterObserver> observer)
diff --git a/ui/shell_dialogs/BUILD.gn b/ui/shell_dialogs/BUILD.gn
index c746f23..7d1dbaca 100644
--- a/ui/shell_dialogs/BUILD.gn
+++ b/ui/shell_dialogs/BUILD.gn
@@ -149,6 +149,9 @@
 
   if (is_mac) {
     sources += [ "select_file_dialog_mac_unittest.mm" ]
+    weak_frameworks = [
+      "UniformTypeIdentifiers.framework",  # macOS 11
+    ]
   }
 
   if (is_win) {
diff --git a/ui/shell_dialogs/select_file_dialog_mac_unittest.mm b/ui/shell_dialogs/select_file_dialog_mac_unittest.mm
index cbe810bc0..ab457b38 100644
--- a/ui/shell_dialogs/select_file_dialog_mac_unittest.mm
+++ b/ui/shell_dialogs/select_file_dialog_mac_unittest.mm
@@ -2,15 +2,17 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "base/memory/raw_ptr.h"
-
 #import "ui/shell_dialogs/select_file_dialog_mac.h"
 
+#import <UniformTypeIdentifiers/UniformTypeIdentifiers.h>
+
 #include "base/callback_forward.h"
 #include "base/files/file_util.h"
 #import "base/mac/foundation_util.h"
 #include "base/mac/mac_util.h"
+#include "base/memory/raw_ptr.h"
 #include "base/memory/ref_counted.h"
+#include "base/ranges/algorithm.h"
 #include "base/run_loop.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/sys_string_conversions.h"
@@ -18,6 +20,7 @@
 #include "base/test/task_environment.h"
 #include "components/remote_cocoa/app_shim/select_file_dialog_bridge.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "testing/gtest_mac.h"
 #include "ui/shell_dialogs/select_file_policy.h"
 
 #define EXPECT_EQ_BOOL(a, b) \
@@ -29,26 +32,15 @@
 // Returns a vector containing extension descriptions for a given popup.
 std::vector<std::u16string> GetExtensionDescriptionList(NSPopUpButton* popup) {
   std::vector<std::u16string> extension_descriptions;
-  for (NSString* description in [popup itemTitles])
+  for (NSString* description in popup.itemTitles) {
     extension_descriptions.push_back(base::SysNSStringToUTF16(description));
+  }
   return extension_descriptions;
 }
 
-// Fake user event to select the item at the given `index` from the extension
-// dropdown popup.
-void SelectItemAtIndex(NSPopUpButton* popup, int index) {
-  [[popup menu] performActionForItemAtIndex:index];
-}
-
 // Returns the NSPopupButton associated with the given `panel`.
 NSPopUpButton* GetPopup(NSSavePanel* panel) {
-  return [[panel accessoryView] viewWithTag:kFileTypePopupTag];
-}
-
-// Helper method to convert an array to a vector.
-template <typename T, size_t N>
-std::vector<T> GetVectorFromArray(const T (&data)[N]) {
-  return std::vector<T>(data, data + N);
+  return [panel.accessoryView viewWithTag:kFileTypePopupTag];
 }
 
 }  // namespace
@@ -116,8 +108,9 @@
                     object:panel
                      queue:nil
                 usingBlock:^(NSNotification* note) {
-                  if (panel.visible)
+                  if (panel.visible) {
                     quit_closure.Run();
+                  }
                 }];
     run_loop.Run();
     [NSNotificationCenter.defaultCenter removeObserver:observer];
@@ -160,23 +153,17 @@
 // Verify that the extension popup has the correct description and changing the
 // popup item changes the allowed file types.
 TEST_F(SelectFileDialogMacTest, ExtensionPopup) {
-  const std::string extensions_arr[][2] = {{"html", "htm"}, {"jpeg", "jpg"}};
-  const std::u16string extension_descriptions_arr[] = {u"Webpage", u"Image"};
-
   SelectFileDialog::FileTypeInfo file_type_info;
-  file_type_info.extensions.push_back(
-      GetVectorFromArray<std::string>(extensions_arr[0]));
-  file_type_info.extensions.push_back(
-      GetVectorFromArray<std::string>(extensions_arr[1]));
-  file_type_info.extension_description_overrides =
-      GetVectorFromArray<std::u16string>(extension_descriptions_arr);
+  file_type_info.extensions = {{"html", "htm"}, {"jpeg", "jpg"}};
+  file_type_info.extension_description_overrides = {u"Webpage", u"Image"};
   file_type_info.include_all_files = false;
 
   FileDialogArguments args;
   args.file_types = &file_type_info;
+
   NSSavePanel* panel = SelectFileWithParams(args);
   NSPopUpButton* popup = GetPopup(panel);
-  EXPECT_TRUE(popup);
+  ASSERT_TRUE(popup);
 
   // Check that the dropdown list created has the correct description.
   const std::vector<std::u16string> extension_descriptions =
@@ -185,41 +172,45 @@
             extension_descriptions);
 
   // Ensure other file types are not allowed.
-  EXPECT_FALSE([panel allowsOtherFileTypes]);
+  EXPECT_FALSE(panel.allowsOtherFileTypes);
 
   // Check that the first item was selected, since a file_type_index of 0 was
   // passed and no default extension was provided.
-  EXPECT_EQ(0, [popup indexOfSelectedItem]);
-  EXPECT_TRUE([[panel allowedFileTypes] containsObject:@"htm"]);
-  EXPECT_TRUE([[panel allowedFileTypes] containsObject:@"html"]);
-  // Extensions should appear in order of input.
-  EXPECT_LT([[panel allowedFileTypes] indexOfObject:@"html"],
-            [[panel allowedFileTypes] indexOfObject:@"htm"]);
-  EXPECT_FALSE([[panel allowedFileTypes] containsObject:@"jpg"]);
+  EXPECT_EQ(0, popup.indexOfSelectedItem);
+
+  if (@available(macOS 11, *)) {
+    ASSERT_EQ(1lu, panel.allowedContentTypes.count);
+    EXPECT_NSEQ(UTTypeHTML, panel.allowedContentTypes[0]);
+  } else {
+    EXPECT_TRUE([panel.allowedFileTypes containsObject:@"htm"]);
+    EXPECT_TRUE([panel.allowedFileTypes containsObject:@"html"]);
+    // Extensions should appear in order of input.
+    EXPECT_LT([panel.allowedFileTypes indexOfObject:@"html"],
+              [panel.allowedFileTypes indexOfObject:@"htm"]);
+    EXPECT_FALSE([panel.allowedFileTypes containsObject:@"jpg"]);
+  }
 
   // Select the second item.
-  SelectItemAtIndex(popup, 1);
-  EXPECT_EQ(1, [popup indexOfSelectedItem]);
-  EXPECT_TRUE([[panel allowedFileTypes] containsObject:@"jpg"]);
-  EXPECT_TRUE([[panel allowedFileTypes] containsObject:@"jpeg"]);
-  // Extensions should appear in order of input.
-  EXPECT_LT([[panel allowedFileTypes] indexOfObject:@"jpeg"],
-            [[panel allowedFileTypes] indexOfObject:@"jpg"]);
-  EXPECT_FALSE([[panel allowedFileTypes] containsObject:@"html"]);
+  [popup.menu performActionForItemAtIndex:1];
+  EXPECT_EQ(1, popup.indexOfSelectedItem);
+  if (@available(macOS 11, *)) {
+    ASSERT_EQ(1lu, panel.allowedContentTypes.count);
+    EXPECT_NSEQ(UTTypeJPEG, panel.allowedContentTypes[0]);
+  } else {
+    EXPECT_TRUE([panel.allowedFileTypes containsObject:@"jpg"]);
+    EXPECT_TRUE([panel.allowedFileTypes containsObject:@"jpeg"]);
+    // Extensions should appear in order of input.
+    EXPECT_LT([panel.allowedFileTypes indexOfObject:@"jpeg"],
+              [panel.allowedFileTypes indexOfObject:@"jpg"]);
+    EXPECT_FALSE([panel.allowedFileTypes containsObject:@"html"]);
+  }
 }
 
 // Verify file_type_info.include_all_files argument is respected.
 TEST_P(SelectFileDialogMacOpenAndSaveTest, IncludeAllFiles) {
-  const std::string extensions_arr[][2] = {{"html", "htm"}, {"jpeg", "jpg"}};
-  const std::u16string extension_descriptions_arr[] = {u"Webpage", u"Image"};
-
   SelectFileDialog::FileTypeInfo file_type_info;
-  file_type_info.extensions.push_back(
-      GetVectorFromArray<std::string>(extensions_arr[0]));
-  file_type_info.extensions.push_back(
-      GetVectorFromArray<std::string>(extensions_arr[1]));
-  file_type_info.extension_description_overrides =
-      GetVectorFromArray<std::u16string>(extension_descriptions_arr);
+  file_type_info.extensions = {{"html", "htm"}, {"jpeg", "jpg"}};
+  file_type_info.extension_description_overrides = {u"Webpage", u"Image"};
   file_type_info.include_all_files = true;
 
   FileDialogArguments args;
@@ -227,12 +218,11 @@
   args.file_types = &file_type_info;
 
   NSSavePanel* panel = SelectFileWithParams(args);
-
   NSPopUpButton* popup = GetPopup(panel);
-  EXPECT_TRUE(popup);
+  ASSERT_TRUE(popup);
 
   // Ensure other file types are allowed.
-  EXPECT_TRUE([panel allowsOtherFileTypes]);
+  EXPECT_TRUE(panel.allowsOtherFileTypes);
 
   // Check that the dropdown list created has the correct description.
   const std::vector<std::u16string> extension_descriptions =
@@ -258,96 +248,97 @@
 // Verify that file_type_index and default_extension arguments cause the
 // appropriate extension group to be initially selected.
 TEST_F(SelectFileDialogMacTest, InitialSelection) {
-  const std::string extensions_arr[][2] = {{"html", "htm"}, {"jpeg", "jpg"}};
-  const std::u16string extension_descriptions_arr[] = {u"Webpage", u"Image"};
-
   SelectFileDialog::FileTypeInfo file_type_info;
-  file_type_info.extensions.push_back(
-      GetVectorFromArray<std::string>(extensions_arr[0]));
-  file_type_info.extensions.push_back(
-      GetVectorFromArray<std::string>(extensions_arr[1]));
-  file_type_info.extension_description_overrides =
-      GetVectorFromArray<std::u16string>(extension_descriptions_arr);
+  file_type_info.extensions = {{"html", "htm"}, {"jpeg", "jpg"}};
+  file_type_info.extension_description_overrides = {u"Webpage", u"Image"};
 
   FileDialogArguments args;
   args.file_types = &file_type_info;
-
   args.file_type_index = 2;
   args.default_extension = "jpg";
+
   NSSavePanel* panel = SelectFileWithParams(args);
   NSPopUpButton* popup = GetPopup(panel);
-  EXPECT_TRUE(popup);
-  // Verify that the file_type_index causes the second item to be initially
+  ASSERT_TRUE(popup);
+
+  // Verify that the `file_type_index` causes the second item to be initially
   // selected.
-  EXPECT_EQ(1, [popup indexOfSelectedItem]);
-  EXPECT_TRUE([[panel allowedFileTypes] containsObject:@"jpg"]);
-  EXPECT_TRUE([[panel allowedFileTypes] containsObject:@"jpeg"]);
-  EXPECT_FALSE([[panel allowedFileTypes] containsObject:@"html"]);
+  EXPECT_EQ(1, popup.indexOfSelectedItem);
+  if (@available(macOS 11, *)) {
+    ASSERT_EQ(1lu, panel.allowedContentTypes.count);
+    EXPECT_NSEQ(UTTypeJPEG, panel.allowedContentTypes[0]);
+  } else {
+    EXPECT_TRUE([panel.allowedFileTypes containsObject:@"jpg"]);
+    EXPECT_TRUE([panel.allowedFileTypes containsObject:@"jpeg"]);
+    EXPECT_FALSE([panel.allowedFileTypes containsObject:@"html"]);
+  }
 
   ResetDialog();
   args.file_type_index = 0;
   args.default_extension = "pdf";
   panel = SelectFileWithParams(args);
   popup = GetPopup(panel);
-  EXPECT_TRUE(popup);
+  ASSERT_TRUE(popup);
+
   // Verify that the first item was selected, since the default extension passed
   // was not present in the extension list.
-  EXPECT_EQ(0, [popup indexOfSelectedItem]);
-  EXPECT_TRUE([[panel allowedFileTypes] containsObject:@"html"]);
-  EXPECT_TRUE([[panel allowedFileTypes] containsObject:@"htm"]);
-  EXPECT_FALSE([[panel allowedFileTypes] containsObject:@"pdf"]);
-  EXPECT_FALSE([[panel allowedFileTypes] containsObject:@"jpeg"]);
+  EXPECT_EQ(0, popup.indexOfSelectedItem);
+  if (@available(macOS 11, *)) {
+    ASSERT_EQ(1lu, panel.allowedContentTypes.count);
+    EXPECT_NSEQ(UTTypeHTML, panel.allowedContentTypes[0]);
+  } else {
+    EXPECT_TRUE([panel.allowedFileTypes containsObject:@"html"]);
+    EXPECT_TRUE([panel.allowedFileTypes containsObject:@"htm"]);
+    EXPECT_FALSE([panel.allowedFileTypes containsObject:@"pdf"]);
+    EXPECT_FALSE([panel.allowedFileTypes containsObject:@"jpeg"]);
+  }
 
   ResetDialog();
   args.file_type_index = 0;
   args.default_extension = "jpg";
   panel = SelectFileWithParams(args);
   popup = GetPopup(panel);
-  EXPECT_TRUE(popup);
+  ASSERT_TRUE(popup);
+
   // Verify that the extension group corresponding to the default extension is
   // initially selected.
-  EXPECT_EQ(1, [popup indexOfSelectedItem]);
-  // The allowed file types should just contain the default extension.
-  EXPECT_EQ(1lu, [[panel allowedFileTypes] count]);
-  EXPECT_TRUE([[panel allowedFileTypes] containsObject:@"jpg"]);
-  EXPECT_FALSE([[panel allowedFileTypes] containsObject:@"jpeg"]);
-  EXPECT_FALSE([[panel allowedFileTypes] containsObject:@"html"]);
+  EXPECT_EQ(1, popup.indexOfSelectedItem);
+  if (@available(macOS 11, *)) {
+    ASSERT_EQ(1lu, panel.allowedContentTypes.count);
+    EXPECT_NSEQ(UTTypeJPEG, panel.allowedContentTypes[0]);
+  } else {
+    EXPECT_TRUE([panel.allowedFileTypes containsObject:@"jpg"]);
+    EXPECT_TRUE([panel.allowedFileTypes containsObject:@"jpeg"]);
+    EXPECT_FALSE([panel.allowedFileTypes containsObject:@"html"]);
+  }
 }
 
 // Verify that an appropriate extension description is shown even if an empty
 // extension description is passed for a given extension group.
 TEST_F(SelectFileDialogMacTest, EmptyDescription) {
-  const std::string extensions_arr[][1] = {{"pdf"}, {"jpg"}, {"qqq"}};
-  const std::u16string extension_descriptions_arr[] = {u"", u"Image", u""};
-
   SelectFileDialog::FileTypeInfo file_type_info;
-  file_type_info.extensions.push_back(
-      GetVectorFromArray<std::string>(extensions_arr[0]));
-  file_type_info.extensions.push_back(
-      GetVectorFromArray<std::string>(extensions_arr[1]));
-  file_type_info.extensions.push_back(
-      GetVectorFromArray<std::string>(extensions_arr[2]));
-  file_type_info.extension_description_overrides =
-      GetVectorFromArray<std::u16string>(extension_descriptions_arr);
+  file_type_info.extensions = {{"pdf"}, {"jpg"}, {"qqq"}};
+  file_type_info.extension_description_overrides = {u"", u"Image", u""};
 
   FileDialogArguments args;
   args.file_types = &file_type_info;
-
   NSSavePanel* panel = SelectFileWithParams(args);
   NSPopUpButton* popup = GetPopup(panel);
-  EXPECT_TRUE(popup);
+  ASSERT_TRUE(popup);
 
-  // Check that the dropdown list created has the correct description.
   const std::vector<std::u16string> extension_descriptions =
       GetExtensionDescriptionList(popup);
-  EXPECT_EQ(3lu, extension_descriptions.size());
+  ASSERT_EQ(3lu, extension_descriptions.size());
+
   // Verify that the correct system description is produced for known file types
-  // like pdf if no extension description is provided by the client. Search the
-  // string for "PDF" as the system may display:
-  // - Portable Document Format (PDF)
-  // - PDF document
-  EXPECT_NE(std::u16string::npos, extension_descriptions[0].find(u"PDF"));
+  // like pdf if no extension description is provided by the client. Modern
+  // versions of macOS have settled on "PDF document" as the official
+  // description.
+  EXPECT_EQ(u"PDF document", extension_descriptions[0]);
+
+  // Verify that if an override description is provided, it is used.
   EXPECT_EQ(u"Image", extension_descriptions[1]);
+
   // Verify the description for unknown file types if no extension description
   // is provided by the client.
   EXPECT_EQ(u"QQQ File (.qqq)", extension_descriptions[2]);
@@ -363,21 +354,21 @@
   args.file_types = &file_type_info;
 
   NSSavePanel* panel = SelectFileWithParams(args);
-  EXPECT_FALSE([panel accessoryView]);
+  EXPECT_FALSE(panel.accessoryView);
 
   // Ensure other file types are allowed.
-  EXPECT_TRUE([panel allowsOtherFileTypes]);
+  EXPECT_TRUE(panel.allowsOtherFileTypes);
 }
 
 // Verify that passing a null file_types value causes no extension dropdown to
 // display.
 TEST_F(SelectFileDialogMacTest, FileTypesNull) {
   NSSavePanel* panel = SelectFileWithParams({});
-  EXPECT_TRUE([panel allowsOtherFileTypes]);
-  EXPECT_FALSE([panel accessoryView]);
+  EXPECT_TRUE(panel.allowsOtherFileTypes);
+  EXPECT_FALSE(panel.accessoryView);
 
   // Ensure other file types are allowed.
-  EXPECT_TRUE([panel allowsOtherFileTypes]);
+  EXPECT_TRUE(panel.allowsOtherFileTypes);
 }
 
 // Verify that appropriate properties are set on the NSSavePanel for different
@@ -417,22 +408,22 @@
     NSSavePanel* panel = SelectFileWithParams(args);
 
     EXPECT_EQ_BOOL(test_cases[i].options & HAS_ACCESSORY_VIEW,
-                   [panel accessoryView]);
+                   panel.accessoryView);
     EXPECT_EQ_BOOL(test_cases[i].options & CREATE_DIRS,
-                   [panel canCreateDirectories]);
+                   panel.canCreateDirectories);
     EXPECT_EQ(test_cases[i].prompt, base::SysNSStringToUTF8([panel prompt]));
 
     if (args.type != SelectFileDialog::SELECT_SAVEAS_FILE) {
       NSOpenPanel* open_panel = base::mac::ObjCCast<NSOpenPanel>(panel);
       // Verify that for types other than save file dialogs, an NSOpenPanel is
       // created.
-      EXPECT_TRUE(open_panel);
+      ASSERT_TRUE(open_panel);
       EXPECT_EQ_BOOL(test_cases[i].options & PICK_FILES,
-                     [open_panel canChooseFiles]);
+                     open_panel.canChooseFiles);
       EXPECT_EQ_BOOL(test_cases[i].options & PICK_DIRS,
-                     [open_panel canChooseDirectories]);
+                     open_panel.canChooseDirectories);
       EXPECT_EQ_BOOL(test_cases[i].options & MULTIPLE_SELECTION,
-                     [open_panel allowsMultipleSelection]);
+                     open_panel.allowsMultipleSelection);
     }
   }
 }
@@ -443,7 +434,7 @@
   FileDialogArguments args;
   args.title = base::ASCIIToUTF16(test_title);
   NSSavePanel* panel = SelectFileWithParams(args);
-  EXPECT_EQ(test_title, base::SysNSStringToUTF8([panel message]));
+  EXPECT_EQ(test_title, base::SysNSStringToUTF8(panel.message));
 }
 
 // Verify that multiple file dialogs are correctly handled.
@@ -451,14 +442,14 @@
   FileDialogArguments args;
   NSSavePanel* panel1 = SelectFileWithParams(args);
   NSSavePanel* panel2 = SelectFileWithParams(args);
-  EXPECT_EQ(2lu, GetActivePanelCount());
+  ASSERT_EQ(2lu, GetActivePanelCount());
 
   // Verify closing the panel decreases the panel count.
   base::RunLoop run_loop1;
   SetDialogClosedCallback(run_loop1.QuitClosure());
   [panel1 cancel:nil];
   run_loop1.Run();
-  EXPECT_EQ(1lu, GetActivePanelCount());
+  ASSERT_EQ(1lu, GetActivePanelCount());
 
   // In 10.15, file picker dialogs are remote, and the restriction of apps not
   // being allowed to OK their own file requests has been extended from just
@@ -466,10 +457,11 @@
   // but if not, at least try to close them all.
   base::RunLoop run_loop2;
   SetDialogClosedCallback(run_loop2.QuitClosure());
-  if (base::mac::IsAtMostOS10_14())
+  if (base::mac::IsAtMostOS10_14()) {
     [panel2 ok:nil];
-  else
+  } else {
     [panel2 cancel:nil];
+  }
   run_loop2.Run();
   EXPECT_EQ(0lu, GetActivePanelCount());
 
@@ -483,12 +475,12 @@
 
   NSSavePanel* panel = SelectFileWithParams(args);
 
-  [panel setExtensionHidden:NO];
+  panel.extensionHidden = NO;
 
   EXPECT_EQ(args.default_path.DirName(),
-            base::mac::NSStringToFilePath([[panel directoryURL] path]));
+            base::mac::NSStringToFilePath(panel.directoryURL.path));
   EXPECT_EQ(args.default_path.BaseName(),
-            base::mac::NSStringToFilePath([panel nameFieldStringValue]));
+            base::mac::NSStringToFilePath(panel.nameFieldStringValue));
 }
 
 // Verify that the file dialog does not hide extension for filenames with
@@ -501,40 +493,35 @@
 
   args.default_path = base::FilePath(FILE_PATH_LITERAL(fake_path_normal));
   NSSavePanel* panel = SelectFileWithParams(args);
-  EXPECT_TRUE([panel canSelectHiddenExtension]);
-  EXPECT_TRUE([panel isExtensionHidden]);
+  EXPECT_TRUE(panel.canSelectHiddenExtension);
+  EXPECT_TRUE(panel.extensionHidden);
 
   ResetDialog();
   args.default_path = base::FilePath(FILE_PATH_LITERAL(fake_path_multiple));
   panel = SelectFileWithParams(args);
-  EXPECT_FALSE([panel canSelectHiddenExtension]);
-  EXPECT_FALSE([panel isExtensionHidden]);
+  EXPECT_FALSE(panel.canSelectHiddenExtension);
+  EXPECT_FALSE(panel.extensionHidden);
 
   ResetDialog();
   args.default_path = base::FilePath(FILE_PATH_LITERAL(fake_path_long));
   panel = SelectFileWithParams(args);
-  EXPECT_FALSE([panel canSelectHiddenExtension]);
-  EXPECT_FALSE([panel isExtensionHidden]);
+  EXPECT_FALSE(panel.canSelectHiddenExtension);
+  EXPECT_FALSE(panel.extensionHidden);
 }
 
 // Verify that the file dialog does not hide extension when the
 // `keep_extension_visible` flag is set to true.
 TEST_F(SelectFileDialogMacTest, KeepExtensionVisible) {
-  const std::string extensions_arr[][2] = {{"html", "htm"}, {"jpeg", "jpg"}};
-
   SelectFileDialog::FileTypeInfo file_type_info;
-  file_type_info.extensions.push_back(
-      GetVectorFromArray<std::string>(extensions_arr[0]));
-  file_type_info.extensions.push_back(
-      GetVectorFromArray<std::string>(extensions_arr[1]));
+  file_type_info.extensions = {{"html", "htm"}, {"jpeg", "jpg"}};
   file_type_info.keep_extension_visible = true;
 
   FileDialogArguments args;
   args.file_types = &file_type_info;
 
   NSSavePanel* panel = SelectFileWithParams(args);
-  EXPECT_FALSE([panel canSelectHiddenExtension]);
-  EXPECT_FALSE([panel isExtensionHidden]);
+  EXPECT_FALSE(panel.canSelectHiddenExtension);
+  EXPECT_FALSE(panel.extensionHidden);
 }
 
 // Test to ensure lifetime is sound if a reference to
diff --git a/ui/views/cocoa/native_widget_mac_ns_window_host.mm b/ui/views/cocoa/native_widget_mac_ns_window_host.mm
index 5b71ee9..0cb9bea 100644
--- a/ui/views/cocoa/native_widget_mac_ns_window_host.mm
+++ b/ui/views/cocoa/native_widget_mac_ns_window_host.mm
@@ -1185,6 +1185,7 @@
                                display_.device_scale_factor(),
                                display_.color_spaces());
   }
+
   if (display_id_changed) {
     display_link_ = ui::DisplayLinkMac::GetForDisplay(
         base::checked_cast<CGDirectDisplayID>(display_.id()));
@@ -1193,6 +1194,10 @@
       // created, so this should not be a fatal error.
       LOG(ERROR) << "Failed to create display link.";
     }
+
+    if (compositor_) {
+      compositor_->compositor()->SetVSyncDisplayID(display_.id());
+    }
   }
 }
 
diff --git a/ui/views/controls/button/toggle_button.h b/ui/views/controls/button/toggle_button.h
index 8939a49..a6a86fda 100644
--- a/ui/views/controls/button/toggle_button.h
+++ b/ui/views/controls/button/toggle_button.h
@@ -101,6 +101,12 @@
   bool accepts_events_ = true;
 };
 
+BEGIN_VIEW_BUILDER(VIEWS_EXPORT, ToggleButton, Button)
+VIEW_BUILDER_PROPERTY(bool, IsOn)
+END_VIEW_BUILDER
+
 }  // namespace views
 
+DEFINE_VIEW_BUILDER(VIEWS_EXPORT, ToggleButton)
+
 #endif  // UI_VIEWS_CONTROLS_BUTTON_TOGGLE_BUTTON_H_
diff --git a/ui/webui/examples/browser/ui/web/webui.cc b/ui/webui/examples/browser/ui/web/webui.cc
index 2a1214c..1254c06 100644
--- a/ui/webui/examples/browser/ui/web/webui.cc
+++ b/ui/webui/examples/browser/ui/web/webui.cc
@@ -51,13 +51,12 @@
 }  // namespace
 
 WebUI::WebUI(content::WebUI* web_ui) : content::WebUIController(web_ui) {
-  content::WebUIDataSource* source = content::WebUIDataSource::Create(kHost);
+  content::WebUIDataSource* source = content::WebUIDataSource::CreateAndAdd(
+      web_ui->GetWebContents()->GetBrowserContext(), kHost);
   SetupWebUIDataSource(
       source,
       base::make_span(kWebuiGalleryResources, kWebuiGalleryResourcesSize),
       IDR_WEBUI_GALLERY_WEBUI_GALLERY_HTML);
-  content::WebUIDataSource::Add(web_ui->GetWebContents()->GetBrowserContext(),
-                                source);
 }
 
 WebUI::~WebUI() = default;
diff --git a/ui/webui/resources/BUILD.gn b/ui/webui/resources/BUILD.gn
index 184e4ef9..10e7c39d 100644
--- a/ui/webui/resources/BUILD.gn
+++ b/ui/webui/resources/BUILD.gn
@@ -51,6 +51,7 @@
       "cr_components/app_management:build_grdp",
       "cr_components/customize_themes:build_grdp",
       "cr_components/help_bubble:build_grdp",
+      "cr_components/localized_link:build_grdp",
       "cr_components/managed_dialog:build_grdp",
       "cr_components/managed_footnote:build_grdp",
       "cr_components/most_visited:build_grdp",
@@ -62,6 +63,7 @@
       "$target_gen_dir/cr_components/app_management/resources.grdp",
       "$target_gen_dir/cr_components/customize_themes/resources.grdp",
       "$target_gen_dir/cr_components/help_bubble/resources.grdp",
+      "$target_gen_dir/cr_components/localized_link/resources.grdp",
       "$target_gen_dir/cr_components/managed_dialog/resources.grdp",
       "$target_gen_dir/cr_components/managed_footnote/resources.grdp",
       "$target_gen_dir/cr_components/most_visited/resources.grdp",
@@ -140,10 +142,6 @@
     "cr_elements:preprocess",
     "js:preprocess",
   ]
-
-  if (include_polymer) {
-    public_deps += [ "cr_components:preprocess" ]
-  }
 }
 
 # TypeScript targets
@@ -168,13 +166,6 @@
     in_files += [ "js/" + f ]
   }
 
-  if (include_polymer) {
-    in_files += [
-      "cr_components/localized_link/localized_link.html.ts",
-      "cr_components/localized_link/localized_link.ts",
-    ]
-  }
-
   definitions = [
     "//tools/typescript/definitions/chrome_send.d.ts",
     "//tools/typescript/definitions/pending.d.ts",
diff --git a/ui/webui/resources/cr_components/BUILD.gn b/ui/webui/resources/cr_components/BUILD.gn
deleted file mode 100644
index dbeda2fb..0000000
--- a/ui/webui/resources/cr_components/BUILD.gn
+++ /dev/null
@@ -1,43 +0,0 @@
-# Copyright 2018 The Chromium Authors
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import("//build/config/chromeos/ui_mode.gni")
-import("//third_party/closure_compiler/compile_js.gni")
-import("//tools/grit/preprocess_if_expr.gni")
-import("//tools/polymer/html_to_wrapper.gni")
-import("//ui/webui/resources/tools/generate_grd.gni")
-import("//ui/webui/webui_features.gni")
-
-assert(!is_android && !is_ios)
-
-preprocess_folder =
-    "$root_gen_dir/ui/webui/resources/preprocessed/cr_components"
-
-group("preprocess") {
-  public_deps = [
-    ":preprocess_generated_ts",
-    ":preprocess_src_ts",
-  ]
-}
-
-# TS files are passed to a separate target so that they are not listed in the
-# |out_manifest|.
-preprocess_if_expr("preprocess_src_ts") {
-  in_folder = "."
-  out_folder = preprocess_folder
-  in_files = [ "localized_link/localized_link.ts" ]
-}
-
-# TS files are passed to a separate target so that they are not listed in the
-# |out_manifest|.
-preprocess_if_expr("preprocess_generated_ts") {
-  deps = [ ":html_wrapper_files" ]
-  in_folder = target_gen_dir
-  out_folder = preprocess_folder
-  in_files = [ "localized_link/localized_link.html.ts" ]
-}
-
-html_to_wrapper("html_wrapper_files") {
-  in_files = [ "localized_link/localized_link.html" ]
-}
diff --git a/ui/webui/resources/cr_components/localized_link/BUILD.gn b/ui/webui/resources/cr_components/localized_link/BUILD.gn
new file mode 100644
index 0000000..204efab
--- /dev/null
+++ b/ui/webui/resources/cr_components/localized_link/BUILD.gn
@@ -0,0 +1,23 @@
+# Copyright 2023 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//ui/webui/resources/include_polymer.gni")
+import("//ui/webui/webui_features.gni")
+import("../../tools/build_cr_component.gni")
+
+assert(include_polymer)
+
+build_cr_component("build") {
+  grd_prefix = "cr_components_localized_link"
+
+  web_component_files = [ "localized_link.ts" ]
+
+  tsc_dir = "$root_gen_dir/ui/webui/resources/preprocessed/cr_components/localized_link"
+  ts_deps = [
+    "//third_party/polymer/v3_0:library",
+    "//ui/webui/resources:library",
+  ]
+
+  optimize = optimize_webui
+}
diff --git a/ui/webui/resources/cr_components/localized_link/localized_link.ts b/ui/webui/resources/cr_components/localized_link/localized_link.ts
index 84c2e2d0..4a19839d 100644
--- a/ui/webui/resources/cr_components/localized_link/localized_link.ts
+++ b/ui/webui/resources/cr_components/localized_link/localized_link.ts
@@ -20,8 +20,8 @@
  * string with a link and sometimes returns a normal string.
  */
 
-import '../../cr_elements/cr_shared_vars.css.js';
-import '../../cr_elements/cr_shared_style.css.js';
+import '//resources/cr_elements/cr_shared_vars.css.js';
+import '//resources/cr_elements/cr_shared_style.css.js';
 
 import {assert, assertNotReached} from '//resources/js/assert_ts.js';
 import {sanitizeInnerHtml} from '//resources/js/parse_html_subset.js';
diff --git a/ui/webui/resources/cr_components/localized_link/tsconfig_base.json b/ui/webui/resources/cr_components/localized_link/tsconfig_base.json
new file mode 100644
index 0000000..3513d446
--- /dev/null
+++ b/ui/webui/resources/cr_components/localized_link/tsconfig_base.json
@@ -0,0 +1,7 @@
+{
+  "extends": "../../../../../tools/typescript/tsconfig_base.json",
+  "compilerOptions": {
+    "noUnusedLocals": false,
+    "strictPropertyInitialization": false
+  }
+}
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/ChildProcessServiceImpl.java b/weblayer/browser/java/org/chromium/weblayer_private/ChildProcessServiceImpl.java
index efbbd8f..ccde651 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/ChildProcessServiceImpl.java
+++ b/weblayer/browser/java/org/chromium/weblayer_private/ChildProcessServiceImpl.java
@@ -7,13 +7,11 @@
 import android.app.Service;
 import android.content.Context;
 import android.content.Intent;
-import android.os.Build;
 import android.os.IBinder;
 import android.webkit.WebViewFactory;
 
 import com.google.android.gms.common.GooglePlayServicesUtilLight;
 
-import org.chromium.base.Log;
 import org.chromium.base.StrictModeContext;
 import org.chromium.base.library_loader.LibraryLoader;
 import org.chromium.base.library_loader.NativeLibraryPreloader;
@@ -26,8 +24,6 @@
 import org.chromium.weblayer_private.interfaces.ObjectWrapper;
 import org.chromium.weblayer_private.interfaces.StrictModeWorkaround;
 
-import java.lang.reflect.Method;
-
 /**
  * Implementation of IChildProcessService.
  */
@@ -88,20 +84,7 @@
     private static int loadNativeLibrary(String packageName, ClassLoader cl) {
         // Loading the library triggers disk access.
         try (StrictModeContext ignored = StrictModeContext.allowDiskReads()) {
-            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
-                return WebViewFactory.loadWebViewNativeLibraryFromPackage(packageName, cl);
-            } else {
-                try {
-                    Method loadNativeLibrary =
-                            WebViewFactory.class.getDeclaredMethod("loadNativeLibrary");
-                    loadNativeLibrary.setAccessible(true);
-                    loadNativeLibrary.invoke(null);
-                    return 0; // LIBLOAD_SUCCESS
-                } catch (ReflectiveOperationException e) {
-                    Log.e(TAG, "Failed to load native library.", e);
-                    return 6; // LIBLOAD_FAILED_TO_LOAD_LIBRARY
-                }
-            }
+            return WebViewFactory.loadWebViewNativeLibraryFromPackage(packageName, cl);
         }
     }
 }
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/WebLayerImpl.java b/weblayer/browser/java/org/chromium/weblayer_private/WebLayerImpl.java
index ff507d80..fbe529b9 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/WebLayerImpl.java
+++ b/weblayer/browser/java/org/chromium/weblayer_private/WebLayerImpl.java
@@ -738,28 +738,11 @@
      */
     private static int getPackageId(Context appContext, String implPackageName) {
         try {
-            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
-                Constructor<WebViewDelegate> constructor =
-                        WebViewDelegate.class.getDeclaredConstructor();
-                constructor.setAccessible(true);
-                WebViewDelegate delegate = constructor.newInstance();
-                return delegate.getPackageId(appContext.getResources(), implPackageName);
-            } else {
-                // In L WebViewDelegate did not yet exist, so we have to look inside AssetManager.
-                Method getAssignedPackageIdentifiers =
-                        AssetManager.class.getMethod("getAssignedPackageIdentifiers");
-                SparseArray<String> packageIdentifiers =
-                        (SparseArray) getAssignedPackageIdentifiers.invoke(
-                                appContext.getResources().getAssets());
-                for (int i = 0; i < packageIdentifiers.size(); i++) {
-                    final String name = packageIdentifiers.valueAt(i);
-
-                    if (implPackageName.equals(name)) {
-                        return packageIdentifiers.keyAt(i);
-                    }
-                }
-                throw new RuntimeException("Package not found: " + implPackageName);
-            }
+            Constructor<WebViewDelegate> constructor =
+                    WebViewDelegate.class.getDeclaredConstructor();
+            constructor.setAccessible(true);
+            WebViewDelegate delegate = constructor.newInstance();
+            return delegate.getPackageId(appContext.getResources(), implPackageName);
         } catch (ReflectiveOperationException e) {
             throw new RuntimeException(e);
         }
@@ -820,11 +803,6 @@
     }
 
     private static boolean supportsBindingToWebViewService(Context context, String packageName) {
-        // BIND_EXTERNAL_SERVICE is not supported before N.
-        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
-            return false;
-        }
-
         // Android N has issues with WebView with the non-system user.
         if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
             try {
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/WebLayerNotificationChannels.java b/weblayer/browser/java/org/chromium/weblayer_private/WebLayerNotificationChannels.java
index fd8e575..d9bb590 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/WebLayerNotificationChannels.java
+++ b/weblayer/browser/java/org/chromium/weblayer_private/WebLayerNotificationChannels.java
@@ -69,7 +69,6 @@
     }
 
     // Map defined in static inner class so it's only initialized lazily.
-    @RequiresApi(Build.VERSION_CODES.N) // for NotificationManager.IMPORTANCE_* constants
     private static class PredefinedChannels {
         static final Map<String, PredefinedChannel> MAP;
 
diff --git a/weblayer/browser/webui/net_export_ui.cc b/weblayer/browser/webui/net_export_ui.cc
index d6685888..7889a72 100644
--- a/weblayer/browser/webui/net_export_ui.cc
+++ b/weblayer/browser/webui/net_export_ui.cc
@@ -159,14 +159,12 @@
 NetExportUI::NetExportUI(content::WebUI* web_ui) : WebUIController(web_ui) {
   web_ui->AddMessageHandler(std::make_unique<NetExportMessageHandler>());
 
-  content::WebUIDataSource* source =
-      content::WebUIDataSource::Create(kChromeUINetExportHost);
+  content::WebUIDataSource* source = content::WebUIDataSource::CreateAndAdd(
+      web_ui->GetWebContents()->GetBrowserContext(), kChromeUINetExportHost);
   source->UseStringsJs();
   source->AddResourcePath(net_log::kNetExportUICSS, IDR_NET_LOG_NET_EXPORT_CSS);
   source->AddResourcePath(net_log::kNetExportUIJS, IDR_NET_LOG_NET_EXPORT_JS);
   source->SetDefaultResource(IDR_NET_LOG_NET_EXPORT_HTML);
-  content::WebUIDataSource::Add(web_ui->GetWebContents()->GetBrowserContext(),
-                                source);
 }
 
 NetExportUI::~NetExportUI() = default;
diff --git a/weblayer/browser/webui/weblayer_internals_ui.cc b/weblayer/browser/webui/weblayer_internals_ui.cc
index 856418ea..afae5cf 100644
--- a/weblayer/browser/webui/weblayer_internals_ui.cc
+++ b/weblayer/browser/webui/weblayer_internals_ui.cc
@@ -16,14 +16,12 @@
 
 WebLayerInternalsUI::WebLayerInternalsUI(content::WebUI* web_ui)
     : ui::MojoWebUIController(web_ui) {
-  content::WebUIDataSource* source =
-      content::WebUIDataSource::Create(kChromeUIWebLayerHost);
+  content::WebUIDataSource* source = content::WebUIDataSource::CreateAndAdd(
+      web_ui->GetWebContents()->GetBrowserContext(), kChromeUIWebLayerHost);
   source->AddResourcePath("weblayer_internals.js", IDR_WEBLAYER_INTERNALS_JS);
   source->AddResourcePath("weblayer_internals.mojom-webui.js",
                           IDR_WEBLAYER_INTERNALS_MOJO_JS);
   source->SetDefaultResource(IDR_WEBLAYER_INTERNALS_HTML);
-  content::WebUIDataSource::Add(web_ui->GetWebContents()->GetBrowserContext(),
-                                source);
 }
 
 WebLayerInternalsUI::~WebLayerInternalsUI() = default;
diff --git a/weblayer/public/java/org/chromium/weblayer/WebLayer.java b/weblayer/public/java/org/chromium/weblayer/WebLayer.java
index 837c55e..cf2bf31 100644
--- a/weblayer/public/java/org/chromium/weblayer/WebLayer.java
+++ b/weblayer/public/java/org/chromium/weblayer/WebLayer.java
@@ -582,15 +582,11 @@
         sAppContext = appContext;
         if (implPackageName != null) {
             sRemoteContext = createRemoteContextFromPackageName(appContext, implPackageName);
-        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+        } else {
             Method getContext =
                     webViewFactoryClass.getDeclaredMethod("getWebViewContextAndSetProvider");
             getContext.setAccessible(true);
             sRemoteContext = (Context) getContext.invoke(null);
-        } else {
-            implPackageName =
-                    (String) webViewFactoryClass.getMethod("getWebViewPackageName").invoke(null);
-            sRemoteContext = createRemoteContextFromPackageName(appContext, implPackageName);
         }
         sContextCreationTime = SystemClock.elapsedRealtime() - start;
         return sRemoteContext;
diff --git a/weblayer/public/javatests/org/chromium/weblayer/WebViewCompatibilityHelperTest.java b/weblayer/public/javatests/org/chromium/weblayer/WebViewCompatibilityHelperTest.java
index 270cb67..963fde3 100644
--- a/weblayer/public/javatests/org/chromium/weblayer/WebViewCompatibilityHelperTest.java
+++ b/weblayer/public/javatests/org/chromium/weblayer/WebViewCompatibilityHelperTest.java
@@ -5,7 +5,6 @@
 package org.chromium.weblayer;
 
 import android.content.Context;
-import android.os.Build;
 import android.support.test.InstrumentationRegistry;
 
 import androidx.test.filters.SmallTest;
@@ -15,7 +14,6 @@
 import org.junit.runner.RunWith;
 
 import org.chromium.base.test.BaseJUnit4ClassRunner;
-import org.chromium.base.test.util.MinAndroidSdkLevel;
 
 /**
  * Tests for (@link WebViewCompatibilityHelper}.
@@ -24,7 +22,6 @@
 public class WebViewCompatibilityHelperTest {
     @Test
     @SmallTest
-    @MinAndroidSdkLevel(Build.VERSION_CODES.N)
     public void testLibraryPaths() throws Exception {
         Context appContext = InstrumentationRegistry.getTargetContext();
         ClassLoader classLoader = WebViewCompatibilityHelper.initialize(appContext);