diff --git a/DEPS b/DEPS
index 64a0fa3..e9286bc 100644
--- a/DEPS
+++ b/DEPS
@@ -308,7 +308,7 @@
   # 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': 'c42ac527ff0e66875adf1e849e6211e3bbfa51dd',
+  'skia_revision': 'f4b79968de2a319bd8e0e1b958a4b70aa734482f',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
@@ -383,7 +383,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling chromium_variations
   # and whatever else without interference from each other.
-  'chromium_variations_revision': 'f9a3242f9bf85b3e41e25bd57d4a0a85a2a8ff2d',
+  'chromium_variations_revision': '901d1840715d46381809bb62cbe1c6d1b7724660',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling CrossBench
   # and whatever else without interference from each other.
@@ -399,7 +399,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling devtools-frontend
   # and whatever else without interference from each other.
-  'devtools_frontend_revision': '97f497e6be3a25ee3d489c31a183cafcb441ba15',
+  'devtools_frontend_revision': '7f28655bc9e25129032852b084aa07a171d1cca2',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libprotobuf-mutator
   # and whatever else without interference from each other.
@@ -423,7 +423,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'dawn_revision': 'c62e9d9be33be8b9d1b437840cfb02b03d8c35a5',
+  'dawn_revision': '3e968653f537c5f38f1b45d07efe8051c6021779',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -502,7 +502,7 @@
   'libcxx_revision':       '28aa23ffb4c7344914a5b4ac7169f12e5a12333f',
 
   # GN CIPD package version.
-  'gn_version': 'git_revision:b5adfe5f574d7110b80feb9aae6fae97c366840b',
+  'gn_version': 'git_revision:f99e015ac35f689cfdbf46e4eb174e5d2da78d8e',
 
   # ninja CIPD package version.
   # https://chrome-infra-packages.appspot.com/p/infra/3pp/tools/ninja
@@ -823,7 +823,7 @@
 
   'src/clank': {
     'url': Var('chrome_git') + '/clank/internal/apps.git' + '@' +
-    '95dba2c15cf581680a54dd78d2bed0fc2acc1558',
+    '361cdfa3f0ddcd5ed6ed4283e32b62ee619e7f2d',
     'condition': 'checkout_android and checkout_src_internal',
   },
 
@@ -1181,7 +1181,7 @@
 
   # For Linux and Chromium OS.
   'src/third_party/cros_system_api': {
-      'url': Var('chromium_git') + '/chromiumos/platform2/system_api.git' + '@' + 'e5100d618713aadf930da88e2e8458a6432f15c6',
+      'url': Var('chromium_git') + '/chromiumos/platform2/system_api.git' + '@' + '12d5e386005a211570cfdf9849d2fa6a3b38594b',
       'condition': 'checkout_linux or checkout_chromeos',
   },
 
@@ -1201,7 +1201,7 @@
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
 
   'src/third_party/devtools-frontend-internal': {
-      'url': Var('chrome_git') + '/devtools/devtools-internal.git' + '@' + '9c8974e12b5baa4671db8afa6675bf191ff97e99',
+      'url': Var('chrome_git') + '/devtools/devtools-internal.git' + '@' + '4e0cc80c5875ea35501cf9ff3b859cc2c8565ccd',
     'condition': 'checkout_src_internal',
   },
 
@@ -1661,7 +1661,7 @@
     Var('pdfium_git') + '/pdfium.git' + '@' +  Var('pdfium_revision'),
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + '28eadcf5efccc550c5317bdccaca0e64d9698f65',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + 'd6af17fef257af28ee2417216ef87d5c5b743a1b',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '8ef97ff3b7332e38e61b347a2fbed425a4617151',
@@ -1846,7 +1846,7 @@
     Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + 'e082b08475761a2ba6a3349dfea72f704c8b68d4',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '1768705d997b466f528624ba143dbe55fba94a36',
+    Var('webrtc_git') + '/src.git' + '@' + '348438154abf25bfda544c7e4460ef2258eb1eab',
 
   # 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.
@@ -1871,7 +1871,7 @@
   },
 
   'src/third_party/xnnpack/src':
-    Var('chromium_git') + '/external/github.com/google/XNNPACK.git' + '@' + '8951decff5114f70bae7cc2e23b732812e73acc7',
+    Var('chromium_git') + '/external/github.com/google/XNNPACK.git' + '@' + 'a68aa0a24b0d3e1c75f2f7c0915b70121cee0470',
 
   'src/tools/page_cycler/acid3':
     Var('chromium_git') + '/chromium/deps/acid3.git' + '@' + 'a926d0a32e02c4c03ae95bb798e6c780e0e184ba',
@@ -1969,7 +1969,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/help_app/app',
-        'version': 'xf98Xu_TEAbZLR4Rn11rHcLSZ4FLJ3IRgiZDTTx3n7UC',
+        'version': 'zOVvjrx-YUZ_7L2eQsnfpw0Wf8Klnm0f2GaLp3gGbTIC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
@@ -4009,7 +4009,7 @@
 
   'src/ios_internal':  {
       'url': Var('chrome_git') + '/chrome/ios_internal.git' + '@' +
-        '29461128ddc44d4245c80f5c10c859c21b325fbb',
+        'e0312dbf4d16d483662226c982fafc3165f1aecb',
       'condition': 'checkout_ios and checkout_src_internal',
   },
 
diff --git a/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java b/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java
index 599a9a9..550fa83 100644
--- a/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java
+++ b/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java
@@ -220,16 +220,15 @@
                 AndroidAutofillFeatures.ANDROID_AUTOFILL_PREFILL_REQUESTS_FOR_LOGIN_FORMS_NAME,
                 "When enabled, prefill requests are supported for login forms."),
         Flag.baseFeature(
-                AndroidAutofillFeatures
-                        .ANDROID_AUTOFILL_SIGNATURE_FOR_PREFILL_REQUEST_SIMILARITY_CHECK_NAME,
-                "When enabled, the comparison between the cached form and the currently focused"
-                        + " form that is used to decide whether to show a bottom sheet is performed"
-                        + " by comparing form signatures."),
-        Flag.baseFeature(
                 AndroidAutofillFeatures.ANDROID_AUTOFILL_SUPPORT_VISIBILITY_CHANGES_NAME,
                 "Enables communicating visibility changes of form fields of a form in an "
                         + "ongoing Autofill session to Android AutofillManager."),
         Flag.baseFeature(
+                AndroidAutofillFeatures.ANDROID_AUTOFILL_USE_PWM_PREDICTIONS_FOR_OVERRIDES_NAME,
+                "When enabled, the comparison between the cached form and the currently focused"
+                        + " form that is used to decide whether to show a bottom sheet is performed"
+                        + " by comparing password manager's FormDataParser predictions."),
+        Flag.baseFeature(
                 AutofillFeatures.AUTOFILL_ENABLE_DEPENDENT_LOCALITY_PARSING,
                 "Enables parsing dependent locality fields (e.g. Bairros in Brazil)."),
         Flag.baseFeature(
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index b43c68a..e04637f2 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -68,6 +68,8 @@
     "accelerators/keyboard_code_util.h",
     "accelerators/magnifier_key_scroller.cc",
     "accelerators/magnifier_key_scroller.h",
+    "accelerators/modifier_key_combo_recorder.cc",
+    "accelerators/modifier_key_combo_recorder.h",
     "accelerators/pre_target_accelerator_handler.cc",
     "accelerators/pre_target_accelerator_handler.h",
     "accelerators/shortcut_input_handler.cc",
@@ -3184,6 +3186,7 @@
     "accelerators/accelerator_unittest.cc",
     "accelerators/ash_accelerator_configuration_unittest.cc",
     "accelerators/magnifier_key_scroller_unittest.cc",
+    "accelerators/modifier_key_combo_recorder_unittest.cc",
     "accelerators/shortcut_input_handler_unittest.cc",
     "accelerators/spoken_feedback_toggler_unittest.cc",
     "accelerometer/accel_gyro_samples_observer_unittest.cc",
@@ -4118,6 +4121,7 @@
     "//ui/events:gesture_detection",
     "//ui/events:test_support",
     "//ui/events/ash",
+    "//ui/events/ash:test_support",
     "//ui/events/ash:unit_tests",
     "//ui/events/devices",
     "//ui/events/devices:test_support",
diff --git a/ash/accelerators/modifier_key_combo_recorder.cc b/ash/accelerators/modifier_key_combo_recorder.cc
new file mode 100644
index 0000000..41bb2ed8
--- /dev/null
+++ b/ash/accelerators/modifier_key_combo_recorder.cc
@@ -0,0 +1,243 @@
+// Copyright 2024 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/accelerators/modifier_key_combo_recorder.h"
+
+#include <optional>
+#include <string_view>
+#include <tuple>
+
+#include "ash/events/event_rewriter_controller_impl.h"
+#include "ash/events/prerewritten_event_forwarder.h"
+#include "ash/public/cpp/accelerators_util.h"
+#include "ash/shell.h"
+#include "base/containers/fixed_flat_set.h"
+#include "base/metrics/histogram_functions.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/strings/string_piece.h"
+#include "ui/base/ime/ash/input_method_manager.h"
+#include "ui/events/ash/keyboard_capability.h"
+#include "ui/events/event_constants.h"
+#include "ui/events/keycodes/dom/dom_code.h"
+#include "ui/events/keycodes/dom/dom_key.h"
+#include "ui/events/keycodes/keyboard_codes_posix.h"
+#include "ui/events/types/event_type.h"
+
+namespace ash {
+
+namespace {
+
+using DeviceType = ui::KeyboardCapability::DeviceType;
+using Modifier = ModifierKeyComboRecorder::Modifier;
+using ModifierFlag = ModifierKeyComboRecorder::ModifierFlag;
+using ModifierLocation = ModifierKeyComboRecorder::ModifierLocation;
+
+constexpr auto kShiftKeys = base::MakeFixedFlatSet<ui::KeyboardCode>({
+    ui::VKEY_SHIFT,
+    ui::VKEY_LSHIFT,
+    ui::VKEY_RSHIFT,
+});
+
+constexpr auto kAltKeys = base::MakeFixedFlatSet<ui::KeyboardCode>({
+    ui::VKEY_MENU,
+    ui::VKEY_LMENU,
+    ui::VKEY_RMENU,
+});
+
+constexpr auto kAltGrKeys = base::MakeFixedFlatSet<ui::KeyboardCode>({
+    ui::VKEY_ALTGR,
+});
+
+constexpr auto kMetaKeys = base::MakeFixedFlatSet<ui::KeyboardCode>({
+    ui::VKEY_LWIN,
+    ui::VKEY_RWIN,
+});
+
+constexpr auto kControlKeys = base::MakeFixedFlatSet<ui::KeyboardCode>({
+    ui::VKEY_CONTROL,
+    ui::VKEY_LCONTROL,
+    ui::VKEY_RCONTROL,
+});
+
+// The ModifierKeyCombo metric is formed as follows:
+// The bottom 16 bits are composed of the `AcceleratorKeyInputType` that matches
+// what key was pressed. The top 16 bits are composed of bitfields from the
+// `ModifierFlag` enum for each modifier (including differentiating between Left
+// and Right modifiers).
+uint32_t CalculateHash(AcceleratorKeyInputType key_type,
+                       uint32_t modifier_flags) {
+  static_assert(sizeof(AcceleratorKeyInputType) <= sizeof(uint16_t));
+  static_assert(static_cast<uint32_t>(ModifierFlag::kMaxValue) <=
+                (sizeof(uint16_t) * 8));
+  return (modifier_flags << (sizeof(uint16_t) * 8)) +
+         static_cast<uint16_t>(key_type);
+}
+
+ModifierFlag GetModifierFlagFromModifierAndLocation(Modifier modifier,
+                                                    ModifierLocation location) {
+  switch (modifier) {
+    case Modifier::kMeta:
+      return location == ModifierLocation::kLeft ? ModifierFlag::kMetaLeft
+                                                 : ModifierFlag::kMetaRight;
+    case Modifier::kControl:
+      return location == ModifierLocation::kLeft ? ModifierFlag::kControlLeft
+                                                 : ModifierFlag::kControlRight;
+    case Modifier::kAlt: {
+      return location == ModifierLocation::kLeft ? ModifierFlag::kAltLeft
+                                                 : ModifierFlag::kAltRight;
+    }
+    case Modifier::kShift:
+      return location == ModifierLocation::kLeft ? ModifierFlag::kShiftLeft
+                                                 : ModifierFlag::kShiftRight;
+  }
+}
+
+std::optional<std::pair<Modifier, ModifierLocation>> GetModifierFromKeyEvent(
+    const ui::KeyEvent& key_event) {
+  switch (key_event.code()) {
+    case ui::DomCode::META_LEFT:
+      return std::make_pair(Modifier::kMeta, ModifierLocation::kLeft);
+    case ui::DomCode::META_RIGHT:
+      return std::make_pair(Modifier::kMeta, ModifierLocation::kRight);
+    case ui::DomCode::CONTROL_LEFT:
+      return std::make_pair(Modifier::kControl, ModifierLocation::kLeft);
+    case ui::DomCode::CONTROL_RIGHT:
+      return std::make_pair(Modifier::kControl, ModifierLocation::kRight);
+    case ui::DomCode::ALT_LEFT:
+      return std::make_pair(Modifier::kAlt, ModifierLocation::kLeft);
+    case ui::DomCode::ALT_RIGHT:
+      return std::make_pair(Modifier::kAlt, ModifierLocation::kRight);
+    case ui::DomCode::SHIFT_LEFT:
+      return std::make_pair(Modifier::kShift, ModifierLocation::kLeft);
+    case ui::DomCode::SHIFT_RIGHT:
+      return std::make_pair(Modifier::kShift, ModifierLocation::kRight);
+    default:
+      return std::nullopt;
+  }
+}
+
+void RecordMetricForDeviceType(ui::KeyboardCapability::DeviceType device_type,
+                               uint32_t hash) {
+  switch (device_type) {
+    case ui::KeyboardCapability::DeviceType::kDeviceExternalChromeOsKeyboard:
+      UMA_HISTOGRAM_SPARSE("ChromeOS.Inputs.ModifierKeyCombo.CrOSExternal",
+                           hash);
+      break;
+    case ui::KeyboardCapability::DeviceType::kDeviceExternalAppleKeyboard:
+    case ui::KeyboardCapability::DeviceType::
+        kDeviceExternalNullTopRowChromeOsKeyboard:
+    case ui::KeyboardCapability::DeviceType::kDeviceExternalGenericKeyboard:
+    case ui::KeyboardCapability::DeviceType::kDeviceExternalUnknown:
+    case ui::KeyboardCapability::DeviceType::kDeviceHotrodRemote:
+    case ui::KeyboardCapability::DeviceType::kDeviceVirtualCoreKeyboard:
+      UMA_HISTOGRAM_SPARSE("ChromeOS.Inputs.ModifierKeyCombo.External", hash);
+      break;
+    case ui::KeyboardCapability::DeviceType::kDeviceUnknown:
+    case ui::KeyboardCapability::DeviceType::kDeviceInternalKeyboard:
+    case ui::KeyboardCapability::DeviceType::kDeviceInternalRevenKeyboard:
+      UMA_HISTOGRAM_SPARSE("ChromeOS.Inputs.ModifierKeyCombo.Internal", hash);
+      break;
+  }
+}
+
+}  // namespace
+
+ModifierKeyComboRecorder::ModifierKeyComboRecorder() = default;
+ModifierKeyComboRecorder::~ModifierKeyComboRecorder() {
+  CHECK(Shell::Get());
+  auto* event_forwarder =
+      Shell::Get()->event_rewriter_controller()->prerewritten_event_forwarder();
+  if (initialized_ && event_forwarder) {
+    event_forwarder->RemoveObserver(this);
+  }
+}
+
+void ModifierKeyComboRecorder::Initialize() {
+  CHECK(Shell::Get());
+  auto* event_forwarder =
+      Shell::Get()->event_rewriter_controller()->prerewritten_event_forwarder();
+  if (!event_forwarder) {
+    LOG(ERROR) << "Attempted to initialiaze ModifierKeyComboRecorder before "
+               << "PrerewrittenEventForwarder was initialized.";
+    return;
+  }
+
+  initialized_ = true;
+  event_forwarder->AddObserver(this);
+}
+
+void ModifierKeyComboRecorder::OnPrerewriteKeyInputEvent(
+    const ui::KeyEvent& key_event) {
+  if (key_event.type() == ui::ET_KEY_RELEASED || key_event.is_repeat()) {
+    return;
+  }
+
+  UpdateModifierLocations(key_event);
+
+  const AcceleratorKeyInputType type = GetKeyInputTypeFromKeyEvent(key_event);
+  const uint32_t modifier_flags = GenerateModifierFlagsFromKeyEvent(key_event);
+  const uint32_t hash = CalculateHash(type, modifier_flags);
+
+  // Do not emit the metric if its only an alpha or digit key pressed.
+  if ((type == AcceleratorKeyInputType::kAlpha ||
+       type == AcceleratorKeyInputType::kDigit) &&
+      modifier_flags == 0) {
+    return;
+  }
+
+  const auto device_type = Shell::Get()->keyboard_capability()->GetDeviceType(
+      key_event.source_device_id());
+  RecordMetricForDeviceType(device_type, hash);
+}
+
+uint32_t ModifierKeyComboRecorder::GenerateModifierFlagsFromKeyEvent(
+    const ui::KeyEvent& event) {
+  uint32_t modifier_flags = 0;
+  if (event.flags() & ui::EF_ALT_DOWN && !kAltKeys.contains(event.key_code())) {
+    modifier_flags += GetModifierFlagFromModifier(Modifier::kAlt);
+  }
+
+  // AltGr can be check purely from the modifier flags.
+  if (event.flags() & ui::EF_ALTGR_DOWN &&
+      !kAltGrKeys.contains(event.key_code())) {
+    modifier_flags += 1 << static_cast<uint32_t>(ModifierFlag::kAltGr);
+  }
+
+  if (event.flags() & ui::EF_CONTROL_DOWN &&
+      !kControlKeys.contains(event.key_code())) {
+    modifier_flags += GetModifierFlagFromModifier(Modifier::kControl);
+  }
+
+  if (event.flags() & ui::EF_COMMAND_DOWN &&
+      !kMetaKeys.contains(event.key_code())) {
+    modifier_flags += GetModifierFlagFromModifier(Modifier::kMeta);
+  }
+
+  if (event.flags() & ui::EF_SHIFT_DOWN &&
+      !kShiftKeys.contains(event.key_code())) {
+    modifier_flags += GetModifierFlagFromModifier(Modifier::kShift);
+  }
+
+  return modifier_flags;
+}
+
+uint32_t ModifierKeyComboRecorder::GetModifierFlagFromModifier(
+    Modifier modifier) {
+  const ModifierFlag modifier_flag = GetModifierFlagFromModifierAndLocation(
+      modifier, modifier_locations_[static_cast<uint32_t>(modifier)]);
+  return 1 << static_cast<uint32_t>(modifier_flag);
+}
+
+void ModifierKeyComboRecorder::UpdateModifierLocations(
+    const ui::KeyEvent& event) {
+  auto modifier_modifier_location_pair = GetModifierFromKeyEvent(event);
+  if (!modifier_modifier_location_pair) {
+    return;
+  }
+
+  const auto& [modifier, modifier_location] = *modifier_modifier_location_pair;
+  modifier_locations_[static_cast<uint32_t>(modifier)] = modifier_location;
+}
+
+}  // namespace ash
diff --git a/ash/accelerators/modifier_key_combo_recorder.h b/ash/accelerators/modifier_key_combo_recorder.h
new file mode 100644
index 0000000..3c80866f
--- /dev/null
+++ b/ash/accelerators/modifier_key_combo_recorder.h
@@ -0,0 +1,70 @@
+// Copyright 2024 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_ACCELERATORS_MODIFIER_KEY_COMBO_RECORDER_H_
+#define ASH_ACCELERATORS_MODIFIER_KEY_COMBO_RECORDER_H_
+
+#include "ash/ash_export.h"
+#include "ash/events/prerewritten_event_forwarder.h"
+#include "ui/events/event.h"
+#include "ui/events/event_constants.h"
+#include "ui/events/event_handler.h"
+
+namespace ash {
+
+// Emits a metric for what keys a user presses on their keyboard. It categorizes
+// in broad categories like "Alphabetic" or "Numpad" including their modifiers
+// within the computed metric hash.
+class ASH_EXPORT ModifierKeyComboRecorder
+    : public PrerewrittenEventForwarder::Observer {
+ public:
+  enum class ModifierLocation { kLeft, kRight };
+
+  enum class Modifier {
+    kMeta,
+    kControl,
+    kAlt,
+    kShift,
+    kMaxValue = kShift,
+  };
+
+  enum class ModifierFlag : uint8_t {
+    kMetaLeft,
+    kMetaRight,
+    kControlLeft,
+    kControlRight,
+    kAltLeft,
+    kAltRight,
+    kShiftLeft,
+    kShiftRight,
+    kAltGr,
+    kMaxValue = kAltGr,
+  };
+
+  ModifierKeyComboRecorder();
+  ModifierKeyComboRecorder(const ModifierKeyComboRecorder&) = delete;
+  ModifierKeyComboRecorder& operator=(const ModifierKeyComboRecorder&) = delete;
+  ~ModifierKeyComboRecorder() override;
+
+  void Initialize();
+
+  // ui::PrerewrittenEventForwarder::Observer:
+  void OnPrerewriteKeyInputEvent(const ui::KeyEvent& event) override;
+
+ private:
+  uint32_t GenerateModifierFlagsFromKeyEvent(const ui::KeyEvent& event);
+  uint32_t GetModifierFlagFromModifier(Modifier modifier);
+
+  void UpdateModifierLocations(const ui::KeyEvent& event);
+
+  // Contains which location (left or right) for each modifier that was last
+  // pressed.
+  std::array<ModifierLocation, static_cast<uint32_t>(Modifier::kMaxValue) + 1>
+      modifier_locations_{ModifierLocation::kLeft};
+  bool initialized_ = false;
+};
+
+}  // namespace ash
+
+#endif
diff --git a/ash/accelerators/modifier_key_combo_recorder_unittest.cc b/ash/accelerators/modifier_key_combo_recorder_unittest.cc
new file mode 100644
index 0000000..8b1716c3c7
--- /dev/null
+++ b/ash/accelerators/modifier_key_combo_recorder_unittest.cc
@@ -0,0 +1,212 @@
+// Copyright 2024 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/accelerators/modifier_key_combo_recorder.h"
+
+#include "ash/public/cpp/accelerators_util.h"
+#include "ash/shell.h"
+#include "ash/test/ash_test_base.h"
+#include "base/test/metrics/histogram_tester.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/events/ash/keyboard_capability.h"
+#include "ui/events/devices/device_data_manager_test_api.h"
+#include "ui/events/devices/input_device.h"
+#include "ui/events/devices/keyboard_device.h"
+#include "ui/events/event.h"
+#include "ui/events/event_constants.h"
+#include "ui/events/keycodes/dom/dom_code.h"
+#include "ui/events/keycodes/keyboard_codes_posix.h"
+#include "ui/events/types/event_type.h"
+
+namespace ash {
+
+namespace {
+
+using ModifierFlag = ModifierKeyComboRecorder::ModifierFlag;
+
+}  // namespace
+
+class ModifierKeyComboRecorderTest : public AshTestBase {
+ public:
+  void SetUp() override {
+    AshTestBase::SetUp();
+    modifier_key_combo_recorder_ = std::make_unique<ModifierKeyComboRecorder>();
+    histogram_tester_ = std::make_unique<base::HistogramTester>();
+  }
+
+  void TearDown() override {
+    modifier_key_combo_recorder_.reset();
+    AshTestBase::TearDown();
+  }
+
+ protected:
+  std::unique_ptr<ModifierKeyComboRecorder> modifier_key_combo_recorder_;
+  std::unique_ptr<base::HistogramTester> histogram_tester_;
+};
+
+TEST_F(ModifierKeyComboRecorderTest, ModifierLocation) {
+  ui::KeyboardDevice keyboard(1, ui::INPUT_DEVICE_INTERNAL, "Keyboard");
+  ui::DeviceDataManagerTestApi().SetKeyboardDevices({keyboard});
+
+  ui::KeyboardCapability* keyboard_capability =
+      Shell::Get()->keyboard_capability();
+  ui::KeyboardCapability::KeyboardInfo keyboard_info;
+  keyboard_info.device_type =
+      ui::KeyboardCapability::DeviceType::kDeviceInternalKeyboard;
+  keyboard_capability->DisableKeyboardInfoTrimmingForTesting();
+  keyboard_capability->SetKeyboardInfoForTesting(keyboard,
+                                                 std::move(keyboard_info));
+
+  ui::KeyEvent control_event(ui::ET_KEY_PRESSED, ui::VKEY_RCONTROL,
+                             ui::DomCode::CONTROL_RIGHT, ui::EF_CONTROL_DOWN);
+  control_event.set_source_device_id(keyboard.id);
+  modifier_key_combo_recorder_->OnPrerewriteKeyInputEvent(control_event);
+  histogram_tester_->ExpectBucketCount(
+      "ChromeOS.Inputs.ModifierKeyCombo.Internal",
+      static_cast<uint32_t>(AcceleratorKeyInputType::kControlRight), 1);
+
+  ui::KeyEvent alpha_event(ui::ET_KEY_PRESSED, ui::VKEY_Z, ui::EF_CONTROL_DOWN);
+  alpha_event.set_source_device_id(keyboard.id);
+  modifier_key_combo_recorder_->OnPrerewriteKeyInputEvent(alpha_event);
+
+  const uint32_t modifier_flag =
+      1 << static_cast<uint32_t>(ModifierFlag::kControlRight);
+  const uint32_t expected_hash =
+      static_cast<uint32_t>(AcceleratorKeyInputType::kAlpha) +
+      (modifier_flag << 16);
+  histogram_tester_->ExpectBucketCount(
+      "ChromeOS.Inputs.ModifierKeyCombo.Internal", expected_hash, 1);
+}
+
+TEST_F(ModifierKeyComboRecorderTest, AltGrModifier) {
+  ui::KeyboardDevice keyboard(1, ui::INPUT_DEVICE_INTERNAL, "Keyboard");
+  ui::DeviceDataManagerTestApi().SetKeyboardDevices({keyboard});
+
+  ui::KeyboardCapability* keyboard_capability =
+      Shell::Get()->keyboard_capability();
+  ui::KeyboardCapability::KeyboardInfo keyboard_info;
+  keyboard_info.device_type =
+      ui::KeyboardCapability::DeviceType::kDeviceInternalKeyboard;
+  keyboard_capability->DisableKeyboardInfoTrimmingForTesting();
+  keyboard_capability->SetKeyboardInfoForTesting(keyboard,
+                                                 std::move(keyboard_info));
+
+  ui::KeyEvent altgr_event(ui::ET_KEY_PRESSED, ui::VKEY_ALTGR,
+                           ui::DomCode::ALT_RIGHT, ui::EF_ALTGR_DOWN);
+  altgr_event.set_source_device_id(keyboard.id);
+  modifier_key_combo_recorder_->OnPrerewriteKeyInputEvent(altgr_event);
+  histogram_tester_->ExpectBucketCount(
+      "ChromeOS.Inputs.ModifierKeyCombo.Internal",
+      static_cast<uint32_t>(AcceleratorKeyInputType::kAltGr), 1);
+
+  ui::KeyEvent alpha_event(ui::ET_KEY_PRESSED, ui::VKEY_Z, ui::EF_ALTGR_DOWN);
+  alpha_event.set_source_device_id(keyboard.id);
+  modifier_key_combo_recorder_->OnPrerewriteKeyInputEvent(alpha_event);
+
+  const uint32_t modifier_flag = 1
+                                 << static_cast<uint32_t>(ModifierFlag::kAltGr);
+  const uint32_t expected_hash =
+      static_cast<uint32_t>(AcceleratorKeyInputType::kAlpha) +
+      (modifier_flag << 16);
+  histogram_tester_->ExpectBucketCount(
+      "ChromeOS.Inputs.ModifierKeyCombo.Internal", expected_hash, 1);
+}
+
+class ModifierKeyComboRecorderParameterizedTest
+    : public ModifierKeyComboRecorderTest,
+      public testing::WithParamInterface<std::tuple<ui::KeyEvent, uint32_t>> {};
+
+INSTANTIATE_TEST_SUITE_P(
+    All,
+    ModifierKeyComboRecorderParameterizedTest,
+    testing::ValuesIn(std::vector<std::tuple<ui::KeyEvent, uint32_t>>{
+        {ui::KeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_NUMPAD0, ui::EF_NONE),
+         static_cast<uint32_t>(AcceleratorKeyInputType::kNumberPad)},
+        {ui::KeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_HOME, ui::EF_NONE),
+         static_cast<uint32_t>(AcceleratorKeyInputType::kSixPack)},
+        {ui::KeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_ESCAPE, ui::EF_NONE),
+         static_cast<uint32_t>(AcceleratorKeyInputType::kEscape)},
+        {ui::KeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_BROWSER_BACK, ui::EF_NONE),
+         static_cast<uint32_t>(AcceleratorKeyInputType::kTopRow)},
+        {ui::KeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_BACK, ui::EF_NONE),
+         static_cast<uint32_t>(AcceleratorKeyInputType::kBackspace)},
+        {ui::KeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_UP, ui::EF_NONE),
+         static_cast<uint32_t>(AcceleratorKeyInputType::kUpArrow)},
+        {ui::KeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_TAB, ui::EF_NONE),
+         static_cast<uint32_t>(AcceleratorKeyInputType::kTab)},
+        {ui::KeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_ALTGR, ui::EF_NONE),
+         static_cast<uint32_t>(AcceleratorKeyInputType::kAltGr)},
+        {ui::KeyEvent(ui::ET_KEY_PRESSED,
+                      ui::VKEY_LWIN,
+                      ui::DomCode::META_LEFT,
+                      ui::EF_NONE),
+         static_cast<uint32_t>(AcceleratorKeyInputType::kMetaLeft)},
+        {ui::KeyEvent(ui::ET_KEY_PRESSED,
+                      ui::VKEY_RWIN,
+                      ui::DomCode::META_RIGHT,
+                      ui::EF_NONE),
+         static_cast<uint32_t>(AcceleratorKeyInputType::kMetaRight)}}));
+
+TEST_P(ModifierKeyComboRecorderParameterizedTest, InternalKeyboard) {
+  auto [key_event, hash] = GetParam();
+
+  ui::KeyboardDevice keyboard(1, ui::INPUT_DEVICE_INTERNAL, "Keyboard");
+  ui::DeviceDataManagerTestApi().SetKeyboardDevices({keyboard});
+
+  ui::KeyboardCapability* keyboard_capability =
+      Shell::Get()->keyboard_capability();
+  ui::KeyboardCapability::KeyboardInfo keyboard_info;
+  keyboard_info.device_type =
+      ui::KeyboardCapability::DeviceType::kDeviceInternalKeyboard;
+  keyboard_capability->DisableKeyboardInfoTrimmingForTesting();
+  keyboard_capability->SetKeyboardInfoForTesting(keyboard,
+                                                 std::move(keyboard_info));
+
+  key_event.set_source_device_id(keyboard.id);
+  modifier_key_combo_recorder_->OnPrerewriteKeyInputEvent(key_event);
+  histogram_tester_->ExpectUniqueSample(
+      "ChromeOS.Inputs.ModifierKeyCombo.Internal", hash, 1);
+}
+
+TEST_P(ModifierKeyComboRecorderParameterizedTest, ExternalKeyboard) {
+  auto [key_event, hash] = GetParam();
+
+  ui::KeyboardDevice keyboard(1, ui::INPUT_DEVICE_INTERNAL, "Keyboard");
+  ui::DeviceDataManagerTestApi().SetKeyboardDevices({keyboard});
+
+  ui::KeyboardCapability* keyboard_capability =
+      Shell::Get()->keyboard_capability();
+  ui::KeyboardCapability::KeyboardInfo keyboard_info;
+  keyboard_info.device_type =
+      ui::KeyboardCapability::DeviceType::kDeviceExternalGenericKeyboard;
+  keyboard_capability->SetKeyboardInfoForTesting(keyboard,
+                                                 std::move(keyboard_info));
+
+  key_event.set_source_device_id(keyboard.id);
+  modifier_key_combo_recorder_->OnPrerewriteKeyInputEvent(key_event);
+  histogram_tester_->ExpectUniqueSample(
+      "ChromeOS.Inputs.ModifierKeyCombo.External", hash, 1);
+}
+
+TEST_P(ModifierKeyComboRecorderParameterizedTest, ExternalChromeOSKeyboard) {
+  auto [key_event, hash] = GetParam();
+
+  ui::KeyboardDevice keyboard(1, ui::INPUT_DEVICE_INTERNAL, "Keyboard");
+  ui::DeviceDataManagerTestApi().SetKeyboardDevices({keyboard});
+
+  ui::KeyboardCapability* keyboard_capability =
+      Shell::Get()->keyboard_capability();
+  ui::KeyboardCapability::KeyboardInfo keyboard_info;
+  keyboard_info.device_type =
+      ui::KeyboardCapability::DeviceType::kDeviceExternalChromeOsKeyboard;
+  keyboard_capability->SetKeyboardInfoForTesting(keyboard,
+                                                 std::move(keyboard_info));
+
+  key_event.set_source_device_id(keyboard.id);
+  modifier_key_combo_recorder_->OnPrerewriteKeyInputEvent(key_event);
+  histogram_tester_->ExpectUniqueSample(
+      "ChromeOS.Inputs.ModifierKeyCombo.CrOSExternal", hash, 1);
+}
+
+}  // namespace ash
diff --git a/ash/events/accessibility_event_rewriter_unittest.cc b/ash/events/accessibility_event_rewriter_unittest.cc
index e5867726..a07b7716 100644
--- a/ash/events/accessibility_event_rewriter_unittest.cc
+++ b/ash/events/accessibility_event_rewriter_unittest.cc
@@ -20,6 +20,7 @@
 #include "ui/aura/window_tree_host.h"
 #include "ui/base/ime/ash/fake_ime_keyboard.h"
 #include "ui/events/ash/event_rewriter_ash.h"
+#include "ui/events/ash/fake_event_rewriter_ash_delegate.h"
 #include "ui/events/ash/keyboard_capability.h"
 #include "ui/events/ash/mojom/extended_fkeys_modifier.mojom-shared.h"
 #include "ui/events/ash/mojom/modifier_key.mojom-shared.h"
@@ -98,15 +99,14 @@
 
 }  // namespace
 
-class ChromeVoxAccessibilityEventRewriterTest
-    : public ash::AshTestBase,
-      public ui::EventRewriterAsh::Delegate {
+class ChromeVoxAccessibilityEventRewriterTest : public ash::AshTestBase {
  public:
   ChromeVoxAccessibilityEventRewriterTest() {
     keyboard_capability_ =
         ui::KeyboardCapability::CreateStubKeyboardCapability();
     event_rewriter_ash_ = std::make_unique<ui::EventRewriterAsh>(
-        this, keyboard_capability_.get(), nullptr, false, &fake_ime_keyboard_);
+        &event_rewriter_delegate_, keyboard_capability_.get(), nullptr, false,
+        &fake_ime_keyboard_);
   }
   ChromeVoxAccessibilityEventRewriterTest(
       const ChromeVoxAccessibilityEventRewriterTest&) = delete;
@@ -160,8 +160,7 @@
 
   void SetModifierRemapping(const std::string& pref_name,
                             ui::mojom::ModifierKey value) {
-    DCHECK_NE(ui::mojom::ModifierKey::kIsoLevel5ShiftMod3, value);
-    modifier_remapping_[pref_name] = value;
+    event_rewriter_delegate_.SetModifierRemapping(pref_name, value);
   }
 
   bool RewriteEventForChromeVox(
@@ -192,74 +191,10 @@
 
   std::unique_ptr<AccessibilityEventRewriter> accessibility_event_rewriter_;
 
+  ui::test::FakeEventRewriterAshDelegate event_rewriter_delegate_;
   input_method::FakeImeKeyboard fake_ime_keyboard_;
   std::unique_ptr<ui::KeyboardCapability> keyboard_capability_;
   std::unique_ptr<ui::EventRewriterAsh> event_rewriter_ash_;
-
- private:
-  // ui::EventRewriterAsh::Delegate:
-  bool RewriteModifierKeys() override { return true; }
-  void SuppressModifierKeyRewrites(bool should_suppress) override {}
-  bool RewriteMetaTopRowKeyComboEvents(int device_id) const override {
-    return true;
-  }
-  void SuppressMetaTopRowKeyComboRewrites(bool should_suppress) override {}
-
-  std::optional<ui::mojom::ModifierKey> GetKeyboardRemappedModifierValue(
-      int device_id,
-      ui::mojom::ModifierKey modifier_key,
-      const std::string& pref_name) const override {
-    auto it = modifier_remapping_.find(pref_name);
-    if (it == modifier_remapping_.end())
-      return std::nullopt;
-
-    return it->second;
-  }
-
-  bool TopRowKeysAreFunctionKeys(int device_id) const override { return false; }
-
-  bool IsExtensionCommandRegistered(ui::KeyboardCode key_code,
-                                    int flags) const override {
-    return false;
-  }
-
-  bool IsSearchKeyAcceleratorReserved() const override { return false; }
-
-  bool NotifyDeprecatedRightClickRewrite() override { return false; }
-  bool NotifyDeprecatedSixPackKeyRewrite(ui::KeyboardCode key_code) override {
-    return false;
-  }
-  void RecordEventRemappedToRightClick(bool alt_based_right_click) override {}
-  void RecordSixPackEventRewrite(ui::KeyboardCode key_code,
-                                 bool alt_based) override {}
-  std::optional<ui::mojom::SimulateRightClickModifier>
-  GetRemapRightClickModifier(int device_id) override {
-    return std::nullopt;
-  }
-
-  std::optional<ui::mojom::SixPackShortcutModifier>
-  GetShortcutModifierForSixPackKey(int device_id,
-                                   ui::KeyboardCode key_code) override {
-    return std::nullopt;
-  }
-
-  void NotifyRightClickRewriteBlockedBySetting(
-      ui::mojom::SimulateRightClickModifier blocked_modifier,
-      ui::mojom::SimulateRightClickModifier active_modifier) override {}
-
-  void NotifySixPackRewriteBlockedBySetting(
-      ui::KeyboardCode key_code,
-      ui::mojom::SixPackShortcutModifier blocked_modifier,
-      ui::mojom::SixPackShortcutModifier active_modifier,
-      int device_id) override {}
-
-  std::optional<ui::mojom::ExtendedFkeysModifier> GetExtendedFkeySetting(
-      int device_id,
-      ui::KeyboardCode key_code) override {
-    return std::nullopt;
-  }
-
-  std::map<std::string, ui::mojom::ModifierKey> modifier_remapping_;
 };
 
 // The delegate should not intercept events when spoken feedback is disabled.
@@ -518,15 +453,14 @@
   std::unique_ptr<ui::KeyEvent> last_key_event_;
 };
 
-class SwitchAccessAccessibilityEventRewriterTest
-    : public AshTestBase,
-      public ui::EventRewriterAsh::Delegate {
+class SwitchAccessAccessibilityEventRewriterTest : public AshTestBase {
  public:
   SwitchAccessAccessibilityEventRewriterTest() {
     keyboard_capability_ =
         ui::KeyboardCapability::CreateStubKeyboardCapability();
     event_rewriter_ash_ = std::make_unique<ui::EventRewriterAsh>(
-        this, keyboard_capability_.get(), nullptr, false, &fake_ime_keyboard_);
+        &event_rewriter_delegate_, keyboard_capability_.get(), nullptr, false,
+        &fake_ime_keyboard_);
   }
   ~SwitchAccessAccessibilityEventRewriterTest() override = default;
 
@@ -579,8 +513,7 @@
 
   void SetModifierRemapping(const std::string& pref_name,
                             ui::mojom::ModifierKey value) {
-    DCHECK_NE(ui::mojom::ModifierKey::kIsoLevel5ShiftMod3, value);
-    modifier_remapping_[pref_name] = value;
+    event_rewriter_delegate_.SetModifierRemapping(pref_name, value);
   }
 
   const std::map<int, std::set<ui::InputDeviceType>> GetKeyCodesToCapture() {
@@ -599,77 +532,12 @@
     return std::map<int, SwitchAccessCommand>();
   }
 
- private:
-  // ui::EventRewriterAsh::Delegate:
-  bool RewriteModifierKeys() override { return true; }
-  void SuppressModifierKeyRewrites(bool should_suppress) override {}
-  bool RewriteMetaTopRowKeyComboEvents(int device_id) const override {
-    return true;
-  }
-  void SuppressMetaTopRowKeyComboRewrites(bool should_suppress) override {}
-
-  std::optional<ui::mojom::ModifierKey> GetKeyboardRemappedModifierValue(
-      int device_id,
-      ui::mojom::ModifierKey modifier_key,
-      const std::string& pref_name) const override {
-    auto it = modifier_remapping_.find(pref_name);
-    if (it == modifier_remapping_.end())
-      return std::nullopt;
-
-    return it->second;
-  }
-
-  bool TopRowKeysAreFunctionKeys(int device_id) const override { return false; }
-
-  bool IsExtensionCommandRegistered(ui::KeyboardCode key_code,
-                                    int flags) const override {
-    return false;
-  }
-
-  bool IsSearchKeyAcceleratorReserved() const override { return false; }
-
-  bool NotifyDeprecatedRightClickRewrite() override { return false; }
-  bool NotifyDeprecatedSixPackKeyRewrite(ui::KeyboardCode key_code) override {
-    return false;
-  }
-
-  void RecordEventRemappedToRightClick(bool alt_based_right_click) override {}
-  void RecordSixPackEventRewrite(ui::KeyboardCode key_code,
-                                 bool alt_based) override {}
-  std::optional<ui::mojom::SimulateRightClickModifier>
-  GetRemapRightClickModifier(int device_id) override {
-    return std::nullopt;
-  }
-
-  std::optional<ui::mojom::SixPackShortcutModifier>
-  GetShortcutModifierForSixPackKey(int device_id,
-                                   ui::KeyboardCode key_code) override {
-    return std::nullopt;
-  }
-
-  void NotifyRightClickRewriteBlockedBySetting(
-      ui::mojom::SimulateRightClickModifier blocked_modifier,
-      ui::mojom::SimulateRightClickModifier active_modifier) override {}
-
-  void NotifySixPackRewriteBlockedBySetting(
-      ui::KeyboardCode key_code,
-      ui::mojom::SixPackShortcutModifier blocked_modifier,
-      ui::mojom::SixPackShortcutModifier active_modifier,
-      int device_id) override {}
-
-  std::optional<ui::mojom::ExtendedFkeysModifier> GetExtendedFkeySetting(
-      int device_id,
-      ui::KeyboardCode key_code) override {
-    return std::nullopt;
-  }
-
-  std::map<std::string, ui::mojom::ModifierKey> modifier_remapping_;
-
  protected:
   raw_ptr<ui::test::EventGenerator> generator_ = nullptr;
   EventCapturer event_capturer_;
   raw_ptr<AccessibilityController> controller_ = nullptr;
   TestAccessibilityEventRewriterDelegate delegate_;
+  ui::test::FakeEventRewriterAshDelegate event_rewriter_delegate_;
   input_method::FakeImeKeyboard fake_ime_keyboard_;
   std::unique_ptr<AccessibilityEventRewriter> accessibility_event_rewriter_;
   std::unique_ptr<ui::KeyboardCapability> keyboard_capability_;
diff --git a/ash/glanceables/common/glanceables_view_id.h b/ash/glanceables/common/glanceables_view_id.h
index 0f25e35d..53d18f5 100644
--- a/ash/glanceables/common/glanceables_view_id.h
+++ b/ash/glanceables/common/glanceables_view_id.h
@@ -35,6 +35,7 @@
 
   // `GlanceablesTasksView` or `TasksBubbleView`.
   kTasksBubbleComboBox,
+  kTasksBubbleListScrollView,
   kTasksBubbleListContainer,
   kTasksBubbleAddNewButton,
   kTasksBubbleListFooter,
diff --git a/ash/glanceables/tasks/glanceables_tasks_view.cc b/ash/glanceables/tasks/glanceables_tasks_view.cc
index abca8d4..c21ec7a 100644
--- a/ash/glanceables/tasks/glanceables_tasks_view.cc
+++ b/ash/glanceables/tasks/glanceables_tasks_view.cc
@@ -94,6 +94,45 @@
 BEGIN_METADATA(AddNewTaskButton)
 END_METADATA
 
+class TaskListScrollView : public views::ScrollView,
+                           public views::ViewObserver {
+  METADATA_HEADER(TaskListScrollView, views::ScrollView)
+ public:
+  TaskListScrollView() : scoped_observation_(this) {
+    SetID(base::to_underlying(GlanceablesViewId::kTasksBubbleListScrollView));
+    ClipHeightTo(0, std::numeric_limits<int>::max());
+    SetBackgroundColor(std::nullopt);
+    SetDrawOverflowIndicator(false);
+  }
+
+  TaskListScrollView(const TaskListScrollView&) = delete;
+  TaskListScrollView& operator=(const TaskListScrollView&) = delete;
+  ~TaskListScrollView() override = default;
+
+  views::View* SetContents(std::unique_ptr<views::View> view) {
+    views::View* contents = views::ScrollView::SetContents(std::move(view));
+    scoped_observation_.Observe(contents);
+    return contents;
+  }
+
+  // views::ViewObserver:
+  void OnViewBoundsChanged(View* observed_view) override {
+    // Updates the preferred size of the scroll view when the content's
+    // preferred size changed.
+    if (contents_old_size_ != observed_view->size()) {
+      contents_old_size_ = observed_view->size();
+      PreferredSizeChanged();
+    }
+  }
+
+ private:
+  gfx::Size contents_old_size_;
+  base::ScopedObservation<views::View, views::ViewObserver> scoped_observation_;
+};
+
+BEGIN_METADATA(TaskListScrollView)
+END_METADATA
+
 }  // namespace
 
 GlanceablesTasksViewBase::GlanceablesTasksViewBase()
@@ -131,10 +170,8 @@
   progress_bar_ = AddChildView(std::make_unique<GlanceablesProgressBarView>());
   progress_bar_->UpdateProgressBarVisibility(/*visible=*/false);
 
-  auto* const scroll_view = AddChildView(std::make_unique<views::ScrollView>());
-  scroll_view->ClipHeightTo(0, std::numeric_limits<int>::max());
-  scroll_view->SetBackgroundColor(std::nullopt);
-  scroll_view->SetDrawOverflowIndicator(false);
+  auto* const scroll_view =
+      AddChildView(std::make_unique<TaskListScrollView>());
 
   auto* const list_view =
       scroll_view->SetContents(std::make_unique<views::View>());
@@ -219,6 +256,10 @@
   }
 }
 
+void GlanceablesTasksView::ChildPreferredSizeChanged(View* child) {
+  PreferredSizeChanged();
+}
+
 void GlanceablesTasksView::CancelUpdates() {
   weak_ptr_factory_.InvalidateWeakPtrs();
 }
diff --git a/ash/glanceables/tasks/glanceables_tasks_view.h b/ash/glanceables/tasks/glanceables_tasks_view.h
index 41e889a..bb9ab420 100644
--- a/ash/glanceables/tasks/glanceables_tasks_view.h
+++ b/ash/glanceables/tasks/glanceables_tasks_view.h
@@ -60,6 +60,9 @@
   GlanceablesTasksView& operator=(const GlanceablesTasksView&) = delete;
   ~GlanceablesTasksView() override;
 
+  // views::View:
+  void ChildPreferredSizeChanged(View* child) override;
+
   // GlanceablesTasksViewBase:
   void CancelUpdates() override;
 
diff --git a/ash/picker/views/picker_search_field_view.cc b/ash/picker/views/picker_search_field_view.cc
index 051d7b6..45a0230 100644
--- a/ash/picker/views/picker_search_field_view.cc
+++ b/ash/picker/views/picker_search_field_view.cc
@@ -5,6 +5,7 @@
 #include "ash/picker/views/picker_search_field_view.h"
 
 #include <string>
+#include <string_view>
 
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/style/typography.h"
@@ -82,7 +83,7 @@
 }
 
 void PickerSearchFieldView::SetPlaceholderText(
-    base::StringPiece16 new_placeholder_text) {
+    std::u16string_view new_placeholder_text) {
   textfield_->SetPlaceholderText(std::u16string(new_placeholder_text));
 }
 
diff --git a/ash/picker/views/picker_search_field_view.h b/ash/picker/views/picker_search_field_view.h
index 479853ba..0b68e399 100644
--- a/ash/picker/views/picker_search_field_view.h
+++ b/ash/picker/views/picker_search_field_view.h
@@ -5,6 +5,8 @@
 #ifndef ASH_PICKER_VIEWS_PICKER_SEARCH_FIELD_VIEW_H_
 #define ASH_PICKER_VIEWS_PICKER_SEARCH_FIELD_VIEW_H_
 
+#include <string_view>
+
 #include "ash/ash_export.h"
 #include "ash/picker/picker_session_metrics.h"
 #include "base/functional/callback_forward.h"
@@ -52,7 +54,7 @@
   void OnDidChangeFocus(View* focused_before, View* focused_now) override;
 
   // Set the placeholder text to show when the textfield is empty.
-  void SetPlaceholderText(base::StringPiece16 new_placeholder_text);
+  void SetPlaceholderText(std::u16string_view new_placeholder_text);
 
   const views::Textfield& textfield_for_testing() const { return *textfield_; }
 
diff --git a/ash/picker/views/picker_user_education_view.cc b/ash/picker/views/picker_user_education_view.cc
index a5e1294..c533c6e 100644
--- a/ash/picker/views/picker_user_education_view.cc
+++ b/ash/picker/views/picker_user_education_view.cc
@@ -5,6 +5,7 @@
 #include "ash/picker/views/picker_user_education_view.h"
 
 #include <string>
+#include <string_view>
 
 #include "ui/base/metadata/metadata_header_macros.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
@@ -34,7 +35,7 @@
   METADATA_HEADER(PickerUserEducationItemView, views::View)
 
  public:
-  explicit PickerUserEducationItemView(const base::StringPiece16 label)
+  explicit PickerUserEducationItemView(const std::u16string_view label)
       : label_(label) {
     SetLayoutManager(std::make_unique<views::FillLayout>());
     AddChildView(std::make_unique<views::Label>(label_));
diff --git a/ash/public/cpp/accelerators_util.cc b/ash/public/cpp/accelerators_util.cc
index 71fb9b2c..321abf1 100644
--- a/ash/public/cpp/accelerators_util.cc
+++ b/ash/public/cpp/accelerators_util.cc
@@ -8,12 +8,14 @@
 #include <string>
 
 #include "ash/public/cpp/accelerator_keycode_lookup_cache.h"
+#include "base/containers/fixed_flat_set.h"
 #include "base/logging.h"
 #include "base/no_destructor.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
 #include "ui/base/ime/ash/input_method_manager.h"
 #include "ui/base/ui_base_features.h"
+#include "ui/events/event.h"
 #include "ui/events/event_constants.h"
 #include "ui/events/keycodes/dom/dom_code.h"
 #include "ui/events/keycodes/dom/dom_codes_array.h"
@@ -126,6 +128,114 @@
              static_cast<int32_t>(dom_code));
 }
 
+bool IsAlphaOrPunctuationKey(ui::KeyboardCode key_code) {
+  if (key_code >= ui::VKEY_A && key_code <= ui::VKEY_Z) {
+    return true;
+  }
+
+  static constexpr auto kPunctuationKeys =
+      base::MakeFixedFlatSet<ui::KeyboardCode>({
+          ui::VKEY_OEM_1,
+          ui::VKEY_OEM_PLUS,
+          ui::VKEY_OEM_COMMA,
+          ui::VKEY_OEM_MINUS,
+          ui::VKEY_OEM_PERIOD,
+          ui::VKEY_OEM_2,
+          ui::VKEY_OEM_3,
+          ui::VKEY_OEM_4,
+          ui::VKEY_OEM_5,
+          ui::VKEY_OEM_6,
+          ui::VKEY_OEM_7,
+          ui::VKEY_OEM_8,
+          ui::VKEY_OEM_102,
+          ui::VKEY_OEM_103,
+          ui::VKEY_OEM_104,
+      });
+  return kPunctuationKeys.contains(key_code);
+}
+
+bool IsDigitKey(ui::KeyboardCode key_code) {
+  return key_code >= ui::VKEY_0 && key_code <= ui::VKEY_9;
+}
+
+bool IsSixPackKey(ui::KeyboardCode key_code) {
+  static constexpr auto kSixPackKeys = base::MakeFixedFlatSet<ui::KeyboardCode>(
+      {ui::VKEY_PRIOR, ui::VKEY_NEXT, ui::VKEY_END, ui::VKEY_HOME,
+       ui::VKEY_INSERT, ui::VKEY_DELETE});
+  return kSixPackKeys.contains(key_code);
+}
+
+bool IsNumpadKey(ui::KeyboardCode key_code) {
+  // Numpad keys are all in consecutive order.
+  return key_code >= ui::VKEY_NUMPAD0 && key_code <= ui::VKEY_DIVIDE;
+}
+
+// This includes only the top row keys we know about, it is possible there are
+// other on external keyboards. They would instead be considered misc keys.
+bool IsTopRowKey(ui::KeyboardCode key_code, ui::DomCode dom_code) {
+  static constexpr auto kTopRowKeys = base::MakeFixedFlatSet<ui::KeyboardCode>({
+      ui::VKEY_F1,
+      ui::VKEY_F2,
+      ui::VKEY_F3,
+      ui::VKEY_F4,
+      ui::VKEY_F5,
+      ui::VKEY_F6,
+      ui::VKEY_F7,
+      ui::VKEY_F8,
+      ui::VKEY_F9,
+      ui::VKEY_F10,
+      ui::VKEY_F11,
+      ui::VKEY_F12,
+      ui::VKEY_F13,
+      ui::VKEY_F14,
+      ui::VKEY_F15,
+      ui::VKEY_F16,
+      ui::VKEY_F17,
+      ui::VKEY_F18,
+      ui::VKEY_F19,
+      ui::VKEY_F20,
+      ui::VKEY_F21,
+      ui::VKEY_F22,
+      ui::VKEY_F23,
+      ui::VKEY_F24,
+      ui::VKEY_BROWSER_BACK,
+      ui::VKEY_BROWSER_FORWARD,
+      ui::VKEY_BROWSER_REFRESH,
+      ui::VKEY_BROWSER_STOP,
+      ui::VKEY_BROWSER_SEARCH,
+      ui::VKEY_BROWSER_FAVORITES,
+      ui::VKEY_BROWSER_HOME,
+      ui::VKEY_VOLUME_MUTE,
+      ui::VKEY_VOLUME_DOWN,
+      ui::VKEY_VOLUME_UP,
+      ui::VKEY_MEDIA_NEXT_TRACK,
+      ui::VKEY_MEDIA_PREV_TRACK,
+      ui::VKEY_MEDIA_STOP,
+      ui::VKEY_MEDIA_PLAY_PAUSE,
+      ui::VKEY_MEDIA_LAUNCH_MAIL,
+      ui::VKEY_MEDIA_LAUNCH_MEDIA_SELECT,
+      ui::VKEY_MEDIA_LAUNCH_APP1,
+      ui::VKEY_MEDIA_LAUNCH_APP2,
+      ui::VKEY_PLAY,
+      ui::VKEY_ZOOM,
+      ui::VKEY_SNAPSHOT,
+      ui::VKEY_PRIVACY_SCREEN_TOGGLE,
+      ui::VKEY_MICROPHONE_MUTE_TOGGLE,
+      ui::VKEY_BRIGHTNESS_DOWN,
+      ui::VKEY_BRIGHTNESS_UP,
+      ui::VKEY_KBD_BACKLIGHT_TOGGLE,
+      ui::VKEY_KBD_BRIGHTNESS_DOWN,
+      ui::VKEY_KBD_BRIGHTNESS_UP,
+      ui::VKEY_SLEEP,
+  });
+
+  if (dom_code == ui::DomCode::SHOW_ALL_WINDOWS) {
+    return true;
+  }
+
+  return kTopRowKeys.contains(key_code);
+}
+
 }  // namespace
 
 absl::optional<ash::KeyCodeLookupEntry> FindKeyCodeEntry(
@@ -257,4 +367,79 @@
   }
 }
 
+AcceleratorKeyInputType GetKeyInputTypeFromKeyEvent(
+    const ui::KeyEvent& key_event) {
+  const ui::KeyboardCode key_code = key_event.key_code();
+  if (IsAlphaOrPunctuationKey(key_code)) {
+    return AcceleratorKeyInputType::kAlpha;
+  }
+
+  if (IsDigitKey(key_code)) {
+    return AcceleratorKeyInputType::kDigit;
+  }
+
+  if (IsTopRowKey(key_code, key_event.code())) {
+    return AcceleratorKeyInputType::kTopRow;
+  }
+
+  if (IsSixPackKey(key_code)) {
+    return AcceleratorKeyInputType::kSixPack;
+  }
+
+  if (IsNumpadKey(key_code)) {
+    return AcceleratorKeyInputType::kNumberPad;
+  }
+
+  switch (key_event.code()) {
+    case ui::DomCode::META_LEFT:
+      return AcceleratorKeyInputType::kMetaLeft;
+    case ui::DomCode::META_RIGHT:
+      return AcceleratorKeyInputType::kMetaRight;
+    case ui::DomCode::CONTROL_LEFT:
+      return AcceleratorKeyInputType::kControlLeft;
+    case ui::DomCode::CONTROL_RIGHT:
+      return AcceleratorKeyInputType::kControlRight;
+    case ui::DomCode::ALT_LEFT:
+      return AcceleratorKeyInputType::kAltLeft;
+    case ui::DomCode::ALT_RIGHT:
+      if (key_event.key_code() == ui::VKEY_ALTGR) {
+        return AcceleratorKeyInputType::kAltGr;
+      }
+      return AcceleratorKeyInputType::kAltRight;
+    case ui::DomCode::SHIFT_LEFT:
+      return AcceleratorKeyInputType::kShiftLeft;
+    case ui::DomCode::SHIFT_RIGHT:
+      return AcceleratorKeyInputType::kShiftRight;
+    default:
+      break;
+  }
+
+  switch (key_code) {
+    case ui::VKEY_ESCAPE:
+      return AcceleratorKeyInputType::kEscape;
+    case ui::VKEY_TAB:
+      return AcceleratorKeyInputType::kTab;
+    case ui::VKEY_CAPITAL:
+      return AcceleratorKeyInputType::kCapsLock;
+    case ui::VKEY_SPACE:
+      return AcceleratorKeyInputType::kSpace;
+    case ui::VKEY_RETURN:
+      return AcceleratorKeyInputType::kEnter;
+    case ui::VKEY_BACK:
+      return AcceleratorKeyInputType::kBackspace;
+    case ui::VKEY_UP:
+      return AcceleratorKeyInputType::kUpArrow;
+    case ui::VKEY_DOWN:
+      return AcceleratorKeyInputType::kDownArrow;
+    case ui::VKEY_RIGHT:
+      return AcceleratorKeyInputType::kRightArrow;
+    case ui::VKEY_LEFT:
+      return AcceleratorKeyInputType::kLeftArrow;
+    default:
+      break;
+  }
+
+  return AcceleratorKeyInputType::kMisc;
+}
+
 }  // namespace ash
diff --git a/ash/public/cpp/accelerators_util.h b/ash/public/cpp/accelerators_util.h
index 2077e59..a08eb86d 100644
--- a/ash/public/cpp/accelerators_util.h
+++ b/ash/public/cpp/accelerators_util.h
@@ -9,11 +9,45 @@
 
 #include "ash/public/cpp/accelerator_keycode_lookup_cache.h"
 #include "ash/public/cpp/ash_public_export.h"
+#include "ui/events/event.h"
 #include "ui/events/keycodes/dom/dom_code.h"
 #include "ui/events/keycodes/keyboard_codes_posix.h"
 
 namespace ash {
 
+// Represents the type of key pressed from a key event.
+// Do not reorder or insert values in the middle as this is used by metrics.
+enum class AcceleratorKeyInputType : uint8_t {
+  kMetaLeft,
+  kMetaRight,
+  kControlLeft,
+  kControlRight,
+  kAltLeft,
+  kAltRight,
+  kShiftLeft,
+  kShiftRight,
+  kAltGr,
+  kAlpha,
+  kDigit,
+  kTopRow,
+  kSixPack,
+  kNumberPad,
+  kLeftArrow,
+  kRightArrow,
+  kUpArrow,
+  kDownArrow,
+  kEscape,
+  kTab,
+  kCapsLock,
+  kSpace,
+  kEnter,
+  kBackspace,
+  // Misc buckets every other key on the keyboard which mostly consists of
+  // non-standard keys.
+  kMisc,
+  kMaxValue = kMisc,
+};
+
 // Returns the string of a DomKey for a given KeyboardCode. A keyboard code
 // needs to be mapped to a physical key, DomCode, and then the DomCode needs
 // to be mapped to a meaning or character of a DomKey based on the
@@ -40,6 +74,11 @@
                  ui::DomCode dom_code = ui::DomCode::NONE,
                  bool remap_positional_key = true);
 
+// Returns the `AcceleratorKeyInputType` that matches for the given key_event.
+ASH_PUBLIC_EXPORT
+AcceleratorKeyInputType GetKeyInputTypeFromKeyEvent(
+    const ui::KeyEvent& key_event);
+
 }  // namespace ash
 
 #endif  // ASH_PUBLIC_CPP_ACCELERATORS_UTIL_H_
diff --git a/ash/shell.cc b/ash/shell.cc
index 274ec3f..7451ba0 100644
--- a/ash/shell.cc
+++ b/ash/shell.cc
@@ -17,6 +17,7 @@
 #include "ash/accelerators/ash_accelerator_configuration.h"
 #include "ash/accelerators/ash_focus_manager_factory.h"
 #include "ash/accelerators/magnifier_key_scroller.h"
+#include "ash/accelerators/modifier_key_combo_recorder.h"
 #include "ash/accelerators/pre_target_accelerator_handler.h"
 #include "ash/accelerators/shortcut_input_handler.h"
 #include "ash/accelerators/spoken_feedback_toggler.h"
@@ -788,6 +789,7 @@
 
   // `shortcut_input_handler_` must be cleaned up before
   // `event_rewriter_controller_`.
+  modifier_key_combo_recorder_.reset();
   shortcut_input_handler_.reset();
   // `AccessibilityEventRewriter` references objects owned by
   // EventRewriterController directly, so it must be reset first to avoid
@@ -1371,6 +1373,10 @@
   keyboard_modifier_metrics_recorder_ =
       std::make_unique<KeyboardModifierMetricsRecorder>();
   event_rewriter_controller_ = std::make_unique<EventRewriterControllerImpl>();
+  if (features::IsPeripheralCustomizationEnabled() ||
+      ::features::IsShortcutCustomizationEnabled()) {
+    modifier_key_combo_recorder_ = std::make_unique<ModifierKeyComboRecorder>();
+  }
 
   env_filter_ = std::make_unique<::wm::CompoundEventFilter>();
   AddPreTargetHandler(env_filter_.get());
diff --git a/ash/shell.h b/ash/shell.h
index 3eb14c5b..c4e4bda7 100644
--- a/ash/shell.h
+++ b/ash/shell.h
@@ -9,6 +9,7 @@
 #include <utility>
 #include <vector>
 
+#include "ash/accelerators/modifier_key_combo_recorder.h"
 #include "ash/accessibility/accessibility_event_handler_manager.h"
 #include "ash/ash_export.h"
 #include "ash/constants/ash_features.h"
@@ -730,6 +731,9 @@
   ShortcutInputHandler* shortcut_input_handler() {
     return shortcut_input_handler_.get();
   }
+  ModifierKeyComboRecorder* modifier_key_combo_recorder() {
+    return modifier_key_combo_recorder_.get();
+  }
   ShutdownControllerImpl* shutdown_controller() {
     return shutdown_controller_.get();
   }
@@ -962,6 +966,7 @@
   std::unique_ptr<EventRewriterControllerImpl> event_rewriter_controller_;
   std::unique_ptr<InputDeviceSettingsControllerImpl>
       input_device_settings_controller_;
+  std::unique_ptr<ModifierKeyComboRecorder> modifier_key_combo_recorder_;
   std::unique_ptr<InputDeviceSettingsDispatcher>
       input_device_settings_dispatcher_;
   std::unique_ptr<InputDeviceTracker> input_device_tracker_;
diff --git a/ash/system/tray/tray_bubble_view.h b/ash/system/tray/tray_bubble_view.h
index 3b69ca4..1aab180 100644
--- a/ash/system/tray/tray_bubble_view.h
+++ b/ash/system/tray/tray_bubble_view.h
@@ -281,6 +281,7 @@
   void SetBubbleBorderInsets(gfx::Insets insets);
 
   views::BoxLayout* box_layout() { return layout_; }
+  const views::BoxLayout* box_layout() const { return layout_; }
 
  private:
   // This reroutes receiving key events to the TrayBubbleView passed in the
diff --git a/ash/system/unified/glanceable_tray_bubble_view.cc b/ash/system/unified/glanceable_tray_bubble_view.cc
index c1d82d2b..f89a6ff8 100644
--- a/ash/system/unified/glanceable_tray_bubble_view.cc
+++ b/ash/system/unified/glanceable_tray_bubble_view.cc
@@ -5,6 +5,7 @@
 #include "ash/system/unified/glanceable_tray_bubble_view.h"
 
 #include <memory>
+#include <numeric>
 
 #include "ash/api/tasks/tasks_client.h"
 #include "ash/api/tasks/tasks_types.h"
@@ -63,8 +64,6 @@
     SetPaintToLayer();
     layer()->SetFillsBoundsOpaquely(false);
     SetOrientation(views::LayoutOrientation::kVertical);
-    SetProperty(views::kMarginsKey,
-                gfx::Insets::TLBR(0, 0, kMarginBetweenGlanceables, 0));
     SetInteriorMargin(gfx::Insets(12));
     SetBackground(views::CreateThemedRoundedRectBackground(
         cros_tokens::kCrosSysSystemBaseElevated,
@@ -204,6 +203,7 @@
   // should be prioritized to be shrunk. Set the default flex to 0 and manually
   // updates the flex of views depending on the view hierarchy.
   box_layout()->SetDefaultFlex(0);
+  box_layout()->set_between_child_spacing(kMarginBetweenGlanceables);
 }
 
 GlanceableTrayBubbleView::~GlanceableTrayBubbleView() {
@@ -321,6 +321,14 @@
   initialized_ = true;
 }
 
+int GlanceableTrayBubbleView::GetHeightForWidth(int width) const {
+  // Let the layout manager calculate the preferred height instead of using the
+  // one from TrayBubbleView, which doesn't take the layout manager and margin
+  // settings into consider.
+  return std::min(views::View::GetHeightForWidth(width),
+                  CalculateMaxTrayBubbleHeight(shelf_->GetWindow()));
+}
+
 void GlanceableTrayBubbleView::AddedToWidget() {
   if (!initialized_) {
     InitializeContents();
@@ -380,9 +388,6 @@
   } else {
     tasks_bubble_view_ = scroll_view_->contents()->AddChildViewAt(
         std::make_unique<TasksBubbleView>(task_lists), 0);
-    tasks_bubble_view_->SetProperty(
-        views::kMarginsKey,
-        gfx::Insets::TLBR(0, 0, kMarginBetweenGlanceables, 0));
     box_layout()->SetFlexForView(scroll_view_, 1);
   }
 
diff --git a/ash/system/unified/glanceable_tray_bubble_view.h b/ash/system/unified/glanceable_tray_bubble_view.h
index d23c0fb..964a9632 100644
--- a/ash/system/unified/glanceable_tray_bubble_view.h
+++ b/ash/system/unified/glanceable_tray_bubble_view.h
@@ -49,6 +49,9 @@
   }
   CalendarView* GetCalendarView() { return calendar_view_; }
 
+  // views::View:
+  int GetHeightForWidth(int w) const override;
+
   // TrayBubbleView:
   void AddedToWidget() override;
   void OnWidgetClosing(views::Widget* widget) override;
diff --git a/ash/webui/diagnostics_ui/backend/BUILD.gn b/ash/webui/diagnostics_ui/backend/BUILD.gn
index 16d837b..4dc4439 100644
--- a/ash/webui/diagnostics_ui/backend/BUILD.gn
+++ b/ash/webui/diagnostics_ui/backend/BUILD.gn
@@ -135,6 +135,7 @@
     "//services/device/public/cpp:test_support",
     "//testing/gtest",
     "//ui/events/ash",
+    "//ui/events/ash:test_support",
     "//ui/events/devices:devices",
     "//ui/events/devices:test_support",
     "//ui/events/ozone",
diff --git a/ash/webui/diagnostics_ui/backend/input/input_data_provider_unittest.cc b/ash/webui/diagnostics_ui/backend/input/input_data_provider_unittest.cc
index d7f84b38..73806e4 100644
--- a/ash/webui/diagnostics_ui/backend/input/input_data_provider_unittest.cc
+++ b/ash/webui/diagnostics_ui/backend/input/input_data_provider_unittest.cc
@@ -54,6 +54,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/events/ash/event_rewriter_ash.h"
+#include "ui/events/ash/fake_event_rewriter_ash_delegate.h"
 #include "ui/events/ash/keyboard_capability.h"
 #include "ui/events/devices/device_data_manager.h"
 #include "ui/events/devices/device_data_manager_test_api.h"
@@ -601,73 +602,6 @@
   }
 };
 
-// Test implementation of ui::EventRewriterAsh::Delegate used to check that
-// modifier key rewrites are suppressed appropriately in InputDataProvider.
-class TestEventRewriterAshDelegate : public ui::EventRewriterAsh::Delegate {
- public:
-  // ui::EventRewriterAsh::Delegate:
-  bool RewriteModifierKeys() override {
-    return !suppress_modifier_key_rewrites_;
-  }
-  void SuppressModifierKeyRewrites(bool should_supress) override {
-    suppress_modifier_key_rewrites_ = should_supress;
-  }
-
-  // Not used, only to satisfy interface.
-  bool RewriteMetaTopRowKeyComboEvents(int device_id) const override {
-    return true;
-  }
-  void SuppressMetaTopRowKeyComboRewrites(bool should_suppress) override {}
-  std::optional<ui::mojom::ModifierKey> GetKeyboardRemappedModifierValue(
-      int device_id,
-      ui::mojom::ModifierKey modifier_key,
-      const std::string& pref_name) const override {
-    return std::nullopt;
-  }
-  bool TopRowKeysAreFunctionKeys(int device_id) const override { return false; }
-  bool IsExtensionCommandRegistered(ui::KeyboardCode key_code,
-                                    int flags) const override {
-    return false;
-  }
-  bool IsSearchKeyAcceleratorReserved() const override { return false; }
-  bool NotifyDeprecatedRightClickRewrite() override { return false; }
-  bool NotifyDeprecatedSixPackKeyRewrite(ui::KeyboardCode key_code) override {
-    return false;
-  }
-  void RecordEventRemappedToRightClick(bool alt_based_right_click) override {}
-  void RecordSixPackEventRewrite(ui::KeyboardCode key_code,
-                                 bool alt_based) override {}
-  std::optional<ui::mojom::SimulateRightClickModifier>
-  GetRemapRightClickModifier(int device_id) override {
-    return std::nullopt;
-  }
-
-  std::optional<ui::mojom::SixPackShortcutModifier>
-  GetShortcutModifierForSixPackKey(int device_id,
-                                   ui::KeyboardCode key_code) override {
-    return std::nullopt;
-  }
-
-  void NotifyRightClickRewriteBlockedBySetting(
-      ui::mojom::SimulateRightClickModifier blocked_modifier,
-      ui::mojom::SimulateRightClickModifier active_modifier) override {}
-
-  void NotifySixPackRewriteBlockedBySetting(
-      ui::KeyboardCode key_code,
-      ui::mojom::SixPackShortcutModifier blocked_modifier,
-      ui::mojom::SixPackShortcutModifier active_modifier,
-      int device_id) override {}
-
-  std::optional<ui::mojom::ExtendedFkeysModifier> GetExtendedFkeySetting(
-      int device_id,
-      ui::KeyboardCode key_code) override {
-    return std::nullopt;
-  }
-
- protected:
-  bool suppress_modifier_key_rewrites_ = false;
-};
-
 // Our modifications to InputDataProvider that carries around its own
 // widget (representing the window that needs to be visible for key events
 // to be observed), the needed factories for our fake utilities, and a
@@ -721,8 +655,6 @@
     AshTestSuite::LoadTestResources();
     AshTestBase::SetUp();
 
-    event_rewriter_delegate_ = std::make_unique<TestEventRewriterAshDelegate>();
-
     // Note: some init for creating widgets is performed in base SetUp
     // instead of the constructor, so our init must also be delayed until
     // SetUp, so we can safely invoke CreateTestWidget().
@@ -734,7 +666,7 @@
     fake_udev_ = std::make_unique<testing::FakeUdevLoader>();
     widget_ = CreateTestWidget();
     provider_ = std::make_unique<TestInputDataProvider>(
-        widget_.get(), watchers_, event_rewriter_delegate_.get());
+        widget_.get(), watchers_, &event_rewriter_delegate_);
     DiagnosticsLogController::Initialize(
         std::make_unique<FakeDiagnosticsBrowserDelegate>());
 
@@ -772,7 +704,7 @@
   }
 
   bool ModifierRewritesAreSuppressed() {
-    return !event_rewriter_delegate_->RewriteModifierKeys();
+    return !event_rewriter_delegate_.RewriteModifierKeys();
   }
 
  protected:
@@ -851,8 +783,8 @@
   std::unique_ptr<views::Widget> widget_;
   // All evdev watchers in use by provider_.
   watchers_t watchers_;
+  ui::test::FakeEventRewriterAshDelegate event_rewriter_delegate_;
   std::unique_ptr<TestInputDataProvider> provider_;
-  std::unique_ptr<TestEventRewriterAshDelegate> event_rewriter_delegate_;
 
  private:
   std::unique_ptr<base::test::ScopedFeatureList> scoped_feature_list_;
@@ -2010,7 +1942,7 @@
   std::unique_ptr<TestInputDataProvider> provider2_ =
       std::make_unique<TestInputDataProvider>(provider2_widget.get(),
                                               provider2_watchers,
-                                              event_rewriter_delegate_.get());
+                                              &event_rewriter_delegate_);
   auto& provider1_ = provider_;
 
   std::unique_ptr<FakeKeyboardObserver> fake_observer1 =
diff --git a/ash/wm/desks/desks_unittests.cc b/ash/wm/desks/desks_unittests.cc
index 3dcc2be6..8c79ea0 100644
--- a/ash/wm/desks/desks_unittests.cc
+++ b/ash/wm/desks/desks_unittests.cc
@@ -133,6 +133,7 @@
 #include "ui/display/screen.h"
 #include "ui/display/test/display_manager_test_api.h"
 #include "ui/events/ash/event_rewriter_ash.h"
+#include "ui/events/ash/fake_event_rewriter_ash_delegate.h"
 #include "ui/events/devices/device_data_manager.h"
 #include "ui/events/devices/device_data_manager_test_api.h"
 #include "ui/events/devices/input_device.h"
@@ -5341,8 +5342,7 @@
 }  // namespace
 
 // Simulates the same behavior of event rewriting that key presses go through.
-class DesksAcceleratorsTest : public DesksTest,
-                              public ui::EventRewriterAsh::Delegate {
+class DesksAcceleratorsTest : public DesksTest {
  public:
   DesksAcceleratorsTest() = default;
 
@@ -5357,64 +5357,11 @@
 
     auto* event_rewriter_controller = EventRewriterController::Get();
     auto event_rewriter = std::make_unique<ui::EventRewriterAsh>(
-        this, Shell::Get()->keyboard_capability(),
+        &event_rewriter_delegate_, Shell::Get()->keyboard_capability(),
         Shell::Get()->sticky_keys_controller(), false, &fake_ime_keyboard_);
     event_rewriter_controller->AddEventRewriter(std::move(event_rewriter));
   }
 
-  // ui::EventRewriterAsh::Delegate:
-  bool RewriteModifierKeys() override { return true; }
-  void SuppressModifierKeyRewrites(bool should_supress) override {}
-  bool RewriteMetaTopRowKeyComboEvents(int device_id) const override {
-    return true;
-  }
-  void SuppressMetaTopRowKeyComboRewrites(bool should_suppress) override {}
-  std::optional<ui::mojom::ModifierKey> GetKeyboardRemappedModifierValue(
-      int device_id,
-      ui::mojom::ModifierKey modifier_key,
-      const std::string& pref_name) const override {
-    return std::nullopt;
-  }
-  bool TopRowKeysAreFunctionKeys(int device_id) const override { return false; }
-  bool IsExtensionCommandRegistered(ui::KeyboardCode key_code,
-                                    int flags) const override {
-    return false;
-  }
-  bool IsSearchKeyAcceleratorReserved() const override { return true; }
-  bool NotifyDeprecatedRightClickRewrite() override { return false; }
-  bool NotifyDeprecatedSixPackKeyRewrite(ui::KeyboardCode key_code) override {
-    return false;
-  }
-  void RecordEventRemappedToRightClick(bool alt_based_right_click) override {}
-  void RecordSixPackEventRewrite(ui::KeyboardCode key_code,
-                                 bool alt_based) override {}
-  std::optional<ui::mojom::SimulateRightClickModifier>
-  GetRemapRightClickModifier(int device_id) override {
-    return std::nullopt;
-  }
-
-  std::optional<ui::mojom::SixPackShortcutModifier>
-  GetShortcutModifierForSixPackKey(int device_id,
-                                   ui::KeyboardCode key_code) override {
-    return std::nullopt;
-  }
-
-  void NotifyRightClickRewriteBlockedBySetting(
-      ui::mojom::SimulateRightClickModifier blocked_modifier,
-      ui::mojom::SimulateRightClickModifier active_modifier) override {}
-
-  void NotifySixPackRewriteBlockedBySetting(
-      ui::KeyboardCode key_code,
-      ui::mojom::SixPackShortcutModifier blocked_modifier,
-      ui::mojom::SixPackShortcutModifier active_modifier,
-      int device_id) override {}
-
-  std::optional<ui::mojom::ExtendedFkeysModifier> GetExtendedFkeySetting(
-      int device_id,
-      ui::KeyboardCode key_code) override {
-    return std::nullopt;
-  }
-
   void SendAccelerator(ui::KeyboardCode key_code, int flags) {
     ui::test::EventGenerator* generator = GetEventGenerator();
     generator->PressKey(key_code, flags);
@@ -5427,6 +5374,7 @@
   }
 
  private:
+  ui::test::FakeEventRewriterAshDelegate event_rewriter_delegate_;
   input_method::FakeImeKeyboard fake_ime_keyboard_;
 };
 
diff --git a/base/BUILD.gn b/base/BUILD.gn
index c761254d..c66d803 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -2345,7 +2345,6 @@
       "trace_event/trace_config.h",
       "trace_event/trace_config_category_filter.cc",
       "trace_event/trace_config_category_filter.h",
-      "trace_event/trace_conversion_helper.h",
       "trace_event/trace_event.h",
       "trace_event/trace_event_impl.cc",
       "trace_event/trace_event_impl.h",
@@ -4081,7 +4080,6 @@
       "trace_event/trace_arguments_unittest.cc",
       "trace_event/trace_category_unittest.cc",
       "trace_event/trace_config_unittest.cc",
-      "trace_event/trace_conversion_helper_unittest.cc",
       "trace_event/trace_event_unittest.cc",
       "trace_event/traced_value_support_unittest.cc",
       "trace_event/traced_value_unittest.cc",
diff --git a/base/trace_event/trace_conversion_helper.h b/base/trace_event/trace_conversion_helper.h
deleted file mode 100644
index da31403a..0000000
--- a/base/trace_event/trace_conversion_helper.h
+++ /dev/null
@@ -1,234 +0,0 @@
-// Copyright 2020 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef BASE_TRACE_EVENT_TRACE_CONVERSION_HELPER_H_
-#define BASE_TRACE_EVENT_TRACE_CONVERSION_HELPER_H_
-
-#include <sstream>
-#include <string>
-
-#include "base/strings/string_number_conversions.h"
-#include "base/template_util.h"
-#include "base/trace_event/traced_value.h"
-
-namespace base {
-
-namespace trace_event {
-
-// Helpers for base::trace_event::ValueToString.
-namespace internal {
-
-// Return std::string representation given by |value|'s ostream operator<<.
-template <typename ValueType>
-std::string OstreamValueToString(const ValueType& value) {
-  std::stringstream ss;
-  ss << value;
-  return ss.str();
-}
-
-// Use SFINAE to decide how to extract a string from the given parameter.
-
-// Check if |value| can be used as a parameter of |base::NumberToString|. If
-// std::string is not constructible from the returned value of
-// |base::NumberToString| cause compilation error.
-//
-// |base::NumberToString| does not do locale specific formatting and should be
-// faster than using |std::ostream::operator<<|.
-template <typename ValueType>
-decltype(base::NumberToString(std::declval<const ValueType>()), std::string())
-ValueToStringHelper(base::internal::priority_tag<5>,
-                    const ValueType& value,
-                    std::string /* unused */) {
-  return base::NumberToString(value);
-}
-
-// If there is |ValueType::ToString| whose return value can be used to construct
-// |std::string|, use this. Else use other methods.
-template <typename ValueType>
-decltype(std::string(std::declval<const ValueType>().ToString()))
-ValueToStringHelper(base::internal::priority_tag<4>,
-                    const ValueType& value,
-                    std::string /* unused */) {
-  return value.ToString();
-}
-
-// If |std::ostream::operator<<| can be used, use it. Useful for |void*|.
-template <typename ValueType>
-decltype(
-    std::declval<std::ostream>().operator<<(std::declval<const ValueType>()),
-    std::string())
-ValueToStringHelper(base::internal::priority_tag<3>,
-                    const ValueType& value,
-                    std::string /* unused */) {
-  return OstreamValueToString(value);
-}
-
-// Use |ValueType::operator<<| if applicable.
-template <typename ValueType>
-decltype(operator<<(std::declval<std::ostream&>(),
-                    std::declval<const ValueType&>()),
-         std::string())
-ValueToStringHelper(base::internal::priority_tag<2>,
-                    const ValueType& value,
-                    std::string /* unused */) {
-  return OstreamValueToString(value);
-}
-
-// If there is |ValueType::data| whose return value can be used to construct
-// |std::string|, use it.
-template <typename ValueType>
-decltype(std::string(std::declval<const ValueType>().data()))
-ValueToStringHelper(base::internal::priority_tag<1>,
-                    const ValueType& value,
-                    std::string /* unused */) {
-  return value.data();
-}
-
-// Fallback returns the |fallback_value|. Needs to have |ValueToStringPriority|
-// with the highest number (to be called last).
-template <typename ValueType>
-std::string ValueToStringHelper(base::internal::priority_tag<0>,
-                                const ValueType& /* unused */,
-                                std::string fallback_value) {
-  return fallback_value;
-}
-
-/*********************************************
-********* SetTracedValueArg methods. *********
-*********************************************/
-
-// base::internal::priority_tag parameter is there to define ordering in which
-// the following methods will be considered. Note that for instance |bool| type
-// is also |std::is_integral|, so we need to test |bool| before testing for
-// integral.
-template <typename T>
-std::enable_if_t<std::is_same_v<T, bool>> SetTracedValueArgHelper(
-    base::internal::priority_tag<6>,
-    TracedValue* traced_value,
-    const char* name,
-    const T& value) {
-  traced_value->SetBoolean(name, value);
-}
-
-// std::is_integral_v<bool> == true
-// This needs to be considered only when T is not bool (has higher
-// base::internal::priority_tag).
-template <typename T>
-std::enable_if_t<std::is_integral_v<T>> SetTracedValueArgHelper(
-    base::internal::priority_tag<5>,
-    TracedValue* traced_value,
-    const char* name,
-    const T& value) {
-  // Avoid loss of precision.
-  if (sizeof(int) < sizeof(value)) {
-    // TODO(crbug.com/1111787): Add 64-bit support to TracedValue.
-    traced_value->SetString(name, base::NumberToString(value));
-  } else {
-    traced_value->SetInteger(name, value);
-  }
-}
-
-// Any floating point type is converted to double.
-template <typename T>
-std::enable_if_t<std::is_floating_point_v<T>> SetTracedValueArgHelper(
-    base::internal::priority_tag<4>,
-    TracedValue* traced_value,
-    const char* name,
-    const T& value) {
-  traced_value->SetDouble(name, static_cast<double>(value));
-}
-
-// |void*| is traced natively.
-template <typename T>
-std::enable_if_t<std::is_same_v<T, void*>> SetTracedValueArgHelper(
-    base::internal::priority_tag<3>,
-    TracedValue* traced_value,
-    const char* name,
-    const T& value) {
-  traced_value->SetPointer(name, value);
-}
-
-// |const char*| is traced natively.
-template <typename T>
-std::enable_if_t<std::is_same_v<T, const char*>> SetTracedValueArgHelper(
-    base::internal::priority_tag<2>,
-    TracedValue* traced_value,
-    const char* name,
-    const T& value) {
-  traced_value->SetString(name, value);
-}
-
-// If an instance of |base::StringPiece| can be constructed from an instance of
-// |T| trace |value| as a string.
-template <typename T>
-decltype(base::StringPiece(std::declval<const T>()), void())
-SetTracedValueArgHelper(base::internal::priority_tag<1>,
-                        TracedValue* traced_value,
-                        const char* name,
-                        const T& value) {
-  traced_value->SetString(name, value);
-}
-
-// Fallback.
-template <typename T>
-void SetTracedValueArgHelper(base::internal::priority_tag<0>,
-                             TracedValue* traced_value,
-                             const char* name,
-                             const T& /* unused */) {
-  // TODO(crbug.com/1111787): Add fallback to |ValueToString|. Crashes on
-  // operator<< have been seen with it.
-  traced_value->SetString(name, "<value>");
-}
-
-}  // namespace internal
-
-// The function to be used.
-template <typename ValueType>
-std::string ValueToString(const ValueType& value,
-                          std::string fallback_value = "<value>") {
-  return internal::ValueToStringHelper(base::internal::priority_tag<5>(), value,
-                                       std::move(fallback_value));
-}
-
-// Method to trace |value| into the given |traced_value|. Support types where
-// there is |TracedValue::SetT| natively.
-//
-// TODO(crbug.com/1111787): Add support for:
-//   absl::optional
-//   AsValueInto (T& and T*)
-//   array and map types
-//   fallback to ValueToString
-template <typename ValueType>
-void SetTracedValueArg(TracedValue* traced_value,
-                       const char* name,
-                       const ValueType& value) {
-  internal::SetTracedValueArgHelper(base::internal::priority_tag<6>(),
-                                    traced_value, name, value);
-}
-
-// Parameter pack support: do nothing for an empty parameter pack.
-//
-// Inline this to avoid linker duplicate symbol error.
-inline void SetTracedValueArg(TracedValue* traced_value, const char* name) {}
-
-// Parameter pack support. All of the packed parameters are traced under the
-// same name. Serves to trace a parameter pack, all parameters having the same
-// name (of the parameter pack) is desired.
-//
-// Example use when |args| is a parameter pack:
-//   SetTracedValueArg(traced_value, name, args...);
-template <typename ValueType, typename... ValueTypes>
-void SetTracedValueArg(TracedValue* traced_value,
-                       const char* name,
-                       const ValueType& value,
-                       const ValueTypes&... args) {
-  SetTracedValueArg(traced_value, name, value);
-  // Trace the rest from the parameter pack.
-  SetTracedValueArg(traced_value, name, args...);
-}
-
-}  // namespace trace_event
-}  // namespace base
-
-#endif  // BASE_TRACE_EVENT_TRACE_CONVERSION_HELPER_H_
diff --git a/base/trace_event/trace_conversion_helper_unittest.cc b/base/trace_event/trace_conversion_helper_unittest.cc
deleted file mode 100644
index b0a9d7a5..0000000
--- a/base/trace_event/trace_conversion_helper_unittest.cc
+++ /dev/null
@@ -1,209 +0,0 @@
-// Copyright 2020 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/trace_event/trace_conversion_helper.h"
-
-#include <stddef.h>
-
-#include <utility>
-
-#include "base/strings/string_util.h"
-#include "base/values.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace base {
-namespace trace_event {
-
-TEST(TraceEventArgumentTest, SetTracedValueArgParameterPack) {
-  std::unique_ptr<TracedValue> value(new TracedValue());
-  TracedValue* raw_value = value.get();
-  SetTracedValueArg(raw_value, "not_traced");
-  SetTracedValueArg(raw_value, "single_value", 42);
-  SetTracedValueArg(raw_value, "pack", 42, false, 0.0, "hello");
-  std::string json;
-  value->AppendAsTraceFormat(&json);
-  EXPECT_EQ(
-      "{"
-      "\"single_value\":42,"
-      "\"pack\":42,"
-      "\"pack\":false,"
-      "\"pack\":0.0,"
-      "\"pack\":\"hello\""
-      "}",
-      json);
-}
-
-TEST(TraceEventArgumentTest, SetTracedValueArgCompatibleTypes) {
-  std::unique_ptr<TracedValue> value(new TracedValue());
-  TracedValue* raw_value = value.get();
-  SetTracedValueArg(raw_value, "float_literal", 0.0f);
-  float my_float = 0.0f;
-  SetTracedValueArg(raw_value, "my_float", my_float);
-  char my_char = 13;
-  SetTracedValueArg(raw_value, "my_char", my_char);
-  std::string json;
-  value->AppendAsTraceFormat(&json);
-  EXPECT_EQ(
-      "{"
-      "\"float_literal\":0.0,"
-      "\"my_float\":0.0,"
-      "\"my_char\":13"
-      "}",
-      json);
-}
-
-class UseFallback {};
-
-TEST(TraceEventArgumentTest, SetTracedValueArgBasicTypes) {
-  std::unique_ptr<TracedValue> value(new TracedValue());
-  TracedValue* raw_value = value.get();
-  SetTracedValueArg(raw_value, "my_int", 1);
-  SetTracedValueArg(raw_value, "my_double", 0.1);
-  SetTracedValueArg(raw_value, "my_bool", false);
-  SetTracedValueArg(raw_value, "my_literal", "hello");
-  SetTracedValueArg(raw_value, "my_string",
-                    std::string("wonderful_") + std::string("world"));
-  SetTracedValueArg(raw_value, "my_void_ptr", static_cast<void*>(nullptr));
-  SetTracedValueArg(raw_value, "use_fallback", UseFallback());
-  UseFallback usefallback_with_variable;
-  SetTracedValueArg(raw_value, "use_fallback", usefallback_with_variable);
-  std::string json;
-  value->AppendAsTraceFormat(&json);
-  EXPECT_EQ(
-      "{\"my_int\":1,"
-      "\"my_double\":0.1,"
-      "\"my_bool\":false,"
-      "\"my_literal\":\"hello\","
-      "\"my_string\":\"wonderful_world\","
-      "\"my_void_ptr\":\"0x0\","
-      "\"use_fallback\":\"\\u003Cvalue>\","
-      "\"use_fallback\":\"\\u003Cvalue>\""
-      "}",
-      json);
-}
-
-TEST(TraceEventConversionHelperTest, OstreamValueToString) {
-  std::string zero = internal::OstreamValueToString(0);
-  EXPECT_EQ("0", zero);
-}
-
-TEST(TraceEventConversionHelperTest, UseFallback) {
-  std::string answer = ValueToString(UseFallback(), "fallback");
-  EXPECT_EQ("fallback", answer);
-}
-
-// std::ostream::operator<<
-TEST(TraceEventConversionHelperTest, StdOstream) {
-  const char* literal = "hello literal";
-  EXPECT_EQ(literal, ValueToString(literal));
-  std::string str = "hello std::string";
-  EXPECT_EQ(str, ValueToString(str));
-  EXPECT_EQ("1", ValueToString(true));
-}
-
-// base::NumberToString
-TEST(TraceEventConversionHelperTest, Number) {
-  EXPECT_EQ("3.14159", ValueToString(3.14159));
-  EXPECT_EQ("0", ValueToString(0.f));
-  EXPECT_EQ("42", ValueToString(42));
-}
-
-class UseToString {
- public:
-  std::string ToString() const { return "UseToString::ToString"; }
-};
-
-TEST(TraceEventConversionHelperTest, UseToString) {
-  std::string answer = ValueToString(UseToString());
-  EXPECT_EQ("UseToString::ToString", answer);
-}
-
-class UseFallbackNonConstTostring {
- public:
-  std::string ToString() { return "don't return me, not const"; }
-};
-
-TEST(TraceEventConversionHelperTest, UseFallbackNonConstToString) {
-  std::string answer = ValueToString(UseFallbackNonConstTostring(), "fallback");
-  EXPECT_EQ("fallback", answer);
-}
-
-class ConfusingToStringAPI {
- public:
-  ConfusingToStringAPI ToString() const { return ConfusingToStringAPI(); }
-};
-
-TEST(TraceEventConversionHelperTest, ConfusingToStringAPI) {
-  std::string answer = ValueToString(ConfusingToStringAPI(), "fallback");
-  EXPECT_EQ("fallback", answer);
-}
-
-// std::ostream::operator<<
-TEST(TraceEventConversionHelperTest, UseOstreamOperator) {
-  // Test that the output is the same as when calling OstreamValueToString.
-  // Different platforms may represent the pointer differently, thus we don't
-  // compare with a value.
-  EXPECT_EQ(internal::OstreamValueToString((void*)0x123),
-            ValueToString((void*)0x123));
-}
-
-class UseOperatorLessLess {};
-
-std::ostream& operator<<(std::ostream& os, const UseOperatorLessLess&) {
-  os << "UseOperatorLessLess";
-  return os;
-}
-
-TEST(TraceEventConversionHelperTest, UseOperatorLessLess) {
-  std::string answer = ValueToString(UseOperatorLessLess());
-  EXPECT_EQ("UseOperatorLessLess", answer);
-}
-
-class HasBoth {
- public:
-  std::string ToString() const { return "HasBoth::ToString"; }
-};
-
-std::ostream& operator<<(std::ostream& os, const HasBoth&) {
-  os << "HasBoth::OperatorLessLess";
-  return os;
-}
-
-TEST(TraceEventConversionHelperTest, HasBoth) {
-  std::string answer = ValueToString(HasBoth());
-  EXPECT_EQ("HasBoth::ToString", answer);
-}
-
-class HasData {
- public:
-  const char* data() const { return "HasData"; }
-};
-
-TEST(TraceEventConversionHelperTest, HasData) {
-  std::string answer = ValueToString(HasData());
-  EXPECT_EQ("HasData", answer);
-}
-
-class HasNonConstData {
- public:
-  const char* data() { return "HasNonConstData"; }
-};
-
-TEST(TraceEventConversionHelperTest, HasNonConstData) {
-  std::string answer = ValueToString(HasNonConstData(), "fallback");
-  EXPECT_EQ("fallback", answer);
-}
-
-class HasDataOfWrongType {
- public:
-  void data() {}
-};
-
-TEST(TraceEventConversionHelperTest, HasDataOfWrongType) {
-  std::string answer = ValueToString(HasDataOfWrongType(), "fallback");
-  EXPECT_EQ("fallback", answer);
-}
-
-}  // namespace trace_event
-}  // namespace base
diff --git a/base/trace_event/traced_value.cc b/base/trace_event/traced_value.cc
index db45b5d..2f3722d 100644
--- a/base/trace_event/traced_value.cc
+++ b/base/trace_event/traced_value.cc
@@ -547,20 +547,20 @@
 
 // TODO(altimin): Add native support for pointers for nested values in
 // DebugAnnotation proto.
-std::string PointerToString(void* value) {
+std::string PointerToString(const void* value) {
   return base::StringPrintf(
       "0x%" PRIx64, static_cast<uint64_t>(reinterpret_cast<uintptr_t>(value)));
 }
 
 }  // namespace
 
-void TracedValue::SetPointer(const char* name, void* value) {
+void TracedValue::SetPointer(const char* name, const void* value) {
   DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
   writer_->SetString(name, PointerToString(value));
 }
 
 void TracedValue::SetPointerWithCopiedName(base::StringPiece name,
-                                           void* value) {
+                                           const void* value) {
   DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
   writer_->SetStringWithCopiedName(name, PointerToString(value));
 }
@@ -609,7 +609,7 @@
   writer_->AppendString(value);
 }
 
-void TracedValue::AppendPointer(void* value) {
+void TracedValue::AppendPointer(const void* value) {
   DCHECK_CURRENT_CONTAINER_IS(kStackTypeArray);
   writer_->AppendString(PointerToString(value));
 }
diff --git a/base/trace_event/traced_value.h b/base/trace_event/traced_value.h
index da856cb..0178d262 100644
--- a/base/trace_event/traced_value.h
+++ b/base/trace_event/traced_value.h
@@ -43,7 +43,7 @@
   void SetBoolean(const char* name, bool value);
   void SetString(const char* name, base::StringPiece value);
   void SetValue(const char* name, TracedValue* value);
-  void SetPointer(const char* name, void* value);
+  void SetPointer(const char* name, const void* value);
   void BeginDictionary(const char* name);
   void BeginArray(const char* name);
 
@@ -53,7 +53,7 @@
   void SetBooleanWithCopiedName(base::StringPiece name, bool value);
   void SetStringWithCopiedName(base::StringPiece name, base::StringPiece value);
   void SetValueWithCopiedName(base::StringPiece name, TracedValue* value);
-  void SetPointerWithCopiedName(base::StringPiece name, void* value);
+  void SetPointerWithCopiedName(base::StringPiece name, const void* value);
   void BeginDictionaryWithCopiedName(base::StringPiece name);
   void BeginArrayWithCopiedName(base::StringPiece name);
 
@@ -61,7 +61,7 @@
   void AppendDouble(double);
   void AppendBoolean(bool);
   void AppendString(base::StringPiece);
-  void AppendPointer(void*);
+  void AppendPointer(const void*);
   void BeginArray();
   void BeginDictionary();
 
diff --git a/chrome/VERSION b/chrome/VERSION
index efc3133..642dfa2 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=122
 MINOR=0
-BUILD=6260
+BUILD=6261
 PATCH=0
diff --git a/chrome/app/os_settings_strings.grdp b/chrome/app/os_settings_strings.grdp
index d79b8e7..4ad2dc4c 100644
--- a/chrome/app/os_settings_strings.grdp
+++ b/chrome/app/os_settings_strings.grdp
@@ -6417,8 +6417,11 @@
   <message name="IDS_OS_SETTINGS_PRIVACY_HUB_NO_APP_CAN_USE_LOCATION_TEXT" translateable="false" desc="The text displayed in the Apps section of the location subpage when location is not allowed.">
     No app is allowed to use your location
   </message>
-  <message name="IDS_OS_SETTINGS_PRIVACY_HUB_BLOCKED_FOR_ALL_TEXT" translateable="false" desc="The text displayed as a sublabel in privacy hub page and in sensor subpages when sensor access is disabled.">
-    Blocked for all
+  <message name="IDS_OS_SETTINGS_PRIVACY_HUB_CAMERA_ACCESS_BLOCKED_TEXT" desc="The text displayed as a sublabel in the privacy hub page and in the camera subpage when camera access is disabled.">
+    Camera access is blocked
+  </message>
+  <message name="IDS_OS_SETTINGS_PRIVACY_HUB_MICROPHONE_ACCESS_BLOCKED_TEXT" desc="The text displayed as a sublabel in the privacy hub page and in the microphone subpage when microphone access is disabled.">
+    Microphone access is blocked
   </message>
   <message name="IDS_OS_SETTINGS_REVAMP_SECURE_DNS" desc="Text for secure DNS toggle in Privacy options for ChromeOS">
     Encrypt URLs entered into the browser
diff --git a/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_PRIVACY_HUB_CAMERA_ACCESS_BLOCKED_TEXT.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_PRIVACY_HUB_CAMERA_ACCESS_BLOCKED_TEXT.png.sha1
new file mode 100644
index 0000000..1f68d99
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_PRIVACY_HUB_CAMERA_ACCESS_BLOCKED_TEXT.png.sha1
@@ -0,0 +1 @@
+ae2ce8aa5f30dfba37c6b3384956a5a99bbf430d
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_PRIVACY_HUB_MICROPHONE_ACCESS_BLOCKED_TEXT.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_PRIVACY_HUB_MICROPHONE_ACCESS_BLOCKED_TEXT.png.sha1
new file mode 100644
index 0000000..8a551c1
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_PRIVACY_HUB_MICROPHONE_ACCESS_BLOCKED_TEXT.png.sha1
@@ -0,0 +1 @@
+be87c45aeff4d6568fb9532a8c18882479f28c44
\ No newline at end of file
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 45c9182..a18efc3 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -64,6 +64,7 @@
 #include "chrome/browser/ui/ui_features.h"
 #include "chrome/browser/unexpire_flags.h"
 #include "chrome/browser/unexpire_flags_gen.h"
+#include "chrome/browser/web_applications/features.h"
 #include "chrome/browser/webauthn/webauthn_switches.h"
 #include "chrome/common/buildflags.h"
 #include "chrome/common/channel_info.h"
@@ -4922,6 +4923,16 @@
      flag_descriptions::kWebAppManifestImmediateUpdatingName,
      flag_descriptions::kWebAppManifestImmediateUpdatingDescription, kOsDesktop,
      FEATURE_VALUE_TYPE(features::kWebAppManifestImmediateUpdating)},
+    {"web-app-separate-user-display-mode-for-cros",
+     flag_descriptions::kWebAppSeparateUserDisplayModeForCrosName,
+     flag_descriptions::kWebAppSeparateUserDisplayModeForCrosDescription,
+     kOsDesktop, FEATURE_VALUE_TYPE(web_app::kSeparateUserDisplayModeForCrOS)},
+    {"web-app-sync-only-separate-user-display-mode-for-cros",
+     flag_descriptions::kWebAppSyncOnlySeparateUserDisplayModeForCrosName,
+     flag_descriptions::
+         kWebAppSyncOnlySeparateUserDisplayModeForCrosDescription,
+     kOsDesktop,
+     FEATURE_VALUE_TYPE(web_app::kSyncOnlySeparateUserDisplayModeForCrOS)},
     {"web-app-sync-generated-icon-background-fix",
      flag_descriptions::kWebAppSyncGeneratedIconBackgroundFixName,
      flag_descriptions::kWebAppSyncGeneratedIconBackgroundFixDescription,
@@ -4938,6 +4949,18 @@
      kOsDesktop,
      FEATURE_VALUE_TYPE(features::kWebAppSyncGeneratedIconUpdateFix)},
 #endif  // !BUILDFLAG(IS_ANDROID)
+#if BUILDFLAG(IS_CHROMEOS)
+    {"web-app-user-display-mode-sync-browser-mitigation",
+     flag_descriptions::kUserDisplayModeSyncBrowserMitigationName,
+     flag_descriptions::kUserDisplayModeSyncBrowserMitigationDescription,
+     kOsCrOS,
+     FEATURE_VALUE_TYPE(web_app::kUserDisplayModeSyncBrowserMitigation)},
+    {"web-app-user-display-mode-sync-standalone-mitigation",
+     flag_descriptions::kUserDisplayModeSyncStandaloneMitigationName,
+     flag_descriptions::kUserDisplayModeSyncStandaloneMitigationDescription,
+     kOsCrOS,
+     FEATURE_VALUE_TYPE(web_app::kUserDisplayModeSyncStandaloneMitigation)},
+#endif  // BUILDFLAG(IS_CHROMEOS)
     {"use-sync-sandbox", flag_descriptions::kSyncSandboxName,
      flag_descriptions::kSyncSandboxDescription, kOsAll,
      SINGLE_VALUE_TYPE_AND_VALUE(
@@ -11112,6 +11135,12 @@
      FEATURE_VALUE_TYPE(blink::features::kLockedMode)},
 #endif  // BUILDFLAG_(IS_CHROMEOS)
 
+#if defined(USE_AURA)
+    {"link-preview", flag_descriptions::kLinkPreviewName,
+     flag_descriptions::kLinkPreviewDescription, kOsAura,
+     FEATURE_VALUE_TYPE(blink::features::kLinkPreview)},
+#endif  // USE_AURA
+
 #if BUILDFLAG(IS_ANDROID)
     {"seed-accounts-revamp", flag_descriptions::kSeedAccountsRevampName,
      flag_descriptions::kSeedAccountsRevampDescription, kOsAndroid,
diff --git a/chrome/browser/apps/app_service/app_service_proxy_ash.cc b/chrome/browser/apps/app_service/app_service_proxy_ash.cc
index 95d701f..4c04627 100644
--- a/chrome/browser/apps/app_service/app_service_proxy_ash.cc
+++ b/chrome/browser/apps/app_service/app_service_proxy_ash.cc
@@ -976,6 +976,15 @@
   }
 }
 
+bool AppServiceProxyAsh::ShouldExcludeBrowserTabApps(
+    bool exclude_browser_tab_apps,
+    WindowMode window_mode) {
+  if (!chromeos::features::IsCrosShortstandEnabled()) {
+    return (exclude_browser_tab_apps && window_mode == WindowMode::kBrowser);
+  }
+  return false;
+}
+
 void AppServiceProxyAsh::LoadIconForDialog(const apps::AppUpdate& update,
                                            apps::LoadIconCallback callback) {
   constexpr bool kAllowPlaceholderIcon = false;
diff --git a/chrome/browser/apps/app_service/app_service_proxy_ash.h b/chrome/browser/apps/app_service/app_service_proxy_ash.h
index 22f58a9..95894e6 100644
--- a/chrome/browser/apps/app_service/app_service_proxy_ash.h
+++ b/chrome/browser/apps/app_service/app_service_proxy_ash.h
@@ -411,6 +411,9 @@
   // AppService stops the running app and applies the paused app icon effect.
   void OnPauseDialogClosed(apps::AppType app_type, const std::string& app_id);
 
+  bool ShouldExcludeBrowserTabApps(bool exclude_browser_tab_apps,
+                                   WindowMode window_mode) override;
+
   // apps::AppRegistryCache::Observer overrides:
   void OnAppUpdate(const apps::AppUpdate& update) override;
   void OnAppRegistryCacheWillBeDestroyed(
diff --git a/chrome/browser/apps/app_service/app_service_proxy_base.cc b/chrome/browser/apps/app_service/app_service_proxy_base.cc
index fe41a4eb..633236d8 100644
--- a/chrome/browser/apps/app_service/app_service_proxy_base.cc
+++ b/chrome/browser/apps/app_service/app_service_proxy_base.cc
@@ -553,8 +553,8 @@
     if (!update.HandlesIntents().value_or(false)) {
       return;
     }
-    if (exclude_browser_tab_apps &&
-        update.WindowMode() == WindowMode::kBrowser) {
+    if (ShouldExcludeBrowserTabApps(exclude_browser_tab_apps,
+                                    update.WindowMode())) {
       return;
     }
     // |activity_label| -> {index, is_generic}
@@ -593,6 +593,12 @@
   return intent_launch_info;
 }
 
+bool AppServiceProxyBase::ShouldExcludeBrowserTabApps(
+    bool exclude_browser_tab_apps,
+    WindowMode window_mode) {
+  return (exclude_browser_tab_apps && window_mode == WindowMode::kBrowser);
+}
+
 std::vector<IntentLaunchInfo> AppServiceProxyBase::GetAppsForFiles(
     std::vector<apps::IntentFilePtr> files) {
   return GetAppsForIntent(std::make_unique<apps::Intent>(
diff --git a/chrome/browser/apps/app_service/app_service_proxy_base.h b/chrome/browser/apps/app_service/app_service_proxy_base.h
index 96b0975..817d7c8 100644
--- a/chrome/browser/apps/app_service/app_service_proxy_base.h
+++ b/chrome/browser/apps/app_service/app_service_proxy_base.h
@@ -434,6 +434,9 @@
   virtual void OnLaunched(LaunchCallback callback,
                           LaunchResult&& launch_result);
 
+  virtual bool ShouldExcludeBrowserTabApps(bool exclude_browser_tab_apps,
+                                           WindowMode window_mode);
+
   // Returns true if we should read icon image files from the local app_service
   // icon directory on disk, e.g. for ChromeOS. Otherwise, returns false.
   virtual bool ShouldReadIcons(AppType app_type);
diff --git a/chrome/browser/apps/app_service/app_service_proxy_lacros.cc b/chrome/browser/apps/app_service/app_service_proxy_lacros.cc
index 84f3b945..21b9325 100644
--- a/chrome/browser/apps/app_service/app_service_proxy_lacros.cc
+++ b/chrome/browser/apps/app_service/app_service_proxy_lacros.cc
@@ -342,9 +342,11 @@
         !update.ShowInLauncher().value_or(false)) {
       return;
     }
-    if (exclude_browser_tab_apps &&
-        update.WindowMode() == WindowMode::kBrowser) {
-      return;
+    if (!chromeos::features::IsCrosShortstandEnabled()) {
+      if (exclude_browser_tab_apps &&
+          update.WindowMode() == WindowMode::kBrowser) {
+        return;
+      }
     }
     std::set<std::string> existing_activities;
     for (const auto& filter : update.IntentFilters()) {
diff --git a/chrome/browser/apps/app_service/metrics/app_platform_metrics_utils.cc b/chrome/browser/apps/app_service/metrics/app_platform_metrics_utils.cc
index 6164c597..6ae6fe64 100644
--- a/chrome/browser/apps/app_service/metrics/app_platform_metrics_utils.cc
+++ b/chrome/browser/apps/app_service/metrics/app_platform_metrics_utils.cc
@@ -29,6 +29,7 @@
 #include "chrome/browser/web_applications/web_app_utils.h"
 #include "chromeos/components/kiosk/kiosk_utils.h"
 #include "chromeos/components/mgs/managed_guest_session_utils.h"
+#include "chromeos/constants/chromeos_features.h"
 #include "components/app_constants/constants.h"
 #include "components/services/app_service/public/cpp/app_launch_util.h"
 #include "components/services/app_service/public/cpp/app_types.h"
@@ -166,6 +167,14 @@
   if (type_name != AppTypeName::kWeb) {
     return type_name;
   }
+  // TODO(b/321143888): When Shortstand is enabled, the window mode will
+  // always be kWindow. update.WindowMode is now used to check previous
+  // window mode for use by migration nudge. The WindowMode value for web
+  // apps will be updated to accurately represent the change after
+  // migration has been completed.
+  if (chromeos::features::IsCrosShortstandEnabled()) {
+    return GetWebAppTypeName();
+  }
 
   switch (container) {
     case apps::LaunchContainer::kLaunchContainerWindow:
diff --git a/chrome/browser/apps/app_service/publishers/arc_apps.cc b/chrome/browser/apps/app_service/publishers/arc_apps.cc
index 87fe8ea9..6b1108a2 100644
--- a/chrome/browser/apps/app_service/publishers/arc_apps.cc
+++ b/chrome/browser/apps/app_service/publishers/arc_apps.cc
@@ -21,6 +21,7 @@
 #include "ash/constants/ash_features.h"
 #include "ash/public/cpp/app_menu_constants.h"
 #include "base/containers/contains.h"
+#include "base/containers/fixed_flat_set.h"
 #include "base/containers/flat_map.h"
 #include "base/feature_list.h"
 #include "base/files/file_path.h"
@@ -420,9 +421,22 @@
   return app_id == arc::kPlayStoreAppId;
 }
 
-// Returns true if the given `profile` should open supported links inside the
-// app by default.
-bool ProfileShouldDefaultHandleLinksInApp(Profile* profile) {
+// Returns true if the package with the given |package_name| should open
+// supported links inside the browser by default, on managed devices.
+bool PackageShouldDefaultHandleLinksInBrowser(const std::string& package_name) {
+  constexpr auto allowlist = base::MakeFixedFlatSet<std::string_view>({
+      "com.google.android.apps.docs",                 // Google Drive
+      "com.google.android.apps.docs.editors.docs",    // Google Docs
+      "com.google.android.apps.docs.editors.sheets",  // Google Sheets
+      "com.google.android.apps.docs.editors.slides",  // Google Slides
+  });
+
+  return allowlist.contains(package_name);
+}
+
+// Returns true if the given `profile` is managed, and therefore should open
+// supported links inside the app by default.
+bool IsProfileManaged(Profile* profile) {
   // TODO(crbug.com/1454381): Remove once we have policy control over link
   // capturing behavior.
   return profile->GetProfilePolicyConnector()->IsManaged();
@@ -1186,16 +1200,28 @@
     }
 
     // ARC apps may handle links by default on the ARC side, but do not handle
-    // links by default on the Ash side. Therefore, we ignore any requests from
-    // the ARC system to change the default setting. We allow changes if they
-    // were initiated by user action, if the app already has a non-default
-    // setting on the Ash side, or if the app/profile should have an exception
-    // to the default behavior.
+    // links by default on the Ash side. This means that the default setting may
+    // be different between Ash and ARC. Any user action to change the setting
+    // will make it the same between both sides.
+    //
+    // To make this work, we need to ignore request from the ARC system to
+    // update the supported links setting. We allow updates in the following
+    // cases:
     bool allow_update =
+        // When the user explicitly changes the setting in Android Settings.
         source == arc::mojom::SupportedLinkChangeSource::kUserPreference ||
+        // If the app is already marked as preferred on the Ash side.
         proxy()->PreferredAppsList().IsPreferredAppForSupportedLinks(app_id) ||
-        AppShouldDefaultHandleLinksInApp(app_id) ||
-        ProfileShouldDefaultHandleLinksInApp(profile_);
+        // If the app is specifically allowed to handle links by default.
+        AppShouldDefaultHandleLinksInApp(app_id);
+
+    // Managed users are temporarily opted out of this behavior (b/280056133)
+    // and always apply updates from the ARC side, except for an allowlist of
+    // apps which handle links in the browser to improve the user experience.
+    if (IsProfileManaged(profile_) && !PackageShouldDefaultHandleLinksInBrowser(
+                                          supported_link->package_name)) {
+      allow_update = true;
+    }
 
     if (!allow_update) {
       continue;
diff --git a/chrome/browser/apps/app_service/publishers/arc_apps_unittest.cc b/chrome/browser/apps/app_service/publishers/arc_apps_unittest.cc
index 6e60e2f..386dfa6 100644
--- a/chrome/browser/apps/app_service/publishers/arc_apps_unittest.cc
+++ b/chrome/browser/apps/app_service/publishers/arc_apps_unittest.cc
@@ -21,6 +21,7 @@
 #include "base/functional/callback_helpers.h"
 #include "base/memory/raw_ptr.h"
 #include "base/ranges/algorithm.h"
+#include "base/strings/strcat.h"
 #include "base/strings/string_util.h"
 #include "base/test/bind.h"
 #include "base/test/scoped_feature_list.h"
@@ -471,6 +472,91 @@
                         GURL("https://www.example.com/foo")));
 }
 
+TEST_F(ArcAppsPublisherManagedProfileTest,
+       SetSupportedLinksIgnoresWorkspaceInstall) {
+  constexpr char kTestAuthority[] = "drive.google.com";
+  std::string package_name = "com.google.android.apps.docs";
+  std::string activity_name = base::StrCat({package_name, ".MainActivity"});
+  std::string app_id = ArcAppListPrefs::GetAppId(package_name, activity_name);
+
+  arc::mojom::AppInfoPtr app =
+      arc::mojom::AppInfo::New("Google Drive", package_name, activity_name);
+  std::vector<arc::mojom::AppInfoPtr> app_list;
+  app_list.push_back(std::move(app));
+
+  arc_test()->app_instance()->SendRefreshAppList(std::move(app_list));
+
+  // Update intent filters and supported links for the app, as if it was just
+  // installed.
+  intent_helper()->OnIntentFiltersUpdatedForPackage(
+      package_name, CreateFilterList(package_name, {kTestAuthority}));
+  VerifyIntentFilters(app_id, {kTestAuthority});
+  intent_helper()->OnSupportedLinksChanged(
+      CreateSupportedLinks(package_name), {},
+      arc::mojom::SupportedLinkChangeSource::kArcSystem);
+
+  ASSERT_EQ(std::nullopt, preferred_apps().FindPreferredAppForUrl(
+                              GURL("https://drive.google.com/foo")));
+}
+
+TEST_F(ArcAppsPublisherManagedProfileTest,
+       SetSupportedLinksAllowsWorkspaceUserChange) {
+  constexpr char kTestAuthority[] = "docs.google.com";
+  std::string package_name = "com.google.android.apps.docs.editor.docs";
+  std::string activity_name = base::StrCat({package_name, ".MainActivity"});
+  std::string app_id = ArcAppListPrefs::GetAppId(package_name, activity_name);
+
+  arc::mojom::AppInfoPtr app =
+      arc::mojom::AppInfo::New("Google Docs", package_name, activity_name);
+  std::vector<arc::mojom::AppInfoPtr> app_list;
+  app_list.push_back(std::move(app));
+
+  arc_test()->app_instance()->SendRefreshAppList(std::move(app_list));
+  intent_helper()->OnIntentFiltersUpdatedForPackage(
+      package_name, CreateFilterList(package_name, {kTestAuthority}));
+
+  intent_helper()->OnSupportedLinksChanged(
+      CreateSupportedLinks(package_name), {},
+      arc::mojom::SupportedLinkChangeSource::kUserPreference);
+
+  ASSERT_EQ(app_id, preferred_apps().FindPreferredAppForUrl(
+                        GURL("https://docs.google.com/document/")));
+}
+
+TEST_F(ArcAppsPublisherManagedProfileTest,
+       SetSupportedLinksAllowsWorkspaceUpdate) {
+  constexpr char kDriveAuthority[] = "drive.google.com";
+  constexpr char kDocsAuthority[] = "docs.google.com";
+  std::string package_name = "com.google.android.apps.docs";
+  std::string activity_name = base::StrCat({package_name, ".MainActivity"});
+  std::string app_id = ArcAppListPrefs::GetAppId(package_name, activity_name);
+
+  arc::mojom::AppInfoPtr app =
+      arc::mojom::AppInfo::New("Google Drive", package_name, activity_name);
+  std::vector<arc::mojom::AppInfoPtr> app_list;
+  app_list.push_back(std::move(app));
+  arc_test()->app_instance()->SendRefreshAppList(std::move(app_list));
+  intent_helper()->OnIntentFiltersUpdatedForPackage(
+      package_name, CreateFilterList(package_name, {kDriveAuthority}));
+
+  apps::AppServiceProxyFactory::GetForProfile(profile())
+      ->SetSupportedLinksPreference(app_id);
+  ASSERT_EQ(app_id, preferred_apps().FindPreferredAppForUrl(
+                        GURL("https://drive.google.com/foo")));
+
+  // Simulate the app being updated to add a new intent filter.
+  intent_helper()->OnIntentFiltersUpdatedForPackage(
+      package_name,
+      CreateFilterList(package_name, {kDriveAuthority, kDocsAuthority}));
+  intent_helper()->OnSupportedLinksChanged(
+      CreateSupportedLinks(package_name), {},
+      arc::mojom::SupportedLinkChangeSource::kArcSystem);
+
+  // Verify that the new intent filter is also marked as preferred.
+  ASSERT_EQ(app_id, preferred_apps().FindPreferredAppForUrl(
+                        GURL("https://docs.google.com/document")));
+}
+
 // Verifies that ARC permissions are published to App Service correctly.
 TEST_F(ArcAppsPublisherTest, PublishPermission) {
   constexpr char kPackageName[] = "com.test.package";
diff --git a/chrome/browser/apps/app_service/publishers/shortcut_publisher_unittest.cc b/chrome/browser/apps/app_service/publishers/shortcut_publisher_unittest.cc
index d4426642..582be71 100644
--- a/chrome/browser/apps/app_service/publishers/shortcut_publisher_unittest.cc
+++ b/chrome/browser/apps/app_service/publishers/shortcut_publisher_unittest.cc
@@ -130,7 +130,7 @@
 
   ShortcutPtr shortcut_2 = std::make_unique<Shortcut>("app_id_1", "local_id_2");
   shortcut_2->name = "name2";
-  shortcut_2->shortcut_source = ShortcutSource::kDeveloper;
+  shortcut_2->shortcut_source = ShortcutSource::kUser;
 
   Shortcuts initial_chrome_shortcuts;
   initial_chrome_shortcuts.push_back(std::move(shortcut_1));
@@ -226,7 +226,7 @@
 
   ShortcutPtr shortcut_2 = std::make_unique<Shortcut>("app_id_1", "local_id_2");
   shortcut_2->name = "name2";
-  shortcut_2->shortcut_source = ShortcutSource::kDeveloper;
+  shortcut_2->shortcut_source = ShortcutSource::kUser;
 
   Shortcuts initial_chrome_shortcuts;
   initial_chrome_shortcuts.push_back(std::move(shortcut_1));
diff --git a/chrome/browser/ash/app_list/app_service/app_service_shortcut_model_builder.cc b/chrome/browser/ash/app_list/app_service/app_service_shortcut_model_builder.cc
index cc06439..c0c3d87 100644
--- a/chrome/browser/ash/app_list/app_service/app_service_shortcut_model_builder.cc
+++ b/chrome/browser/ash/app_list/app_service/app_service_shortcut_model_builder.cc
@@ -16,7 +16,7 @@
 
 namespace {
 bool ShouldShowInLauncher(const apps::ShortcutSource& shortcut_source) {
-  return shortcut_source == apps::ShortcutSource::kUser;
+  return shortcut_source != apps::ShortcutSource::kUnknown;
 }
 }  // namespace
 
diff --git a/chrome/browser/ash/app_list/search/essential_search/essential_search_manager.cc b/chrome/browser/ash/app_list/search/essential_search/essential_search_manager.cc
index 0f1e90e..1b70782 100644
--- a/chrome/browser/ash/app_list/search/essential_search/essential_search_manager.cc
+++ b/chrome/browser/ash/app_list/search/essential_search/essential_search_manager.cc
@@ -8,7 +8,9 @@
 #include "chrome/browser/ash/app_list/search/essential_search/socs_cookie_fetcher.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/common/pref_names.h"
 #include "chromeos/constants/chromeos_features.h"
+#include "components/prefs/pref_service.h"
 #include "components/session_manager/session_manager_types.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/storage_partition.h"
@@ -21,6 +23,12 @@
 #include "url/url_constants.h"
 
 namespace app_list {
+namespace {
+bool IsEssentialSearchEnabled(PrefService* prefs) {
+  return chromeos::features::IsEssentialSearchEnabled() &&
+         prefs->GetBoolean(prefs::kSearchSuggestEnabled);
+}
+}  // namespace
 
 EssentialSearchManager::EssentialSearchManager(Profile* primary_profile)
     : primary_profile_(primary_profile) {
@@ -40,8 +48,8 @@
 
 void EssentialSearchManager::OnSessionStateChanged(
     session_manager::SessionState state) {
-  if (chromeos::features::IsEssentialSearchEnabled() &&
-      state == session_manager::SessionState::ACTIVE) {
+  if (state == session_manager::SessionState::ACTIVE &&
+      IsEssentialSearchEnabled(primary_profile_->GetPrefs())) {
     FetchSocsCookie();
   }
 }
diff --git a/chrome/browser/ash/chrome_browser_main_parts_ash.cc b/chrome/browser/ash/chrome_browser_main_parts_ash.cc
index d7f5620..882a0b6 100644
--- a/chrome/browser/ash/chrome_browser_main_parts_ash.cc
+++ b/chrome/browser/ash/chrome_browser_main_parts_ash.cc
@@ -1366,9 +1366,13 @@
   event_rewriter_controller->Initialize(
       event_rewriter_delegate_.get(),
       accessibility_event_rewriter_delegate_.get());
-  // `ShortcutInputHandler` is dependent on `EventRewriterController`'s
-  // initialization.
-  Shell::Get()->shortcut_input_handler()->Initialize();
+  // `ShortcutInputHandler` and `ModifierKeyComboRecorder` are dependent on
+  // `EventRewriterController`'s initialization.
+  if (ash::features::IsPeripheralCustomizationEnabled() ||
+      ::features::IsShortcutCustomizationEnabled()) {
+    Shell::Get()->shortcut_input_handler()->Initialize();
+    Shell::Get()->modifier_key_combo_recorder()->Initialize();
+  }
 
   // Enable the KeyboardDrivenEventRewriter if the OEM manifest flag is on.
   if (system::InputDeviceSettings::Get()->ForceKeyboardDrivenUINavigation()) {
diff --git a/chrome/browser/ash/login/demo_mode/demo_session_browsertest.cc b/chrome/browser/ash/login/demo_mode/demo_session_browsertest.cc
index 0f6a879..6d0a41f 100644
--- a/chrome/browser/ash/login/demo_mode/demo_session_browsertest.cc
+++ b/chrome/browser/ash/login/demo_mode/demo_session_browsertest.cc
@@ -415,8 +415,13 @@
   base::test::ScopedFeatureList scoped_feature_list_;
 };
 
+#if BUILDFLAG(IS_LINUX)
+#define MAYBE_DemoSWALaunchesOnSessionStartupWithPayload DISABLED_DemoSWALaunchesOnSessionStartupWithPayload
+#else
+#define MAYBE_DemoSWALaunchesOnSessionStartupWithPayload DemoSWALaunchesOnSessionStartupWithPayload
+#endif
 IN_PROC_BROWSER_TEST_F(DemoSessionLoginWithGrowthCampaignTest,
-                       DemoSWALaunchesOnSessionStartupWithPayload) {
+                       MAYBE_DemoSWALaunchesOnSessionStartupWithPayload) {
   base::ScopedAllowBlockingForTesting scoped_allow_blocking;
 
   CreateTestCampaignsFile(R"({
diff --git a/chrome/browser/ash/login/signin/oauth2_browsertest.cc b/chrome/browser/ash/login/signin/oauth2_browsertest.cc
index b3455a3a..2a9b3bc 100644
--- a/chrome/browser/ash/login/signin/oauth2_browsertest.cc
+++ b/chrome/browser/ash/login/signin/oauth2_browsertest.cc
@@ -984,11 +984,10 @@
 
   // Kick off XHR request from the extension.
   JsExpectOnBackgroundPageAsync(
-      ext->id(), base::StringPrintf("startThrottledTests('%s', '%s', %s, %s)",
+      ext->id(), base::StringPrintf("startThrottledTests('%s', '%s', %s)",
                                     fake_google_page_url_.spec().c_str(),
                                     non_google_page_url_.spec().c_str(),
-                                    BoolToString(do_async_xhr()),
-                                    BoolToString(/*should_throttle=*/true)));
+                                    BoolToString(do_async_xhr())));
   ExtensionTestMessageListener listener("Both XHR's Opened");
   ASSERT_TRUE(listener.WaitUntilSatisfied());
 
@@ -1053,11 +1052,10 @@
 
   // Kick off XHR request from the extension.
   JsExpectOnBackgroundPage(
-      ext->id(),
-      base::StringPrintf("startThrottledTests('%s', '%s', %s, %s)",
-                         fake_google_page_url_.spec().c_str(),
-                         non_google_page_url_.spec().c_str(),
-                         BoolToString(do_async_xhr()), BoolToString(false)));
+      ext->id(), base::StringPrintf("startThrottledTests('%s', '%s', %s)",
+                                    fake_google_page_url_.spec().c_str(),
+                                    non_google_page_url_.spec().c_str(),
+                                    BoolToString(do_async_xhr())));
 
   if (do_async_xhr()) {
     // Verify that we've sent XHR request from the extension side...
@@ -1136,11 +1134,10 @@
 
   // Kick off XHR request from the extension.
   JsExpectOnBackgroundPageAsync(
-      ext->id(),
-      base::StringPrintf("startThrottledTests('%s', '%s', %s, %s)",
-                         fake_google_page_url_.spec().c_str(),
-                         non_google_page_url_.spec().c_str(),
-                         BoolToString(do_async_xhr()), BoolToString(true)));
+      ext->id(), base::StringPrintf("startThrottledTests('%s', '%s', %s)",
+                                    fake_google_page_url_.spec().c_str(),
+                                    non_google_page_url_.spec().c_str(),
+                                    BoolToString(do_async_xhr())));
 
   if (do_async_xhr()) {
     // Verify that we've sent XHR requests from the extension side...
diff --git a/chrome/browser/ash/login/supervised_user_login_integration_test.cc b/chrome/browser/ash/login/supervised_user_login_integration_test.cc
index 055ae6a..5a5451ea 100644
--- a/chrome/browser/ash/login/supervised_user_login_integration_test.cc
+++ b/chrome/browser/ash/login/supervised_user_login_integration_test.cc
@@ -85,7 +85,9 @@
   SupervisedUserLoginDelegate delegate_;
 };
 
-IN_PROC_BROWSER_TEST_F(SupervisedUserLoginIntegrationTest, TestUnicornLogin) {
+// TODO(b/318616623): Re-enable when flaky timeout is resolved.
+IN_PROC_BROWSER_TEST_F(SupervisedUserLoginIntegrationTest,
+                       DISABLE_TestUnicornLogin) {
   SetupContextWidget();
 
   login_mixin().Login();
@@ -95,7 +97,9 @@
   VerifyPolicies();
 }
 
-IN_PROC_BROWSER_TEST_F(SupervisedUserLoginIntegrationTest, TestGellerLogin) {
+// TODO(b/318616623): Re-enable when flaky timeout is resolved.
+IN_PROC_BROWSER_TEST_F(SupervisedUserLoginIntegrationTest,
+                       DISABLE_TestGellerLogin) {
   SetupContextWidget();
 
   delegate_.set_user_type(
@@ -107,7 +111,9 @@
   VerifyPolicies();
 }
 
-IN_PROC_BROWSER_TEST_F(SupervisedUserLoginIntegrationTest, TestGriffinLogin) {
+// TODO(b/318616623): Re-enable when flaky timeout is resolved.
+IN_PROC_BROWSER_TEST_F(SupervisedUserLoginIntegrationTest,
+                       DISABLE_TestGriffinLogin) {
   SetupContextWidget();
 
   delegate_.set_user_type(
diff --git a/chrome/browser/ash/policy/remote_commands/device_command_fetch_support_packet_job.cc b/chrome/browser/ash/policy/remote_commands/device_command_fetch_support_packet_job.cc
index 46c58cd..b5cfe53 100644
--- a/chrome/browser/ash/policy/remote_commands/device_command_fetch_support_packet_job.cc
+++ b/chrome/browser/ash/policy/remote_commands/device_command_fetch_support_packet_job.cc
@@ -22,6 +22,7 @@
 #include "base/json/json_reader.h"
 #include "base/json/json_writer.h"
 #include "base/metrics/histogram_functions.h"
+#include "base/sequence_checker.h"
 #include "base/strings/strcat.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_piece.h"
@@ -220,10 +221,12 @@
 
 enterprise_management::RemoteCommand_Type
 DeviceCommandFetchSupportPacketJob::GetType() const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   return enterprise_management::RemoteCommand_Type_FETCH_SUPPORT_PACKET;
 }
 
 const base::FilePath DeviceCommandFetchSupportPacketJob::GetTargetDir() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (g_target_directory_for_testing) {
     CHECK_IS_TEST();
     return *g_target_directory_for_testing;
@@ -233,6 +236,7 @@
 
 bool DeviceCommandFetchSupportPacketJob::ParseCommandPayload(
     const std::string& command_payload) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   bool parse_success = ParseCommandPayloadImpl(command_payload);
   if (!parse_success) {
     base::UmaHistogramEnumeration(
@@ -247,6 +251,7 @@
 
 bool DeviceCommandFetchSupportPacketJob::ParseCommandPayloadImpl(
     const std::string& command_payload) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   std::optional<base::Value> value = base::JSONReader::Read(command_payload);
   if (!value.has_value() || !value->is_dict()) {
     return false;
@@ -295,6 +300,7 @@
 }
 
 bool DeviceCommandFetchSupportPacketJob::IsCommandEnabled() const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   bool log_upload_enabled;
   if (!ash::CrosSettings::IsInitialized() ||
       !ash::CrosSettings::Get()->GetBoolean(ash::kSystemLogUploadEnabled,
@@ -306,6 +312,7 @@
 
 void DeviceCommandFetchSupportPacketJob::RunImpl(
     CallbackWithResult result_callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   result_callback_ = std::move(result_callback);
   // Check if the command is enabled for the user.
   if (!IsCommandEnabled()) {
@@ -328,6 +335,7 @@
 }
 
 bool DeviceCommandFetchSupportPacketJob::IsPiiAllowed() const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   switch (current_session_type_) {
     case UserSessionType::AUTO_LAUNCHED_KIOSK_SESSION:
     case UserSessionType::MANUALLY_LAUNCHED_KIOSK_SESSION:
@@ -345,6 +353,7 @@
 }
 
 void DeviceCommandFetchSupportPacketJob::StartJobExecution() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   current_session_type_ = GetCurrentUserSessionType();
 
   // Get sign-in profile on sign-in screen.
@@ -373,6 +382,7 @@
 void DeviceCommandFetchSupportPacketJob::OnDataCollected(
     const PIIMap& detected_pii,
     std::set<SupportToolError> errors) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   // Log the errors for information. We don't return any error for the command
   // job, we just continue the operation with as much data as we could collect.
   if (!errors.empty()) {
@@ -401,6 +411,7 @@
 void DeviceCommandFetchSupportPacketJob::OnDataExported(
     base::FilePath exported_path,
     std::set<SupportToolError> errors) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   const auto export_error =
       base::ranges::find(errors, SupportToolErrorCode::kDataExportError,
                          &SupportToolError::error_code);
@@ -434,12 +445,14 @@
 
 void DeviceCommandFetchSupportPacketJob::OnReportQueueCreated(
     std::unique_ptr<reporting::ReportQueue> report_queue) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   SYSLOG(INFO) << "ReportQueue is created for LogUploadEvent.";
   report_queue_ = std::move(report_queue);
   EnqueueEvent();
 }
 
 void DeviceCommandFetchSupportPacketJob::EnqueueEvent() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   auto log_upload_event = std::make_unique<ash::reporting::LogUploadEvent>();
   log_upload_event->mutable_upload_settings()->set_origin_path(
       exported_path_.value());
@@ -457,6 +470,7 @@
 
 void DeviceCommandFetchSupportPacketJob::OnEventEnqueued(
     reporting::Status status) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (status.ok()) {
     base::UmaHistogramEnumeration(
         kFetchSupportPacketFailureHistogramName,
diff --git a/chrome/browser/ash/policy/remote_commands/device_command_fetch_support_packet_job.h b/chrome/browser/ash/policy/remote_commands/device_command_fetch_support_packet_job.h
index 4220c9c..57b6c423 100644
--- a/chrome/browser/ash/policy/remote_commands/device_command_fetch_support_packet_job.h
+++ b/chrome/browser/ash/policy/remote_commands/device_command_fetch_support_packet_job.h
@@ -13,6 +13,7 @@
 #include "base/files/file_path.h"
 #include "base/functional/callback_forward.h"
 #include "base/memory/weak_ptr.h"
+#include "base/sequence_checker.h"
 #include "chrome/browser/support_tool/data_collection_module.pb.h"
 #include "chrome/browser/support_tool/support_tool_handler.h"
 #include "components/feedback/redaction_tool/pii_types.h"
@@ -121,6 +122,7 @@
 
   void OnEventEnqueued(reporting::Status status);
 
+  SEQUENCE_CHECKER(sequence_checker_);
   // The filepath of the exported support packet. It will be a file within
   // GetTargetDir().
   base::FilePath exported_path_;
diff --git a/chrome/browser/browsing_data/chrome_browsing_data_model_delegate_unittest.cc b/chrome/browser/browsing_data/chrome_browsing_data_model_delegate_unittest.cc
index 82f3f9e..7463e43 100644
--- a/chrome/browser/browsing_data/chrome_browsing_data_model_delegate_unittest.cc
+++ b/chrome/browser/browsing_data/chrome_browsing_data_model_delegate_unittest.cc
@@ -267,8 +267,7 @@
 }
 #endif  // !BUILDFLAG(IS_ANDROID)
 
-// TODO(crbug.com/1493504): Re-enable on macOS once flakiness is resolved.
-#if !BUILDFLAG(IS_MAC) && BUILDFLAG(ENABLE_SUPERVISED_USERS)
+#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
 TEST_F(ChromeBrowsingDataModelDelegateTest, CookieDeletionFilterChildUser) {
   profile_->SetIsSupervisedProfile(true);
 
@@ -308,7 +307,7 @@
   EXPECT_FALSE(
       delegate()->IsCookieDeletionDisabled(GURL("https://youtube.com")));
 }
-#endif  // !BUILDFLAG(IS_MAC) && BUILDFLAG(ENABLE_SUPERVISED_USERS)
+#endif  // BUILDFLAG(ENABLE_SUPERVISED_USERS)
 
 TEST_F(ChromeBrowsingDataModelDelegateTest, RemoveFederatedIdentityData) {
   const url::Origin kRequester =
diff --git a/chrome/browser/chromeos/policy/dlp/dlp_data_transfer_notifier.cc b/chrome/browser/chromeos/policy/dlp/dlp_data_transfer_notifier.cc
index ec05b03..89a31b7 100644
--- a/chrome/browser/chromeos/policy/dlp/dlp_data_transfer_notifier.cc
+++ b/chrome/browser/chromeos/policy/dlp/dlp_data_transfer_notifier.cc
@@ -106,12 +106,16 @@
   params.ownership = views::Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET;
   params.name = kBubbleName;
   params.layer_type = ui::LAYER_NOT_DRAWN;
-  params.parent = nullptr;
   params.shadow_type = views::Widget::InitParams::ShadowType::kDrop;
 #if BUILDFLAG(IS_CHROMEOS_LACROS)
+  // Explicitly setting the parent window is required in Lacros for popup
+  // dismissal to work correctly.
+  params.parent = dlp::GetActiveAuraWindow();
   // WaylandPopups in Lacros need a context window to allow custom positioning.
   // Here, we pass the active Lacros window as context for the bubble widget.
-  params.context = dlp::GetActiveAuraWindow();
+  params.context = params.parent;
+#else
+  params.parent = nullptr;
 #endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
   return params;
 }
diff --git a/chrome/browser/content_settings/one_time_permission_provider.cc b/chrome/browser/content_settings/one_time_permission_provider.cc
index 8fb0f75..a7f1300 100644
--- a/chrome/browser/content_settings/one_time_permission_provider.cc
+++ b/chrome/browser/content_settings/one_time_permission_provider.cc
@@ -46,7 +46,8 @@
   base::PowerMonitor::RemovePowerSuspendObserver(this);
 }
 
-// TODO(b/307193732): handle the PartitionKey in all relevant methods.
+// TODO(b/307193732): handle the PartitionKey in all relevant methods, including
+// when we call NotifyObservers().
 std::unique_ptr<content_settings::RuleIterator>
 OneTimePermissionProvider::GetRuleIterator(
     ContentSettingsType content_type,
@@ -236,7 +237,8 @@
   permissions::PermissionUmaUtil::RecordOneTimePermissionEvent(
       content_settings_type,
       permissions::OneTimePermissionEvent::EXPIRED_AFTER_MAXIMUM_LIFETIME);
-  NotifyObservers(primary_pattern, secondary_pattern, content_settings_type);
+  NotifyObservers(primary_pattern, secondary_pattern, content_settings_type,
+                  /*partition_key=*/nullptr);
 }
 
 void OneTimePermissionProvider::OnSuspend() {
@@ -338,7 +340,7 @@
 
   for (const auto& pattern : entries_to_delete) {
     NotifyObservers(pattern.primary_pattern, pattern.secondary_pattern,
-                    pattern.type);
+                    pattern.type, /*partition_key=*/nullptr);
   }
 }
 
diff --git a/chrome/browser/engagement/important_sites_util.cc b/chrome/browser/engagement/important_sites_util.cc
index 9e5057b..7a0d3948 100644
--- a/chrome/browser/engagement/important_sites_util.cc
+++ b/chrome/browser/engagement/important_sites_util.cc
@@ -159,6 +159,10 @@
     return;
   std::string registerable_domain =
       ImportantSitesUtil::GetRegisterableDomainOrIP(origin);
+  if (registerable_domain.empty()) {
+    return;
+  }
+
   ImportantDomainInfo& info = (*output)[registerable_domain];
   info.reason_bitfield |= 1 << reason;
   if (info.example_origin.is_empty()) {
@@ -264,6 +268,11 @@
 
     std::string registerable_domain =
         ImportantSitesUtil::GetRegisterableDomainOrIP(detail.origin);
+
+    if (registerable_domain.empty()) {
+      continue;
+    }
+
     ImportantDomainInfo& info = (*output)[registerable_domain];
     if (detail.total_score > info.engagement_score) {
       info.registerable_domain = registerable_domain;
diff --git a/chrome/browser/engagement/important_sites_util_unittest.cc b/chrome/browser/engagement/important_sites_util_unittest.cc
index 0b313f3..690cac1c 100644
--- a/chrome/browser/engagement/important_sites_util_unittest.cc
+++ b/chrome/browser/engagement/important_sites_util_unittest.cc
@@ -470,6 +470,33 @@
   EXPECT_TRUE(ImportantSitesUtil::IsDialogDisabled(profile()));
 }
 
+TEST_F(ImportantSitesUtilTest, ExcludeNonRegisterableDomains) {
+  SiteEngagementService* service = SiteEngagementService::Get(profile());
+  ASSERT_TRUE(service);
+
+  GURL url1("http://www.google.com/");
+  GURL url2("chrome://newtab/");
+  GURL url3("chrome://settings/");
+  GURL url4("http://localhost/");
+
+  // Set a bunch of positive signals.
+  service->ResetBaseScoreForURL(url1, 8);
+  service->ResetBaseScoreForURL(url2, 9);
+  AddBookmark(url3);
+  AddContentSetting(ContentSettingsType::NOTIFICATIONS, CONTENT_SETTING_ALLOW,
+                    url4);
+
+  std::vector<ImportantDomainInfo> important_sites =
+      ImportantSitesUtil::GetImportantRegisterableDomains(profile(),
+                                                          kNumImportantSites);
+
+  ASSERT_EQ(1u, important_sites.size());
+  std::vector<std::string> expected_sorted_domains = {"google.com"};
+  std::vector<GURL> expected_sorted_origins = {url1};
+  ExpectImportantResultsEq(expected_sorted_domains, expected_sorted_origins,
+                           important_sites);
+}
+
 }  // namespace site_engagement
 
 #endif  // !BUILDFLAG(IS_ANDROID)
diff --git a/chrome/browser/extensions/api/web_request/web_request_apitest.cc b/chrome/browser/extensions/api/web_request/web_request_apitest.cc
index 573f9309..1fbb18df 100644
--- a/chrome/browser/extensions/api/web_request/web_request_apitest.cc
+++ b/chrome/browser/extensions/api/web_request/web_request_apitest.cc
@@ -3815,8 +3815,14 @@
 // Ensure we don't strip off initiator incorrectly in web request events when
 // both the normal and incognito contexts are active. Regression test for
 // crbug.com/934398.
+// TODO(crbug.com/1520416): enable this flaky test
+#if BUILDFLAG(IS_LINUX) && defined(ADDRESS_SANITIZER) && defined(LEAK_SANITIZER)
+#define MAYBE_Initiator_SpanningIncognito DISABLED_Initiator_SpanningIncognito
+#else
+#define MAYBE_Initiator_SpanningIncognito Initiator_SpanningIncognito
+#endif
 IN_PROC_BROWSER_TEST_P(ExtensionWebRequestApiTestWithContextType,
-                       Initiator_SpanningIncognito) {
+                       MAYBE_Initiator_SpanningIncognito) {
   embedded_test_server()->ServeFilesFromSourceDirectory("chrome/test/data");
   ASSERT_TRUE(embedded_test_server()->Start());
 
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index dcce289..d620fbc1 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -5302,6 +5302,11 @@
     "expiry_milestone": 125
   },
   {
+    "name": "link-preview",
+    "owners": [ "//chrome/browser/preloading/preview/OWNERS" ],
+    "expiry_milestone": 125
+  },
+  {
     "name": "list-all-display-modes",
     "owners": [ "//ui/display/OWNERS" ],
     // This flag is used for debugging and development purposes to list all
@@ -6730,10 +6735,10 @@
   {
     "name": "privacy-sandbox-proactive-topics-blocking",
     "owners": [
-      "sauski@chromium.org",
-      "olesiamarukhno@chromium.org",
+      "sauski@google.com",
+      "olesiamarukhno@google.com",
       "tommasin@chromium.org",
-      "alimariam@chromium.org"],
+      "alimariam@google.com"],
     "expiry_milestone": 122
   },
   {
@@ -8404,6 +8409,11 @@
     "expiry_milestone": 120
   },
   {
+    "name": "web-app-separate-user-display-mode-for-cros",
+    "owners": [ "glenrob@chromium.org", "cros-web-apps-core@google.com" ],
+    "expiry_milestone": 130
+  },
+  {
     "name": "web-app-sync-generated-icon-background-fix",
     "owners": [ "alancutter@chromium.org", "desktop-pwas-team@google.com" ],
     "expiry_milestone": 130
@@ -8419,6 +8429,21 @@
     "expiry_milestone": 130
   },
   {
+    "name": "web-app-sync-only-separate-user-display-mode-for-cros",
+    "owners": [ "glenrob@chromium.org", "cros-web-apps-core@google.com" ],
+    "expiry_milestone": 130
+  },
+  {
+    "name": "web-app-user-display-mode-sync-browser-mitigation",
+    "owners": [ "alancutter@chromium.org", "desktop-pwas-team@google.com" ],
+    "expiry_milestone": 132
+  },
+  {
+    "name": "web-app-user-display-mode-sync-standalone-mitigation",
+    "owners": [ "alancutter@chromium.org", "desktop-pwas-team@google.com" ],
+    "expiry_milestone": 132
+  },
+  {
     "name": "web-authentication-android-credential-management",
     "owners": ["kenrb@chromium.org", "agl@chromium.org"],
     "expiry_milestone": 124
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index cda3767..5488b94 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -4756,6 +4756,14 @@
     "discarding a tab rather than doing it after a fixed amount of time in the "
     "background.";
 
+const char kLinkPreviewName[] = "Link Preview";
+const char kLinkPreviewDescription[] =
+    "When enabled, Link Preview feature gets to be available to preview a "
+    "linked page in a dedicated small window before navigating to the linked "
+    "page. The feature can be triggered from a context menu item, or users' "
+    "actions. For now, Alt + Click is used for the shortcut, but we will "
+    "introduce more options for usability study.";
+
 const char kMemorySaverMultistateModeAvailableName[] =
     "Enable the multi-state option for Memory Saver Mode.";
 const char kMemorySaverMultistateModeAvailableDescription[] =
@@ -5070,6 +5078,22 @@
     "soon as a change has been detected instead of waiting for all app windows "
     "to be closed.";
 
+const char kWebAppSeparateUserDisplayModeForCrosName[] =
+    "Web App separate UserDisplayMode for CrOS";
+const char kWebAppSeparateUserDisplayModeForCrosDescription[] =
+    "Adds and uses a new web app field that records and syncs the User Display "
+    "Mode of web apps between CrOS devices separately from the existing User "
+    "Display Mode field (which syncs between non-CrOS devices and devices with "
+    "this flag disabled).";
+
+const char kWebAppSyncOnlySeparateUserDisplayModeForCrosName[] =
+    "Web App sync only separate UserDisplayMode for CrOS";
+const char kWebAppSyncOnlySeparateUserDisplayModeForCrosDescription[] =
+    "Adds and handles but does not use a new web app field that records and "
+    "syncs the User Display Mode of web apps between CrOS devices separately "
+    "from the existing User Display Mode field (which syncs between non-CrOS "
+    "devices and devices with this flag disabled).";
+
 const char kWebAppSyncGeneratedIconBackgroundFixName[] =
     "Web App Sync Generated Icon Background Fix";
 const char kWebAppSyncGeneratedIconBackgroundFixDescription[] =
@@ -5090,6 +5114,18 @@
     "during a manifest update if the icons were generated, indictative of"
     "network errors during the sync install.";
 
+const char kUserDisplayModeSyncBrowserMitigationName[] =
+    "Web App User Display Mode Sync Browser Mitigation";
+const char kUserDisplayModeSyncBrowserMitigationDescription[] =
+    "Enables a mitigation during web app install on CrOS for syncing "
+    "user_display_mode: kBrowser to non-CrOS devices.";
+
+const char kUserDisplayModeSyncStandaloneMitigationName[] =
+    "Web App User Display Mode Sync Standalone Mitigation";
+const char kUserDisplayModeSyncStandaloneMitigationDescription[] =
+    "Enables a mitigation during web app install on CrOS for syncing "
+    "user_display_mode: kStandalone to non-CrOS devices.";
+
 const char kWebAppSystemMediaControlsWinName[] =
     "Web App System Media Controls on Windows";
 const char kWebAppSystemMediaControlsWinDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 6eed3ef..ef7f6ed 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -2753,6 +2753,9 @@
 extern const char kHeuristicMemorySaverName[];
 extern const char kHeuristicMemorySaverDescription[];
 
+extern const char kLinkPreviewName[];
+extern const char kLinkPreviewDescription[];
+
 extern const char kMemorySaverMultistateModeAvailableName[];
 extern const char kMemorySaverMultistateModeAvailableDescription[];
 
@@ -2940,6 +2943,12 @@
 extern const char kWebAppManifestImmediateUpdatingName[];
 extern const char kWebAppManifestImmediateUpdatingDescription[];
 
+extern const char kWebAppSeparateUserDisplayModeForCrosName[];
+extern const char kWebAppSeparateUserDisplayModeForCrosDescription[];
+
+extern const char kWebAppSyncOnlySeparateUserDisplayModeForCrosName[];
+extern const char kWebAppSyncOnlySeparateUserDisplayModeForCrosDescription[];
+
 extern const char kWebAppSyncGeneratedIconBackgroundFixName[];
 extern const char kWebAppSyncGeneratedIconBackgroundFixDescription[];
 
@@ -2949,6 +2958,12 @@
 extern const char kWebAppSyncGeneratedIconUpdateFixName[];
 extern const char kWebAppSyncGeneratedIconUpdateFixDescription[];
 
+extern const char kUserDisplayModeSyncBrowserMitigationName[];
+extern const char kUserDisplayModeSyncBrowserMitigationDescription[];
+
+extern const char kUserDisplayModeSyncStandaloneMitigationName[];
+extern const char kUserDisplayModeSyncStandaloneMitigationDescription[];
+
 extern const char kWebAppSystemMediaControlsWinName[];
 extern const char kWebAppSystemMediaControlsWinDescription[];
 
diff --git a/chrome/browser/hid/hid_browsertest.cc b/chrome/browser/hid/hid_browsertest.cc
index c638e1a..435b941 100644
--- a/chrome/browser/hid/hid_browsertest.cc
+++ b/chrome/browser/hid/hid_browsertest.cc
@@ -470,8 +470,17 @@
 
 // Test the scenario of waking up the service worker upon device events and
 // the service worker being kept alive with active device session.
-IN_PROC_BROWSER_TEST_F(WebHidExtensionBrowserTest,
-                       DeviceConnectAndOpenDeviceWhenServiceWorkerStopped) {
+// TODO(crbug.com/1520400): enable the flaky test.
+#if BUILDFLAG(IS_LINUX) && defined(LEAK_SANITIZER)
+#define MAYBE_DeviceConnectAndOpenDeviceWhenServiceWorkerStopped \
+  DISABLED_DeviceConnectAndOpenDeviceWhenServiceWorkerStopped
+#else
+#define MAYBE_DeviceConnectAndOpenDeviceWhenServiceWorkerStopped \
+  DeviceConnectAndOpenDeviceWhenServiceWorkerStopped
+#endif
+IN_PROC_BROWSER_TEST_F(
+    WebHidExtensionBrowserTest,
+    MAYBE_DeviceConnectAndOpenDeviceWhenServiceWorkerStopped) {
   content::ServiceWorkerContext* context = browser()
                                                ->profile()
                                                ->GetDefaultStoragePartition()
diff --git a/chrome/browser/installable/installed_webapp_provider.cc b/chrome/browser/installable/installed_webapp_provider.cc
index 6f8b562..51c821e 100644
--- a/chrome/browser/installable/installed_webapp_provider.cc
+++ b/chrome/browser/installable/installed_webapp_provider.cc
@@ -105,5 +105,6 @@
 
 void InstalledWebappProvider::Notify(ContentSettingsType content_type) {
   NotifyObservers(ContentSettingsPattern::Wildcard(),
-                  ContentSettingsPattern::Wildcard(), content_type);
+                  ContentSettingsPattern::Wildcard(), content_type,
+                  /*partition_key=*/nullptr);
 }
diff --git a/chrome/browser/media/webrtc/chrome_screen_enumerator.cc b/chrome/browser/media/webrtc/chrome_screen_enumerator.cc
index e2c4ebd..40aa2b3 100644
--- a/chrome/browser/media/webrtc/chrome_screen_enumerator.cc
+++ b/chrome/browser/media/webrtc/chrome_screen_enumerator.cc
@@ -70,7 +70,8 @@
         /*display_surface=*/media::mojom::DisplayCaptureSurfaceType::MONITOR,
         /*logical_surface=*/true,
         /*cursor=*/media::mojom::CursorCaptureType::NEVER,
-        /*capture_handle=*/nullptr);
+        /*capture_handle=*/nullptr,
+        /*initial_zoom_level=*/100);
     stream_devices_set->stream_devices.push_back(
         blink::mojom::StreamDevices::New(/*audio_device=*/std::nullopt,
                                          /*video_device=*/device));
diff --git a/chrome/browser/media/webrtc/desktop_capture_devices_util.cc b/chrome/browser/media/webrtc/desktop_capture_devices_util.cc
index 3c4d30f..60957e2 100644
--- a/chrome/browser/media/webrtc/desktop_capture_devices_util.cc
+++ b/chrome/browser/media/webrtc/desktop_capture_devices_util.cc
@@ -20,11 +20,13 @@
 #include "chrome/grit/generated_resources.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_thread.h"
+#include "content/public/browser/host_zoom_map.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/web_contents_media_capture_id.h"
 #include "media/audio/audio_device_description.h"
 #include "media/mojo/mojom/capture_handle.mojom.h"
 #include "media/mojo/mojom/display_media_information.mojom.h"
+#include "third_party/blink/public/common/page/page_zoom.h"
 #include "third_party/blink/public/mojom/media/capture_handle_config.mojom.h"
 #include "third_party/blink/public/mojom/mediastream/media_stream.mojom.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -100,6 +102,28 @@
   return result;
 }
 
+absl::optional<int> GetZoomLevel(content::WebContents* capturer,
+                                 const url::Origin& capturer_origin,
+                                 const content::DesktopMediaID& captured_id) {
+  content::RenderFrameHost* const captured_rfh =
+      content::RenderFrameHost::FromID(
+          captured_id.web_contents_id.render_process_id,
+          captured_id.web_contents_id.main_render_frame_id);
+  if (!captured_rfh || !captured_rfh->IsActive()) {
+    return absl::nullopt;
+  }
+
+  content::WebContents* const captured_wc =
+      content::WebContents::FromRenderFrameHost(captured_rfh);
+  if (!captured_wc) {
+    return absl::nullopt;
+  }
+
+  double zoom_level = blink::PageZoomLevelToZoomFactor(
+      content::HostZoomMap::GetZoomLevel(captured_wc));
+  return std::round(100 * zoom_level);
+}
+
 media::mojom::DisplayMediaInformationPtr
 DesktopMediaIDToDisplayMediaInformation(
     content::WebContents* capturer,
@@ -118,6 +142,7 @@
 #endif  // defined(USE_AURA)
 
   media::mojom::CaptureHandlePtr capture_handle;
+  int zoom_level = 100;
   switch (media_id.type) {
     case content::DesktopMediaID::TYPE_SCREEN:
       display_surface = media::mojom::DisplayCaptureSurfaceType::MONITOR;
@@ -133,13 +158,16 @@
       display_surface = media::mojom::DisplayCaptureSurfaceType::BROWSER;
       cursor = media::mojom::CursorCaptureType::MOTION;
       capture_handle = CreateCaptureHandle(capturer, capturer_origin, media_id);
+      zoom_level =
+          GetZoomLevel(capturer, capturer_origin, media_id).value_or(100);
       break;
     case content::DesktopMediaID::TYPE_NONE:
       break;
   }
 
   return media::mojom::DisplayMediaInformation::New(
-      display_surface, logical_surface, cursor, std::move(capture_handle));
+      display_surface, logical_surface, cursor, std::move(capture_handle),
+      zoom_level);
 }
 
 std::u16string GetNotificationText(const std::u16string& application_title,
diff --git a/chrome/browser/media/webrtc/media_stream_capture_indicator_unittest.cc b/chrome/browser/media/webrtc/media_stream_capture_indicator_unittest.cc
index 0c5ef83..39f169e 100644
--- a/chrome/browser/media/webrtc/media_stream_capture_indicator_unittest.cc
+++ b/chrome/browser/media/webrtc/media_stream_capture_indicator_unittest.cc
@@ -173,7 +173,8 @@
          media::mojom::DisplayCaptureSurfaceType::MONITOR,
          /*logical_surface=*/true,
          media::mojom::CursorCaptureType::NEVER,
-         /*capture_handle=*/nullptr),
+         /*capture_handle=*/nullptr,
+         /*initial_zoom_level=*/100),
      &MockObserver::SetOnIsCapturingDisplayChangedExpectation,
      &MediaStreamCaptureIndicator::IsCapturingDisplay},
 };
diff --git a/chrome/browser/notifications/notification_channels_provider_android.cc b/chrome/browser/notifications/notification_channels_provider_android.cc
index 933b04e6..ee692aa 100644
--- a/chrome/browser/notifications/notification_channels_provider_android.cc
+++ b/chrome/browser/notifications/notification_channels_provider_android.cc
@@ -299,7 +299,8 @@
                        provider->weak_factory_.GetWeakPtr(),
                        ContentSettingsPattern::Wildcard(),
                        ContentSettingsPattern::Wildcard(),
-                       ContentSettingsType::NOTIFICATIONS));
+                       ContentSettingsType::NOTIFICATIONS,
+                       /*partition_key=*/nullptr));
     provider->cached_channels_ = std::move(updated_channels_map);
     provider->initialized_cached_channels_ = true;
   }
@@ -350,7 +351,8 @@
       if (channel_to_delete != cached_channels_.end()) {
         bridge_->DeleteChannel(channel_to_delete->second.id);
         cached_channels_.erase(channel_to_delete);
-        NotifyObservers(primary_pattern, secondary_pattern, content_type);
+        NotifyObservers(primary_pattern, secondary_pattern, content_type,
+                        /*partition_key=*/nullptr);
       }
       return false;
     }
@@ -377,7 +379,8 @@
 
   if (channels.size() > 0) {
     NotifyObservers(ContentSettingsPattern::Wildcard(),
-                    ContentSettingsPattern::Wildcard(), content_type);
+                    ContentSettingsPattern::Wildcard(), content_type,
+                    /*partition_key=*/nullptr);
   }
 }
 
@@ -442,9 +445,9 @@
         new_channel_status == NotificationChannelStatus::ENABLED);
     cached_channels_.emplace(origin_string, std::move(channel));
 
-    NotifyObservers(ContentSettingsPattern::Wildcard(),
-                    ContentSettingsPattern::Wildcard(),
-                    ContentSettingsType::NOTIFICATIONS);
+    NotifyObservers(
+        ContentSettingsPattern::Wildcard(), ContentSettingsPattern::Wildcard(),
+        ContentSettingsType::NOTIFICATIONS, /*partition_key=*/nullptr);
   } else {
     auto old_channel_status =
         bridge_->GetChannelStatus(channel_entry->second.id);
diff --git a/chrome/browser/password_manager/android/password_manager_eviction_util.cc b/chrome/browser/password_manager/android/password_manager_eviction_util.cc
index dca97bc..390b93a9 100644
--- a/chrome/browser/password_manager/android/password_manager_eviction_util.cc
+++ b/chrome/browser/password_manager/android/password_manager_eviction_util.cc
@@ -4,7 +4,6 @@
 
 #include "chrome/browser/password_manager/android/password_manager_eviction_util.h"
 
-#include "base/containers/flat_set.h"
 #include "base/logging.h"
 #include "base/metrics/field_trial_params.h"
 #include "base/metrics/histogram_functions.h"
@@ -65,24 +64,4 @@
       password_manager::prefs::kTimesAttemptedToReenrollToGoogleMobileServices);
 }
 
-bool ShouldIgnoreOnApiError(int api_error_code) {
-  return api_error_code ==
-             static_cast<int>(
-                 AndroidBackendAPIErrorCode::kAuthErrorResolvable) ||
-         api_error_code ==
-             static_cast<int>(
-                 AndroidBackendAPIErrorCode::kAuthErrorUnresolvable);
-}
-
-bool ShouldRetryOnApiError(int api_error_code) {
-  const base::flat_set<int> kRetriableErrors = {
-      static_cast<int>(AndroidBackendAPIErrorCode::kNetworkError),
-      static_cast<int>(AndroidBackendAPIErrorCode::kApiNotConnected),
-      static_cast<int>(
-          AndroidBackendAPIErrorCode::kConnectionSuspendedDuringCall),
-      static_cast<int>(AndroidBackendAPIErrorCode::kReconnectionTimedOut),
-      static_cast<int>(AndroidBackendAPIErrorCode::kBackendGeneric)};
-  return kRetriableErrors.contains(api_error_code);
-}
-
 }  // namespace password_manager_upm_eviction
diff --git a/chrome/browser/password_manager/android/password_manager_eviction_util.h b/chrome/browser/password_manager/android/password_manager_eviction_util.h
index 2cae6a4..45d5cea 100644
--- a/chrome/browser/password_manager/android/password_manager_eviction_util.h
+++ b/chrome/browser/password_manager/android/password_manager_eviction_util.h
@@ -26,16 +26,6 @@
 // Also clears the stored eviction reason and error list version.
 void ReenrollCurrentUser(PrefService* prefs);
 
-// Checks whether the occurred GMS Core API error should be ignored.
-// Returns true if the error is in the ignored error list, false otherwise.
-// Ignored errors are not handled by the backend and are returned to the caller.
-bool ShouldIgnoreOnApiError(int api_error_code);
-
-// Checks whether the occurred GMS Core API error should be retried.
-// Returns true if the error is in the retriable error list, false otherwise.
-// Certain backend operations may be retried if the returned error is retriable.
-bool ShouldRetryOnApiError(int api_error_code);
-
 }  // namespace password_manager_upm_eviction
 
 #endif  // CHROME_BROWSER_PASSWORD_MANAGER_ANDROID_PASSWORD_MANAGER_EVICTION_UTIL_H_
diff --git a/chrome/browser/password_manager/android/password_manager_eviction_util_unittest.cc b/chrome/browser/password_manager/android/password_manager_eviction_util_unittest.cc
index f1e14bc..5df137e 100644
--- a/chrome/browser/password_manager/android/password_manager_eviction_util_unittest.cc
+++ b/chrome/browser/password_manager/android/password_manager_eviction_util_unittest.cc
@@ -23,17 +23,7 @@
 constexpr char kUnenrollmentReasonHistogram[] =
     "PasswordManager.UPMUnenrollmentReason";
 
-constexpr int kNetworkError = 7;
 constexpr int kInternalError = 8;
-constexpr int kDeveloperError = 10;
-constexpr int kApiNotConnected = 17;
-constexpr int kConnectionSuspendedDuringCall = 20;
-constexpr int kReconnectionTimedOut = 22;
-constexpr int kAuthErrorResolvable = 11005;
-constexpr int kAuthErrorUnresolvable = 11006;
-constexpr int kBackendGeneric = 11009;
-constexpr int kInvalidData = 11011;
-constexpr int kUnexpectedError = 11013;
 
 }  // namespace
 
@@ -148,35 +138,3 @@
                     kTimesAttemptedToReenrollToGoogleMobileServices),
             0);
 }
-
-TEST_F(PasswordManagerEvictionUtilTest, ShouldIgnoreOnlyListedError) {
-  EXPECT_TRUE(password_manager_upm_eviction::ShouldIgnoreOnApiError(
-      kAuthErrorResolvable));
-  EXPECT_TRUE(password_manager_upm_eviction::ShouldIgnoreOnApiError(
-      kAuthErrorUnresolvable));
-  EXPECT_FALSE(
-      password_manager_upm_eviction::ShouldIgnoreOnApiError(kDeveloperError));
-  EXPECT_FALSE(
-      password_manager_upm_eviction::ShouldIgnoreOnApiError(kUnexpectedError));
-}
-
-TEST_F(PasswordManagerEvictionUtilTest, ShouldRetryOnlyListedError) {
-  EXPECT_TRUE(
-      password_manager_upm_eviction::ShouldRetryOnApiError(kNetworkError));
-  EXPECT_TRUE(
-      password_manager_upm_eviction::ShouldRetryOnApiError(kApiNotConnected));
-  EXPECT_TRUE(password_manager_upm_eviction::ShouldRetryOnApiError(
-      kConnectionSuspendedDuringCall));
-  EXPECT_TRUE(password_manager_upm_eviction::ShouldRetryOnApiError(
-      kReconnectionTimedOut));
-  EXPECT_TRUE(
-      password_manager_upm_eviction::ShouldRetryOnApiError(kBackendGeneric));
-  EXPECT_FALSE(
-      password_manager_upm_eviction::ShouldRetryOnApiError(kDeveloperError));
-  EXPECT_FALSE(
-      password_manager_upm_eviction::ShouldRetryOnApiError(kInvalidData));
-  EXPECT_FALSE(
-      password_manager_upm_eviction::ShouldRetryOnApiError(kInternalError));
-  EXPECT_FALSE(
-      password_manager_upm_eviction::ShouldRetryOnApiError(kUnexpectedError));
-}
diff --git a/chrome/browser/password_manager/android/password_store_android_backend.cc b/chrome/browser/password_manager/android/password_store_android_backend.cc
index e1365f5..59e91eed 100644
--- a/chrome/browser/password_manager/android/password_store_android_backend.cc
+++ b/chrome/browser/password_manager/android/password_store_android_backend.cc
@@ -14,6 +14,7 @@
 #include <vector>
 
 #include "base/barrier_callback.h"
+#include "base/containers/flat_set.h"
 #include "base/functional/bind.h"
 #include "base/functional/callback.h"
 #include "base/location.h"
@@ -265,62 +266,6 @@
                                 UnifiedPasswordManagerActiveStatus::kActive);
 }
 
-bool IsAuthenticationError(AndroidBackendAPIErrorCode api_error_code) {
-  switch (api_error_code) {
-    case AndroidBackendAPIErrorCode::kAuthErrorResolvable:
-    case AndroidBackendAPIErrorCode::kAuthErrorUnresolvable:
-      return true;
-    case AndroidBackendAPIErrorCode::kNetworkError:
-    case AndroidBackendAPIErrorCode::kInternalError:
-    case AndroidBackendAPIErrorCode::kDeveloperError:
-    case AndroidBackendAPIErrorCode::kApiNotConnected:
-    case AndroidBackendAPIErrorCode::kConnectionSuspendedDuringCall:
-    case AndroidBackendAPIErrorCode::kReconnectionTimedOut:
-    case AndroidBackendAPIErrorCode::kPassphraseRequired:
-    case AndroidBackendAPIErrorCode::kKeyRetrievalRequired:
-    case AndroidBackendAPIErrorCode::kAccessDenied:
-    case AndroidBackendAPIErrorCode::kBadRequest:
-    case AndroidBackendAPIErrorCode::kBackendGeneric:
-    case AndroidBackendAPIErrorCode::kBackendResourceExhausted:
-    case AndroidBackendAPIErrorCode::kInvalidData:
-    case AndroidBackendAPIErrorCode::kUnmappedErrorCode:
-    case AndroidBackendAPIErrorCode::kUnexpectedError:
-    case AndroidBackendAPIErrorCode::kChromeSyncAPICallError:
-    case AndroidBackendAPIErrorCode::kErrorWhileDoingLeakServiceGRPC:
-    case AndroidBackendAPIErrorCode::kRequiredSyncingAccountMissing:
-    case AndroidBackendAPIErrorCode::kLeakCheckServiceAuthError:
-    case AndroidBackendAPIErrorCode::kLeakCheckServiceResourceExhausted:
-      return false;
-  }
-  // The api_error_code is determined by static casting an int. It is thus
-  // possible for the value to not be among the explicit enum values, however
-  // that case should still be handled. Not adding a default statement to the
-  // switch, so that the compiler still warns when a new enum value is added and
-  // not explicitly handled here.
-  return false;
-}
-
-bool IsRetriableOperation(PasswordStoreOperation operation) {
-  switch (operation) {
-    case PasswordStoreOperation::kGetAllLoginsAsync:
-    case PasswordStoreOperation::kGetAutofillableLoginsAsync:
-      return true;
-    case PasswordStoreOperation::kGetAllLoginsForAccountAsync:
-    case PasswordStoreOperation::kFillMatchingLoginsAsync:
-    case PasswordStoreOperation::kAddLoginAsync:
-    case PasswordStoreOperation::kUpdateLoginAsync:
-    case PasswordStoreOperation::kRemoveLoginAsync:
-    case PasswordStoreOperation::kRemoveLoginsByURLAndTimeAsync:
-    case PasswordStoreOperation::kRemoveLoginsCreatedBetweenAsync:
-    case PasswordStoreOperation::kDisableAutoSignInForOriginsAsync:
-    case PasswordStoreOperation::kGetGroupedMatchingLoginsAsync:
-    case PasswordStoreOperation::kGetAllLoginsWithBrandingInfoAsync:
-      return false;
-  }
-  NOTREACHED() << "Operation code not handled";
-  return false;
-}
-
 std::string GetOperationName(PasswordStoreOperation operation) {
   switch (operation) {
     case PasswordStoreOperation::kGetAllLoginsAsync:
@@ -377,47 +322,80 @@
                                 attempt, kMaxReportedRetryAttempts);
 }
 
-bool IsUnrecoverableBackendError(
-    AndroidBackendAPIErrorCode api_error_code,
-    PasswordStoreOperation operation,
-    PasswordStoreAndroidBackendBridgeHelper* bridge_helper) {
-  if (password_manager_upm_eviction::ShouldRetryOnApiError(
-          static_cast<int>(api_error_code)) &&
-      IsRetriableOperation(operation)) {
-    // If the error and the operation are retriable, the error does not require
-    // any error-specific support and could be recovered.
-    // Retriable operations as they are defined at the moment should not result
-    // in eviction, not even if the retrying has timed out.
-    return false;
-  }
+enum class ActionOnApiError {
+  // See password_manager_upm_eviction::EvictCurrentUser().
+  kEvict,
+  // See prefs::kSavePasswordsSuspendedByError.
+  kDisableSaving,
+  kRetry,
+  kNone,
+};
 
-  if (api_error_code != AndroidBackendAPIErrorCode::kPassphraseRequired &&
-      bridge_helper->CanRemoveUnenrollment()) {
-    // If unenrollment can be removed, users can be evicted only if they
-    // encounter passphrase error.
-    return false;
+ActionOnApiError GetActionOnApiError(AndroidBackendAPIErrorCode api_error_code,
+                                     PasswordStoreOperation operation,
+                                     base::TimeDelta delay,
+                                     bool can_remove_unenrollment) {
+  switch (api_error_code) {
+    case AndroidBackendAPIErrorCode::kAuthErrorResolvable:
+    case AndroidBackendAPIErrorCode::kAuthErrorUnresolvable:
+      return ActionOnApiError::kDisableSaving;
+    case AndroidBackendAPIErrorCode::kPassphraseRequired: {
+      // kPassphraseRequired is treated separately from most errors leading to
+      // eviction - CanRemoveUnenrollment() branch below - to allow a rollout
+      // independent of kRemoveUPMUnenrollment.
+      // TODO(crbug.com/1511304): Send passphrase to platform client if flag
+      // enabled.
+      return ActionOnApiError::kEvict;
+    }
+    case AndroidBackendAPIErrorCode::kNetworkError:
+    case AndroidBackendAPIErrorCode::kApiNotConnected:
+    case AndroidBackendAPIErrorCode::kConnectionSuspendedDuringCall:
+    case AndroidBackendAPIErrorCode::kReconnectionTimedOut:
+    case AndroidBackendAPIErrorCode::kBackendGeneric:
+      if (operation == PasswordStoreOperation::kGetAllLoginsAsync ||
+          operation == PasswordStoreOperation::kGetAutofillableLoginsAsync) {
+        // This (error, operation) tuple is generally retriable. Still, impose
+        // a max retry timeout. If time ran out...
+        // - ...and unenrollment is present, the operation should still not
+        // result in eviction (historical artifact).
+        // - ...and unenrollment is gone, disable saving.
+        return delay < kTaskRetryTimeout
+                   ? ActionOnApiError::kRetry
+                   : (can_remove_unenrollment ? ActionOnApiError::kDisableSaving
+                                              : ActionOnApiError::kNone);
+      }
+      // Not retriable. Handle with other errors leading to eviction below.
+      ABSL_FALLTHROUGH_INTENDED;
+    case AndroidBackendAPIErrorCode::kInternalError:
+    case AndroidBackendAPIErrorCode::kDeveloperError:
+    case AndroidBackendAPIErrorCode::kAccessDenied:
+    case AndroidBackendAPIErrorCode::kBadRequest:
+    case AndroidBackendAPIErrorCode::kBackendResourceExhausted:
+    case AndroidBackendAPIErrorCode::kInvalidData:
+    case AndroidBackendAPIErrorCode::kUnmappedErrorCode:
+    case AndroidBackendAPIErrorCode::kUnexpectedError:
+    case AndroidBackendAPIErrorCode::kKeyRetrievalRequired:
+    case AndroidBackendAPIErrorCode::kChromeSyncAPICallError:
+    case AndroidBackendAPIErrorCode::kErrorWhileDoingLeakServiceGRPC:
+    case AndroidBackendAPIErrorCode::kRequiredSyncingAccountMissing:
+    case AndroidBackendAPIErrorCode::kLeakCheckServiceAuthError:
+    case AndroidBackendAPIErrorCode::kLeakCheckServiceResourceExhausted:
+      break;
   }
-
-  if (!password_manager_upm_eviction::ShouldIgnoreOnApiError(
-          static_cast<int>(api_error_code))) {
-    // If the error should not be ignored, it will immediately evict the user
-    // with no possibility to recover.
-    return true;
-  }
-
-  return false;
+  return can_remove_unenrollment ? ActionOnApiError::kDisableSaving
+                                 : ActionOnApiError::kEvict;
 }
 
 PasswordStoreBackendErrorType APIErrorCodeToErrorType(
     AndroidBackendAPIErrorCode api_error_code,
-    PasswordStoreAndroidBackendBridgeHelper* bridge_helper) {
+    bool can_remove_unenrollment) {
   switch (api_error_code) {
     case AndroidBackendAPIErrorCode::kAuthErrorResolvable:
       return PasswordStoreBackendErrorType::kAuthErrorResolvable;
     case AndroidBackendAPIErrorCode::kAuthErrorUnresolvable:
       return PasswordStoreBackendErrorType::kAuthErrorUnresolvable;
     case AndroidBackendAPIErrorCode::kKeyRetrievalRequired:
-      return bridge_helper->CanRemoveUnenrollment()
+      return can_remove_unenrollment
                  ? PasswordStoreBackendErrorType::kKeyRetrievalRequired
                  : PasswordStoreBackendErrorType::kUncategorized;
     case AndroidBackendAPIErrorCode::kNetworkError:
@@ -449,18 +427,11 @@
   return PasswordStoreBackendErrorType::kUncategorized;
 }
 
-bool ShouldRetryOperation(PasswordStoreOperation operation,
-                          int api_error,
-                          base::TimeDelta delay) {
-  return IsRetriableOperation(operation) &&
-         password_manager_upm_eviction::ShouldRetryOnApiError(api_error) &&
-         (delay < kTaskRetryTimeout);
-}
-
 PasswordStoreBackendError BackendErrorFromAndroidBackendError(
     const AndroidBackendError& error,
     PasswordStoreOperation operation,
-    PasswordStoreAndroidBackendBridgeHelper* bridge_helper) {
+    base::TimeDelta delay,
+    bool can_remove_unenrollment) {
   if (error.type != AndroidBackendErrorType::kExternalError) {
     return PasswordStoreBackendError(
         PasswordStoreBackendErrorType::kUncategorized,
@@ -478,21 +449,24 @@
   AndroidBackendAPIErrorCode api_error_code =
       static_cast<AndroidBackendAPIErrorCode>(error.api_error_code.value());
   PasswordStoreBackendErrorType error_type =
-      APIErrorCodeToErrorType(api_error_code, bridge_helper);
+      APIErrorCodeToErrorType(api_error_code, can_remove_unenrollment);
 
-  if (password_manager_upm_eviction::ShouldRetryOnApiError(
-          error.api_error_code.value())) {
-    return PasswordStoreBackendError(
-        error_type,
-        IsRetriableOperation(operation)
-            ? PasswordStoreBackendErrorRecoveryType::kRetriable
-            : PasswordStoreBackendErrorRecoveryType::kUnrecoverable);
+  switch (GetActionOnApiError(api_error_code, operation, delay,
+                              can_remove_unenrollment)) {
+    case ActionOnApiError::kRetry:
+      return PasswordStoreBackendError(
+          error_type, PasswordStoreBackendErrorRecoveryType::kRetriable);
+    case ActionOnApiError::kEvict:
+      return PasswordStoreBackendError(
+          error_type, PasswordStoreBackendErrorRecoveryType::kUnrecoverable);
+    // Counterintuitively, kDisableSaving is kRecoverable, as kUnrecoverable is
+    // reserved for eviction.
+    case ActionOnApiError::kDisableSaving:
+    case ActionOnApiError::kNone:
+      return PasswordStoreBackendError(
+          error_type, PasswordStoreBackendErrorRecoveryType::kRecoverable);
   }
-  PasswordStoreBackendErrorRecoveryType recovery_type =
-      IsUnrecoverableBackendError(api_error_code, operation, bridge_helper)
-          ? PasswordStoreBackendErrorRecoveryType::kUnrecoverable
-          : PasswordStoreBackendErrorRecoveryType::kRecoverable;
-  return PasswordStoreBackendError(error_type, recovery_type);
+  NOTREACHED_NORETURN();
 }
 
 }  // namespace
@@ -592,6 +566,7 @@
   // applies to the account store. Support needs to be specifically implemented
   // if desired. See crbug.com/1004777.
   CHECK(!sync_enabled_or_disabled_cb);
+  CHECK(completion);
   affiliated_match_helper_ = affiliated_match_helper;
   main_task_runner_ = base::SequencedTaskRunner::GetCurrentDefault();
   stored_passwords_changed_ = std::move(remote_form_changes_received);
@@ -599,7 +574,7 @@
       &PasswordStoreAndroidBackend::OnForegroundSessionStart,
       base::Unretained(this)));
   // TODO(https://crbug.com/1229650): Create subscription before completion.
-  std::move(completion).Run(/*success=*/true);
+  init_completion_callback_ = std::move(completion);
 }
 
 void PasswordStoreAndroidBackend::Shutdown(
@@ -613,6 +588,7 @@
 
 void PasswordStoreAndroidBackend::GetAllLoginsAsync(
     LoginsOrErrorReply callback) {
+  CHECK(!init_completion_callback_, base::NotFatalUntil::M123);
   GetAllLoginsForAccountInternal(GetSyncingAccount(sync_service_),
                                  std::move(callback),
                                  PasswordStoreOperation::kGetAllLoginsAsync,
@@ -621,6 +597,7 @@
 
 void PasswordStoreAndroidBackend::GetAllLoginsWithAffiliationAndBrandingAsync(
     LoginsOrErrorReply callback) {
+  CHECK(!init_completion_callback_, base::NotFatalUntil::M123);
   if (bridge_helper_->CanUseGetAllLoginsWithBrandingInfoAPI()) {
     JobId job_id = bridge_helper_->GetAllLoginsWithBrandingInfo(
         GetSyncingAccount(sync_service_));
@@ -641,6 +618,7 @@
 
 void PasswordStoreAndroidBackend::GetAutofillableLoginsAsync(
     LoginsOrErrorReply callback) {
+  CHECK(!init_completion_callback_, base::NotFatalUntil::M123);
   GetAutofillableLoginsInternal(
       GetSyncingAccount(sync_service_), std::move(callback),
       PasswordStoreOperation::kGetAutofillableLoginsAsync,
@@ -651,6 +629,7 @@
     std::string account,
     LoginsOrErrorReply callback) {
   CHECK(!account.empty());
+  CHECK(!init_completion_callback_, base::NotFatalUntil::M123);
 
   GetAllLoginsForAccountInternal(
       std::move(account), std::move(callback),
@@ -662,6 +641,7 @@
     LoginsOrErrorReply callback,
     bool include_psl,
     const std::vector<PasswordFormDigest>& forms) {
+  CHECK(!init_completion_callback_, base::NotFatalUntil::M123);
   if (forms.empty()) {
     std::move(callback).Run(LoginsResult());
     return;
@@ -695,6 +675,7 @@
 void PasswordStoreAndroidBackend::GetGroupedMatchingLoginsAsync(
     const PasswordFormDigest& form_digest,
     LoginsOrErrorReply callback) {
+  CHECK(!init_completion_callback_, base::NotFatalUntil::M123);
   if (bridge_helper_->CanUseGetAffiliatedPasswordsAPI()) {
     JobId job_id = bridge_helper_->GetAffiliatedLoginsForSignonRealm(
         form_digest.signon_realm, GetSyncingAccount(sync_service_));
@@ -713,6 +694,7 @@
 void PasswordStoreAndroidBackend::AddLoginAsync(
     const PasswordForm& form,
     PasswordChangesOrErrorReply callback) {
+  CHECK(!init_completion_callback_, base::NotFatalUntil::M123);
   PasswordForm sanitized_form = form;
   if (sanitized_form.blocked_by_user) {
     sanitized_form.username_value.clear();
@@ -728,6 +710,7 @@
 void PasswordStoreAndroidBackend::UpdateLoginAsync(
     const PasswordForm& form,
     PasswordChangesOrErrorReply callback) {
+  CHECK(!init_completion_callback_, base::NotFatalUntil::M123);
   PasswordForm sanitized_form = form;
   if (sanitized_form.blocked_by_user) {
     sanitized_form.username_value.clear();
@@ -740,6 +723,7 @@
 void PasswordStoreAndroidBackend::RemoveLoginAsync(
     const PasswordForm& form,
     PasswordChangesOrErrorReply callback) {
+  CHECK(!init_completion_callback_, base::NotFatalUntil::M123);
   RemoveLoginInternal(
       GetSyncingAccount(sync_service_), form, std::move(callback),
       PasswordStoreOperation::kRemoveLoginAsync, /*delay=*/base::Seconds(0));
@@ -751,6 +735,7 @@
     base::Time delete_end,
     base::OnceCallback<void(bool)> sync_completion,
     PasswordChangesOrErrorReply callback) {
+  CHECK(!init_completion_callback_, base::NotFatalUntil::M123);
   // Record metrics prior to invoking |callback|.
   PasswordChangesOrErrorReply record_metrics_and_reply =
       ReportMetricsAndInvokeCallbackForStoreModifications(
@@ -773,6 +758,7 @@
     base::Time delete_begin,
     base::Time delete_end,
     PasswordChangesOrErrorReply callback) {
+  CHECK(!init_completion_callback_, base::NotFatalUntil::M123);
   // Record metrics prior to invoking |callback|.
   PasswordChangesOrErrorReply record_metrics_and_reply =
       ReportMetricsAndInvokeCallbackForStoreModifications(
@@ -795,6 +781,7 @@
 void PasswordStoreAndroidBackend::DisableAutoSignInForOriginsAsync(
     const base::RepeatingCallback<bool(const GURL&)>& origin_filter,
     base::OnceClosure completion) {
+  CHECK(!init_completion_callback_, base::NotFatalUntil::M123);
   // TODO(https://crbug.com/1229655) Switch to using base::PassThrough to handle
   // this callback more gracefully when it's implemented.
   PasswordChangesOrErrorReply record_metrics_and_run_completion =
@@ -844,6 +831,12 @@
   sync_service_ = sync_service;
   sync_controller_delegate_->OnSyncServiceInitialized(sync_service);
 
+  // `PasswordStore` creation and initialization always happens before
+  // `SyncService` creation.
+  CHECK(init_completion_callback_);
+  // The backend is now considered fully functional.
+  std::move(init_completion_callback_).Run(/*success=*/true);
+
   // Stop fetching affiliations if AndroidBackend can be used and branding info
   // can be obtained directly from the GMS Core backend.
   if (!prefs_->GetBoolean(
@@ -973,9 +966,10 @@
   // The error to report is computed before potential eviction. This is because
   // eviction resets state which might be used to infer the recovery type of
   // the error.
+  base::TimeDelta delay = reply->GetDelay();
   PasswordStoreBackendError reported_error =
-      BackendErrorFromAndroidBackendError(error, operation,
-                                          bridge_helper_.get());
+      BackendErrorFromAndroidBackendError(
+          error, operation, delay, bridge_helper_->CanRemoveUnenrollment());
 
   if (error.api_error_code.has_value() && sync_service_) {
     // TODO(crbug.com/1324588): DCHECK_EQ(api_error_code,
@@ -989,58 +983,35 @@
 
     // Retry the call if the performed operation in combination with the error
     // was retriable and the time limit was not reached.
-    base::TimeDelta delay = reply->GetDelay();
-    if (ShouldRetryOperation(operation, api_error, delay)) {
-      RecordRetryHistograms(operation, api_error_code, delay);
-      switch (operation) {
-        case PasswordStoreOperation::kGetAllLoginsAsync:
-          RetryOperation(
-              base::BindOnce(
-                  &PasswordStoreAndroidBackend::GetAllLoginsForAccountInternal,
-                  weak_ptr_factory_.GetWeakPtr(),
-                  GetSyncingAccount(sync_service_),
-                  std::move(*reply).Get<LoginsOrErrorReply>(), operation),
-              delay);
-          return;
-        case PasswordStoreOperation::kGetAutofillableLoginsAsync:
-          RetryOperation(
-              base::BindOnce(
-                  &PasswordStoreAndroidBackend::GetAutofillableLoginsInternal,
-                  weak_ptr_factory_.GetWeakPtr(),
-                  GetSyncingAccount(sync_service_),
-                  std::move(*reply).Get<LoginsOrErrorReply>(), operation),
-              delay);
-          return;
-        case PasswordStoreOperation::kGetAllLoginsForAccountAsync:
-        case PasswordStoreOperation::kFillMatchingLoginsAsync:
-        case PasswordStoreOperation::kAddLoginAsync:
-        case PasswordStoreOperation::kUpdateLoginAsync:
-        case PasswordStoreOperation::kRemoveLoginAsync:
-        case PasswordStoreOperation::kRemoveLoginsByURLAndTimeAsync:
-        case PasswordStoreOperation::kRemoveLoginsCreatedBetweenAsync:
-        case PasswordStoreOperation::kDisableAutoSignInForOriginsAsync:
-        case PasswordStoreOperation::kGetGroupedMatchingLoginsAsync:
-        case PasswordStoreOperation::kGetAllLoginsWithBrandingInfoAsync:
-          NOTREACHED();
-          return;
+    switch (GetActionOnApiError(api_error_code, operation, delay,
+                                bridge_helper_->CanRemoveUnenrollment())) {
+      case ActionOnApiError::kRetry: {
+        RecordRetryHistograms(operation, api_error_code, delay);
+        CHECK(operation == PasswordStoreOperation::kGetAllLoginsAsync ||
+              operation == PasswordStoreOperation::kGetAutofillableLoginsAsync);
+        const auto method =
+            operation == PasswordStoreOperation::kGetAllLoginsAsync
+                ? &PasswordStoreAndroidBackend::GetAllLoginsForAccountInternal
+                : &PasswordStoreAndroidBackend::GetAutofillableLoginsInternal;
+        RetryOperation(
+            base::BindOnce(method, weak_ptr_factory_.GetWeakPtr(),
+                           GetSyncingAccount(sync_service_),
+                           std::move(*reply).Get<LoginsOrErrorReply>(),
+                           operation),
+            delay);
+        return;
       }
-    }
-
-    // If the user is experiencing an error unresolvable by Chrome or by the
-    // user, unenroll the user from the UPM experience.
-    if (password_manager::IsUnrecoverableBackendError(api_error_code, operation,
-                                                      bridge_helper_.get())) {
-      if (!password_manager_upm_eviction::IsCurrentUserEvicted(prefs_)) {
-        password_manager_upm_eviction::EvictCurrentUser(api_error, prefs_);
+      case ActionOnApiError::kEvict: {
+        if (!password_manager_upm_eviction::IsCurrentUserEvicted(prefs_)) {
+          password_manager_upm_eviction::EvictCurrentUser(api_error, prefs_);
+        }
+        break;
       }
-    } else if (IsAuthenticationError(api_error_code) ||
-               bridge_helper_->CanRemoveUnenrollment()) {
-      // Disable password manager if auth error is encountered or unenrollment
-      // can be removed.
-      prefs_->SetBoolean(prefs::kSavePasswordsSuspendedByError, true);
-      // Mark the error as recoverable to avoid fallbacks to the LoginDatabase.
-      reported_error.recovery_type =
-          PasswordStoreBackendErrorRecoveryType::kRecoverable;
+      case ActionOnApiError::kDisableSaving:
+        prefs_->SetBoolean(prefs::kSavePasswordsSuspendedByError, true);
+        break;
+      case ActionOnApiError::kNone:
+        break;
     }
   }
 
diff --git a/chrome/browser/password_manager/android/password_store_android_backend.h b/chrome/browser/password_manager/android/password_store_android_backend.h
index 2c74093..58955c4 100644
--- a/chrome/browser/password_manager/android/password_store_android_backend.h
+++ b/chrome/browser/password_manager/android/password_store_android_backend.h
@@ -10,6 +10,7 @@
 #include <unordered_map>
 
 #include "base/containers/small_map.h"
+#include "base/functional/callback_forward.h"
 #include "base/memory/raw_ptr.h"
 #include "base/memory/weak_ptr.h"
 #include "base/sequence_checker.h"
@@ -362,6 +363,10 @@
   // requests.
   std::unique_ptr<PasswordStoreAndroidBackendBridgeHelper> bridge_helper_;
 
+  // Callback to be invoked when the backend finished initializing with
+  // the success status of the initialization.
+  base::OnceCallback<void(bool)> init_completion_callback_;
+
   raw_ptr<const syncer::SyncService> sync_service_ = nullptr;
 
   raw_ptr<AffiliatedMatchHelper> affiliated_match_helper_;
diff --git a/chrome/browser/password_manager/android/password_store_android_backend_unittest.cc b/chrome/browser/password_manager/android/password_store_android_backend_unittest.cc
index 6d45a7b3..3225ee9 100644
--- a/chrome/browser/password_manager/android/password_store_android_backend_unittest.cc
+++ b/chrome/browser/password_manager/android/password_store_android_backend_unittest.cc
@@ -299,12 +299,14 @@
   TestingPrefServiceSimple prefs_;
 };
 
-TEST_F(PasswordStoreAndroidBackendTest, CallsCompletionCallbackAfterInit) {
+TEST_F(PasswordStoreAndroidBackendTest,
+       CallsCompletionCallbackAfterSyncServiceInitialized) {
   base::MockCallback<base::OnceCallback<void(bool)>> completion_callback;
-  EXPECT_CALL(completion_callback, Run(true));
   backend().InitBackend(/*affiliated_match_helper=*/nullptr,
                         PasswordStoreAndroidBackend::RemoteChangesReceived(),
                         base::NullCallback(), completion_callback.Get());
+  EXPECT_CALL(completion_callback, Run(true));
+  backend().OnSyncServiceInitialized(sync_service());
 }
 
 TEST_F(PasswordStoreAndroidBackendTest, CallsBridgeForLogins) {
@@ -331,6 +333,7 @@
   backend().InitBackend(/*affiliated_match_helper=*/nullptr,
                         PasswordStoreAndroidBackend::RemoteChangesReceived(),
                         base::NullCallback(), base::DoNothing());
+  backend().OnSyncServiceInitialized(sync_service());
   base::MockCallback<LoginsOrErrorReply> mock_reply;
 
   const JobId kFirstJobId{1337};
@@ -383,6 +386,7 @@
   backend().InitBackend(/*affiliated_match_helper=*/nullptr,
                         PasswordStoreAndroidBackend::RemoteChangesReceived(),
                         base::NullCallback(), base::DoNothing());
+  backend().OnSyncServiceInitialized(sync_service());
   base::MockCallback<LoginsOrErrorReply> mock_reply;
 
   const JobId kFirstJobId{1337};
@@ -434,6 +438,7 @@
   backend().InitBackend(/*affiliated_match_helper=*/nullptr,
                         PasswordStoreAndroidBackend::RemoteChangesReceived(),
                         base::NullCallback(), base::DoNothing());
+  backend().OnSyncServiceInitialized(sync_service());
   base::MockCallback<LoginsOrErrorReply> mock_reply;
 
   const JobId kFirstJobId{1337};
@@ -467,6 +472,7 @@
   backend().InitBackend(/*affiliated_match_helper=*/nullptr,
                         PasswordStoreAndroidBackend::RemoteChangesReceived(),
                         base::NullCallback(), base::DoNothing());
+  backend().OnSyncServiceInitialized(sync_service());
   base::MockCallback<LoginsOrErrorReply> mock_reply;
   EXPECT_CALL(*bridge_helper(), GetAutofillableLogins).WillOnce(Return(kJobId));
   backend().GetAutofillableLoginsAsync(mock_reply.Get());
@@ -482,6 +488,7 @@
   backend().InitBackend(/*affiliated_match_helper=*/nullptr,
                         PasswordStoreAndroidBackend::RemoteChangesReceived(),
                         base::NullCallback(), base::DoNothing());
+  backend().OnSyncServiceInitialized(sync_service());
   base::MockCallback<LoginsOrErrorReply> mock_reply;
   EXPECT_CALL(*bridge_helper(), GetAllLogins).WillOnce(Return(kJobId));
   std::string account = "mytestemail@gmail.com";
@@ -499,6 +506,7 @@
   backend().InitBackend(/*affiliated_match_helper=*/nullptr,
                         PasswordStoreAndroidBackend::RemoteChangesReceived(),
                         base::NullCallback(), base::DoNothing());
+  backend().OnSyncServiceInitialized(sync_service());
   const JobId kRemoveLoginJobId{13388};
   base::MockCallback<PasswordChangesOrErrorReply> mock_reply;
 
@@ -523,6 +531,7 @@
   backend().InitBackend(/*affiliated_match_helper=*/nullptr,
                         PasswordStoreAndroidBackend::RemoteChangesReceived(),
                         base::NullCallback(), base::DoNothing());
+  backend().OnSyncServiceInitialized(sync_service());
   base::MockCallback<PasswordChangesOrErrorReply> mock_deletion_reply;
   base::RepeatingCallback<bool(const GURL&)> url_filter = base::BindRepeating(
       [](const GURL& url) { return url == GURL(kTestUrl); });
@@ -576,6 +585,7 @@
   backend().InitBackend(/*affiliated_match_helper=*/nullptr,
                         PasswordStoreAndroidBackend::RemoteChangesReceived(),
                         base::NullCallback(), base::DoNothing());
+  backend().OnSyncServiceInitialized(sync_service());
   base::MockCallback<PasswordChangesOrErrorReply> mock_deletion_reply;
   base::Time delete_begin = base::Time::FromTimeT(1000);
   base::Time delete_end = base::Time::FromTimeT(2000);
@@ -679,6 +689,7 @@
   backend().InitBackend(/*affiliated_match_helper=*/nullptr,
                         PasswordStoreAndroidBackend::RemoteChangesReceived(),
                         base::NullCallback(), base::DoNothing());
+  backend().OnSyncServiceInitialized(sync_service());
   const JobId kUpdateLoginJobId{13388};
   base::MockCallback<PasswordChangesOrErrorReply> mock_reply;
   PasswordForm form =
@@ -910,7 +921,7 @@
   EXPECT_CALL(
       mock_reply,
       Run(ExpectError(PasswordStoreBackendErrorType::kUncategorized,
-                      PasswordStoreBackendErrorRecoveryType::kRetriable)));
+                      PasswordStoreBackendErrorRecoveryType::kRecoverable)));
   consumer().OnError(kJobId, CreateNetworkError());
 
   RunUntilIdle();
@@ -1294,6 +1305,7 @@
                         /*remote_form_changes_received=*/base::DoNothing(),
                         /*sync_enabled_or_disabled_cb=*/base::NullCallback(),
                         /*completion=*/base::DoNothing());
+  backend().OnSyncServiceInitialized(sync_service());
 
   base::MockCallback<PasswordChangesOrErrorReply> mock_reply;
   EXPECT_CALL(*bridge_helper(), AddLogin).WillOnce(Return(kJobId));
@@ -1341,6 +1353,7 @@
                         /*remote_form_changes_received=*/base::DoNothing(),
                         /*sync_enabled_or_disabled_cb=*/base::NullCallback(),
                         /*completion=*/base::DoNothing());
+  backend().OnSyncServiceInitialized(sync_service());
 
   base::MockCallback<PasswordChangesOrErrorReply> mock_reply;
   EXPECT_CALL(*bridge_helper(), AddLogin).WillOnce(Return(kJobId));
@@ -1366,6 +1379,9 @@
 
 TEST_F(PasswordStoreAndroidBackendTest,
        RecordActiveStatusOnSyncServiceInitialized) {
+  backend().InitBackend(/*affiliated_match_helper=*/nullptr,
+                        PasswordStoreAndroidBackend::RemoteChangesReceived(),
+                        base::NullCallback(), base::DoNothing());
   base::HistogramTester histogram_tester;
   sync_service()->GetUserSettings()->SetSelectedTypes(
       false, {syncer::UserSelectableType::kPasswords});
@@ -1376,6 +1392,9 @@
 
 TEST_F(PasswordStoreAndroidBackendTest, RecordInactiveStatusSyncOff) {
   base::HistogramTester histogram_tester;
+  backend().InitBackend(/*affiliated_match_helper=*/nullptr,
+                        PasswordStoreAndroidBackend::RemoteChangesReceived(),
+                        base::NullCallback(), base::DoNothing());
   sync_service()->GetUserSettings()->SetSelectedTypes(false, {});
   backend().OnSyncServiceInitialized(sync_service());
   histogram_tester.ExpectUniqueSample(
@@ -1384,6 +1403,9 @@
 }
 
 TEST_F(PasswordStoreAndroidBackendTest, RecordInactiveStatusUnenrolled) {
+  backend().InitBackend(/*affiliated_match_helper=*/nullptr,
+                        PasswordStoreAndroidBackend::RemoteChangesReceived(),
+                        base::NullCallback(), base::DoNothing());
   base::HistogramTester histogram_tester;
   sync_service()->GetUserSettings()->SetSelectedTypes(
       false, {syncer::UserSelectableType::kPasswords});
@@ -1403,6 +1425,7 @@
   backend().InitBackend(/*affiliated_match_helper=*/nullptr,
                         PasswordStoreAndroidBackend::RemoteChangesReceived(),
                         base::NullCallback(), base::DoNothing());
+  backend().OnSyncServiceInitialized(sync_service());
   base::MockCallback<LoginsOrErrorReply> mock_reply;
 
   const JobId kFirstJobId{1337};
@@ -1438,6 +1461,7 @@
   backend().InitBackend(&mock_affiliated_match_helper,
                         PasswordStoreAndroidBackend::RemoteChangesReceived(),
                         base::NullCallback(), base::DoNothing());
+  backend().OnSyncServiceInitialized(sync_service());
   base::MockCallback<LoginsOrErrorReply> mock_reply;
 
   const JobId kFirstJobId{1337};
@@ -1492,6 +1516,7 @@
   backend().InitBackend(/*affiliated_match_helper=*/nullptr,
                         PasswordStoreAndroidBackend::RemoteChangesReceived(),
                         base::NullCallback(), base::DoNothing());
+  backend().OnSyncServiceInitialized(sync_service());
   base::MockCallback<LoginsOrErrorReply> mock_reply;
   std::string TestURL1("https://example.com/");
   PasswordFormDigest form_digest(PasswordForm::Scheme::kHtml, TestURL1,
@@ -1548,6 +1573,7 @@
   backend().InitBackend(&mock_affiliated_match_helper,
                         PasswordStoreAndroidBackend::RemoteChangesReceived(),
                         base::NullCallback(), base::DoNothing());
+  backend().OnSyncServiceInitialized(sync_service());
   std::vector<MockAffiliatedMatchHelper::AffiliationAndBrandingInformation>
       affiliation_info_for_results = {
           {kTestUrl, kTestAndroidName, GURL(kTestAndroidIconURL)},
@@ -1594,6 +1620,7 @@
   backend().InitBackend(/*affiliated_match_helper=*/nullptr,
                         PasswordStoreAndroidBackend::RemoteChangesReceived(),
                         base::NullCallback(), base::DoNothing());
+  backend().OnSyncServiceInitialized(sync_service());
   base::MockCallback<LoginsOrErrorReply> mock_reply;
   std::string TestURL1("https://example.com/");
   PasswordFormDigest form_digest(PasswordForm::Scheme::kHtml, TestURL1,
@@ -1632,7 +1659,7 @@
   backend().OnSyncServiceInitialized(sync_service());
 }
 
-// Test suit to verify there is no unenrollment for most of the errors except
+// Test suite to verify there is no unenrollment for most of the errors except
 // Passphrase. Each backend operation is checked by a separate test.
 class PasswordStoreAndroidBackendWithoutUnenrollmentTest
     : public PasswordStoreAndroidBackendTest,
@@ -1656,8 +1683,13 @@
   }
 
   bool IsRetriableError() {
-    return password_manager_upm_eviction::ShouldRetryOnApiError(
-        static_cast<int>(GetParam().first));
+    const base::flat_set<AndroidBackendAPIErrorCode> kRetriableErrors = {
+        AndroidBackendAPIErrorCode::kNetworkError,
+        AndroidBackendAPIErrorCode::kApiNotConnected,
+        AndroidBackendAPIErrorCode::kConnectionSuspendedDuringCall,
+        AndroidBackendAPIErrorCode::kReconnectionTimedOut,
+        AndroidBackendAPIErrorCode::kBackendGeneric};
+    return kRetriableErrors.contains(GetParam().first);
   }
 
   PasswordStoreBackendErrorRecoveryType RecoveryType() {
@@ -1939,6 +1971,7 @@
                         PasswordStoreAndroidBackend::RemoteChangesReceived(),
                         /*sync_enabled_or_disabled_cb=*/base::NullCallback(),
                         /*completion=*/base::DoNothing());
+  backend().OnSyncServiceInitialized(sync_service());
 
   const char kGetAllLoginsMethodName[] = "GetAllLoginsAsync";
   const std::string kDurationMetric =
@@ -1985,6 +2018,7 @@
                         PasswordStoreAndroidBackend::RemoteChangesReceived(),
                         /*sync_enabled_or_disabled_cb=*/base::NullCallback(),
                         /*completion=*/base::DoNothing());
+  backend().OnSyncServiceInitialized(sync_service());
 
   const char kAddLoginMethodName[] = "AddLoginAsync";
   const std::string kDurationMetric = DurationMetricName(kAddLoginMethodName);
@@ -2031,6 +2065,7 @@
                         PasswordStoreAndroidBackend::RemoteChangesReceived(),
                         /*sync_enabled_or_disabled_cb=*/base::NullCallback(),
                         /*completion=*/base::DoNothing());
+  backend().OnSyncServiceInitialized(sync_service());
 
   const char kUpdateLoginMethodName[] = "UpdateLoginAsync";
   const std::string kDurationMetric =
@@ -2079,6 +2114,7 @@
                         PasswordStoreAndroidBackend::RemoteChangesReceived(),
                         /*sync_enabled_or_disabled_cb=*/base::NullCallback(),
                         /*completion=*/base::DoNothing());
+  backend().OnSyncServiceInitialized(sync_service());
 
   const char kRemoveLoginMethodName[] = "RemoveLoginAsync";
   const std::string kDurationMetric =
@@ -2127,6 +2163,7 @@
                         PasswordStoreAndroidBackend::RemoteChangesReceived(),
                         /*sync_enabled_or_disabled_cb=*/base::NullCallback(),
                         /*completion=*/base::DoNothing());
+  backend().OnSyncServiceInitialized(sync_service());
 
   const char kGetAutofillableLoginsMethodName[] = "GetAutofillableLoginsAsync";
   const std::string kDurationMetric =
diff --git a/chrome/browser/password_manager/android/password_store_proxy_backend.cc b/chrome/browser/password_manager/android/password_store_proxy_backend.cc
index 6df1a5e..142acd2 100644
--- a/chrome/browser/password_manager/android/password_store_proxy_backend.cc
+++ b/chrome/browser/password_manager/android/password_store_proxy_backend.cc
@@ -16,6 +16,7 @@
 #include "base/functional/callback_helpers.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/metrics/histogram_functions.h"
+#include "base/not_fatal_until.h"
 #include "base/notreached.h"
 #include "base/ranges/algorithm.h"
 #include "base/strings/strcat.h"
@@ -295,6 +296,7 @@
 void PasswordStoreProxyBackend::OnSyncServiceInitialized(
     syncer::SyncService* sync_service) {
   sync_service_ = sync_service;
+  sync_service_->AddObserver(this);
   android_backend_->OnSyncServiceInitialized(sync_service);
 }
 
@@ -338,6 +340,12 @@
                                            : android_backend_;
 }
 
+void PasswordStoreProxyBackend::OnSyncShutdown(
+    syncer::SyncService* sync_service) {
+  sync_service->RemoveObserver(this);
+  sync_service_ = nullptr;
+}
+
 void PasswordStoreProxyBackend::OnRemoteFormChangesReceived(
     CallbackOriginatesFromAndroidBackend originates_from_android,
     RemoteChangesReceived remote_form_changes_received,
@@ -351,6 +359,7 @@
 }
 
 bool PasswordStoreProxyBackend::UsesAndroidBackendAsMainBackend() {
+  CHECK(sync_service_, base::NotFatalUntil::M123);
   if (is_account_store_) {
     // The account store shouldn't be used unless the split happened.
     CHECK(password_manager_android_util::UsesSplitStoresAndUPMForLocal(prefs_));
diff --git a/chrome/browser/password_manager/android/password_store_proxy_backend.h b/chrome/browser/password_manager/android/password_store_proxy_backend.h
index ffc9ce1..3d2715b6 100644
--- a/chrome/browser/password_manager/android/password_store_proxy_backend.h
+++ b/chrome/browser/password_manager/android/password_store_proxy_backend.h
@@ -15,6 +15,8 @@
 #include "components/password_manager/core/browser/password_store/password_store.h"
 #include "components/password_manager/core/browser/password_store/password_store_backend.h"
 #include "components/password_manager/core/browser/password_store/password_store_change.h"
+#include "components/sync/service/sync_service.h"
+#include "components/sync/service/sync_service_observer.h"
 
 class PrefService;
 
@@ -23,7 +25,8 @@
 // This backend forwards requests to two backends in order to compare and record
 // their results and time differences. The main backend fulfills the  request
 // while the shadow backend is only queried to record shadow traffic.
-class PasswordStoreProxyBackend final : public PasswordStoreBackend {
+class PasswordStoreProxyBackend final : public PasswordStoreBackend,
+                                        public syncer::SyncServiceObserver {
  public:
   // `built_in_backend` and `android_backend` must not be null and must outlive
   // this object as long as Shutdown() is not called.
@@ -85,6 +88,8 @@
   void OnSyncServiceInitialized(syncer::SyncService* sync_service) override;
   base::WeakPtr<PasswordStoreBackend> AsWeakPtr() override;
 
+  void OnSyncShutdown(syncer::SyncService* sync_service) override;
+
   // Forwards the (possible) forms changes caused by a remote event to the
   // main backend.
   void OnRemoteFormChangesReceived(
@@ -92,11 +97,6 @@
       RemoteChangesReceived remote_form_changes_received,
       std::optional<PasswordStoreChangeList> changes);
 
-  // Forwards sync status changes by the backend facilitating them.
-  void OnSyncEnabledOrDisabled(
-      CallbackOriginatesFromAndroidBackend originatesFromAndroid,
-      base::RepeatingClosure sync_enabled_or_disabled_cb);
-
   // Helper used to determine main *and* fallback backends.
   // The account store doesn't use any fallback backend.
   // The profile store only uses the built-in backend as a fallback
@@ -132,7 +132,7 @@
   const raw_ptr<PasswordStoreBackend> built_in_backend_;
   const raw_ptr<PasswordStoreBackend> android_backend_;
   raw_ptr<PrefService> const prefs_ = nullptr;
-  raw_ptr<const syncer::SyncService> sync_service_ = nullptr;
+  raw_ptr<syncer::SyncService> sync_service_ = nullptr;
 
   IsAccountStore is_account_store_;
   base::WeakPtrFactory<PasswordStoreProxyBackend> weak_ptr_factory_{this};
diff --git a/chrome/browser/password_manager/android/password_store_proxy_backend_unittest.cc b/chrome/browser/password_manager/android/password_store_proxy_backend_unittest.cc
index 73223a4..433f47e 100644
--- a/chrome/browser/password_manager/android/password_store_proxy_backend_unittest.cc
+++ b/chrome/browser/password_manager/android/password_store_proxy_backend_unittest.cc
@@ -121,10 +121,6 @@
 
   void SetUp() override {
     proxy_backend_ = CreateProxyBackend();
-
-    // Initialize sync service.
-    EXPECT_CALL(android_backend(), OnSyncServiceInitialized(&sync_service_));
-    proxy_backend().OnSyncServiceInitialized(&sync_service_);
   }
 
   virtual std::unique_ptr<PasswordStoreProxyBackend> CreateProxyBackend() {
@@ -174,35 +170,55 @@
           WithArg<3>(Invoke([](base::OnceCallback<void(bool)> reply) -> void {
             std::move(reply).Run(true);
           })));
+
+  base::OnceCallback<void(bool)> captured_android_backend_reply;
   EXPECT_CALL(android_backend(), InitBackend)
       .WillOnce(
-          WithArg<3>(Invoke([](base::OnceCallback<void(bool)> reply) -> void {
-            std::move(reply).Run(true);
+          WithArg<3>(Invoke([&](base::OnceCallback<void(bool)> reply) -> void {
+            captured_android_backend_reply = std::move(reply);
           })));
-  EXPECT_CALL(completion_callback, Run(true));
+
   proxy_backend().InitBackend(nullptr, base::DoNothing(), base::DoNothing(),
                               completion_callback.Get());
+  // The android backend requires the sync service to be initialized before
+  // signaling that the backend initialization is complete.
+  EXPECT_CALL(completion_callback, Run(true));
+  EXPECT_CALL(android_backend(), OnSyncServiceInitialized(sync_service()))
+      .WillOnce(Invoke([&]() -> void {
+        std::move(captured_android_backend_reply).Run(true);
+      }));
+  proxy_backend().OnSyncServiceInitialized(sync_service());
 }
 
 TEST_F(PasswordStoreProxyBackendBaseTest,
        CallCompletionWithFailureForAnyError) {
   base::MockCallback<base::OnceCallback<void(bool)>> completion_callback;
 
-  // If one backend fails to initialize, the result of the second is irrelevant.
+  // If one backend fails to initialize, the result of the second is
+  // irrelevant.
   EXPECT_CALL(built_in_backend(), InitBackend)
       .WillOnce(
           WithArg<3>(Invoke([](base::OnceCallback<void(bool)> reply) -> void {
             std::move(reply).Run(false);
           })));
+  base::OnceCallback<void(bool)> captured_android_backend_reply;
   EXPECT_CALL(android_backend(), InitBackend)
       .Times(AtMost(1))
       .WillOnce(
-          WithArg<3>(Invoke([](base::OnceCallback<void(bool)> reply) -> void {
-            std::move(reply).Run(true);
+          WithArg<3>(Invoke([&](base::OnceCallback<void(bool)> reply) -> void {
+            captured_android_backend_reply = std::move(reply);
           })));
-  EXPECT_CALL(completion_callback, Run(false));
+
   proxy_backend().InitBackend(nullptr, base::DoNothing(), base::DoNothing(),
                               completion_callback.Get());
+  // The android backend requires the sync service to be initialized before
+  // signaling that the backend initialization is complete.
+  EXPECT_CALL(completion_callback, Run(false));
+  EXPECT_CALL(android_backend(), OnSyncServiceInitialized(sync_service()))
+      .WillOnce(Invoke([&]() -> void {
+        std::move(captured_android_backend_reply).Run(false);
+      }));
+  proxy_backend().OnSyncServiceInitialized(sync_service());
 }
 
 TEST_F(PasswordStoreProxyBackendBaseTest,
@@ -218,6 +234,8 @@
       .WillOnce(SaveArg<1>(&android_remote_changes_callback));
   proxy_backend().InitBackend(nullptr, original_callback.Get(),
                               base::DoNothing(), base::DoNothing());
+  EXPECT_CALL(android_backend(), OnSyncServiceInitialized(sync_service()));
+  proxy_backend().OnSyncServiceInitialized(sync_service());
 
   // With sync enabled, only the android backend calls the original callback.
   EnablePasswordSync();
@@ -258,6 +276,8 @@
       .WillOnce(SaveArg<1>(&android_remote_changes_callback));
   proxy_backend().InitBackend(nullptr, original_callback.Get(),
                               base::DoNothing(), base::DoNothing());
+  EXPECT_CALL(android_backend(), OnSyncServiceInitialized(sync_service()));
+  proxy_backend().OnSyncServiceInitialized(sync_service());
 
   // With sync enabled, only the android backend calls the original callback.
   EnablePasswordSync();
@@ -283,7 +303,8 @@
 
 TEST_F(PasswordStoreProxyBackendBaseTest,
        AccountCallRemoteChangesOnlyForMainBackend) {
-  // The account backend only exists if there is support for local passwords.
+  // The account store backend only exists if there is support for local
+  // passwords.
   prefs()->SetInteger(
       password_manager::prefs::kPasswordsUseUPMLocalAndSeparateStores,
       static_cast<int>(
@@ -306,8 +327,10 @@
 
   proxy_backend->InitBackend(nullptr, original_callback.Get(),
                              base::DoNothing(), base::DoNothing());
+  EXPECT_CALL(android_backend(), OnSyncServiceInitialized(sync_service()));
+  proxy_backend->OnSyncServiceInitialized(sync_service());
 
-  // The account backend is only active when sync is enabled.
+  // The account store backend is only active when sync is enabled.
   EnablePasswordSync();
 
   // Only the android backend should report that logins have changed to avoid
@@ -332,6 +355,8 @@
   EXPECT_CALL(android_backend(), InitBackend);
   proxy_backend().InitBackend(nullptr, base::DoNothing(),
                               original_callback.Get(), base::DoNothing());
+  EXPECT_CALL(android_backend(), OnSyncServiceInitialized(sync_service()));
+  proxy_backend().OnSyncServiceInitialized(sync_service());
 
   // With sync enabled, only the built-in backend calls the original callback.
   EnablePasswordSync();
@@ -368,6 +393,8 @@
           &built_in_backend_, &android_backend_, prefs(), IsAccountStore(true));
   proxy_backend->InitBackend(nullptr, base::DoNothing(),
                              original_callback.Get(), base::DoNothing());
+  EXPECT_CALL(android_backend(), OnSyncServiceInitialized(sync_service()));
+  proxy_backend->OnSyncServiceInitialized(sync_service());
 
   // With sync enabled, only the built-in backend calls the original callback.
   EnablePasswordSync();
@@ -400,6 +427,13 @@
  public:
   void SetUp() override {
     PasswordStoreProxyBackendBaseTest::SetUp();
+    EXPECT_CALL(android_backend(), InitBackend);
+    EXPECT_CALL(built_in_backend(), InitBackend);
+    proxy_backend().InitBackend(nullptr, base::DoNothing(), base::DoNothing(),
+                                base::DoNothing());
+    EXPECT_CALL(android_backend(), OnSyncServiceInitialized(sync_service()));
+    proxy_backend().OnSyncServiceInitialized(sync_service());
+
     if (GetParam().is_sync_enabled) {
       EnablePasswordSync();
     } else {
@@ -609,13 +643,6 @@
   proxy_backend().GetSmartBubbleStatsStore();
 }
 
-TEST_P(PasswordStoreProxyBackendTest,
-       OnSyncServiceInitializedPropagatedToAndroidBackend) {
-  syncer::TestSyncService sync_service;
-  EXPECT_CALL(android_backend(), OnSyncServiceInitialized(&sync_service));
-  proxy_backend().OnSyncServiceInitialized(&sync_service);
-}
-
 INSTANTIATE_TEST_SUITE_P(
     PasswordStoreProxyBackendBaseTest,
     PasswordStoreProxyBackendTest,
@@ -719,6 +746,12 @@
  public:
   void SetUp() override {
     PasswordStoreProxyBackendBaseTest::SetUp();
+    EXPECT_CALL(android_backend(), InitBackend);
+    EXPECT_CALL(built_in_backend(), InitBackend);
+    proxy_backend().InitBackend(nullptr, base::DoNothing(), base::DoNothing(),
+                                base::DoNothing());
+    EXPECT_CALL(android_backend(), OnSyncServiceInitialized(sync_service()));
+    proxy_backend().OnSyncServiceInitialized(sync_service());
     if (GetParam().is_using_split_account_local_stores) {
       prefs()->SetInteger(
           password_manager::prefs::kPasswordsUseUPMLocalAndSeparateStores,
diff --git a/chrome/browser/pdf/pdf_extension_accessibility_test.cc b/chrome/browser/pdf/pdf_extension_accessibility_test.cc
index 97f8f93..a22fcc4 100644
--- a/chrome/browser/pdf/pdf_extension_accessibility_test.cc
+++ b/chrome/browser/pdf/pdf_extension_accessibility_test.cc
@@ -180,7 +180,8 @@
     "      staticText '3'\n"
     "        inlineTextBox '3'\n";
 
-#if BUILDFLAG(IS_LINUX)
+#if BUILDFLAG(IS_LINUX) && !defined(MEMORY_SANITIZER) && \
+    !defined(ADDRESS_SANITIZER) && !defined(THREAD_SANITIZER)
 constexpr char kExpectedHelloWorldPDFAXTreeWithOcrResults[] =
     "pdfRoot 'PDF document containing 1 page'\n"
     "  banner\n"
@@ -217,7 +218,8 @@
     "  region 'Page 1'\n"
     "    paragraph\n"
     "      image 'Unlabeled image'\n";
-#endif  // BUILDFLAG(IS_LINUX)
+#endif  // BUILDFLAG(IS_LINUX) && !defined(MEMORY_SANITIZER) &&
+        // !defined(ADDRESS_SANITIZER) && !defined(THREAD_SANITIZER)
 
 }  // namespace
 
@@ -1247,7 +1249,10 @@
 INSTANTIATE_FEATURE_OVERRIDE_TEST_SUITE(PDFExtensionAccessibilityPdfOcrTest);
 #endif  // BUILDFLAG(ENABLE_SCREEN_AI_SERVICE)
 
-#if BUILDFLAG(IS_LINUX)
+// TODO(crbug.com/1516559): Add a dummy library that is built with Chrome for
+// sanitizer tests.
+#if BUILDFLAG(IS_LINUX) && !defined(MEMORY_SANITIZER) && \
+    !defined(ADDRESS_SANITIZER) && !defined(THREAD_SANITIZER)
 
 class ScreenAIInstallStateObserver
     : public screen_ai::ScreenAIInstallState::Observer {
@@ -1339,14 +1344,7 @@
   }
 };
 
-// TODO(crbug.com/1516559): Add a fake library for sanitizer tests.
-#if defined(MEMORY_SANITIZER) || defined(ADDRESS_SANITIZER) || \
-    defined(THREAD_SANITIZER)
-#define MAYBE_EnsureScreenAIInitializes DISABLED_EnsureScreenAIInitializes
-#else
-#define MAYBE_EnsureScreenAIInitializes EnsureScreenAIInitializes
-#endif
-IN_PROC_BROWSER_TEST_F(PDFOCRIntegrationTest, MAYBE_EnsureScreenAIInitializes) {
+IN_PROC_BROWSER_TEST_F(PDFOCRIntegrationTest, EnsureScreenAIInitializes) {
   ScreenAIInstallStateObserver observer;
 
   // Turn on PDF OCR by setting its pref to be true.
@@ -1360,14 +1358,7 @@
             screen_ai::ScreenAIInstallState::GetInstance()->get_state());
 }
 
-// TODO(crbug.com/1516559): Add a fake library for sanitizer tests.
-#if defined(MEMORY_SANITIZER) || defined(ADDRESS_SANITIZER) || \
-    defined(THREAD_SANITIZER)
-#define MAYBE_HelloWorld DISABLED_HelloWorld
-#else
-#define MAYBE_HelloWorld HelloWorld
-#endif
-IN_PROC_BROWSER_TEST_F(PDFOCRIntegrationTest, MAYBE_HelloWorld) {
+IN_PROC_BROWSER_TEST_F(PDFOCRIntegrationTest, HelloWorld) {
   ScreenAIInstallStateObserver observer;
 
   // Turn on PDF OCR by setting its pref to be true.
@@ -1397,15 +1388,7 @@
                          ax_tree_dump);
 }
 
-// TODO(crbug.com/1516559): Add a fake library for sanitizer tests.
-#if defined(MEMORY_SANITIZER) || defined(ADDRESS_SANITIZER) || \
-    defined(THREAD_SANITIZER)
-#define MAYBE_FeatureNotificationWhenOff DISABLED_FeatureNotificationWhenOff
-#else
-#define MAYBE_FeatureNotificationWhenOff FeatureNotificationWhenOff
-#endif
-IN_PROC_BROWSER_TEST_F(PDFOCRIntegrationTest,
-                       MAYBE_FeatureNotificationWhenOff) {
+IN_PROC_BROWSER_TEST_F(PDFOCRIntegrationTest, FeatureNotificationWhenOff) {
   ScreenAIInstallStateObserver observer;
 
   // Turn off PDF OCR by setting its pref to be false.
@@ -1435,15 +1418,7 @@
                          ax_tree_dump);
 }
 
-// TODO(crbug.com/1516559): Add a fake library for sanitizer tests.
-#if defined(MEMORY_SANITIZER) || defined(ADDRESS_SANITIZER) || \
-    defined(THREAD_SANITIZER)
-#define MAYBE_NoOcrResultOnBlankImagePdf DISABLED_NoOcrResultOnBlankImagePdf
-#else
-#define MAYBE_NoOcrResultOnBlankImagePdf NoOcrResultOnBlankImagePdf
-#endif
-IN_PROC_BROWSER_TEST_F(PDFOCRIntegrationTest,
-                       MAYBE_NoOcrResultOnBlankImagePdf) {
+IN_PROC_BROWSER_TEST_F(PDFOCRIntegrationTest, NoOcrResultOnBlankImagePdf) {
   ScreenAIInstallStateObserver observer;
 
   // Turn on PDF OCR by setting its pref to be false.
@@ -1474,4 +1449,5 @@
   ASSERT_MULTILINE_STREQ(kExpectedBlankPDFAXTreeWithPdfOcr, ax_tree_dump);
 }
 
-#endif  // BUILDFLAG(IS_LINUX)
+#endif  // BUILDFLAG(IS_LINUX) && !defined(MEMORY_SANITIZER) &&
+        // !defined(ADDRESS_SANITIZER) && !defined(THREAD_SANITIZER)
diff --git a/chrome/browser/performance_manager/decorators/helpers/page_live_state_decorator_helper_unittest.cc b/chrome/browser/performance_manager/decorators/helpers/page_live_state_decorator_helper_unittest.cc
index 99816c6..e7ecc56e 100644
--- a/chrome/browser/performance_manager/decorators/helpers/page_live_state_decorator_helper_unittest.cc
+++ b/chrome/browser/performance_manager/decorators/helpers/page_live_state_decorator_helper_unittest.cc
@@ -147,7 +147,8 @@
       media::mojom::DisplayMediaInformation::New(
           media::mojom::DisplayCaptureSurfaceType::MONITOR,
           /*logical_surface=*/true, media::mojom::CursorCaptureType::NEVER,
-          /*capture_handle=*/nullptr),
+          /*capture_handle=*/nullptr,
+          /*initial_zoom_level=*/100),
       &PageLiveStateDecorator::Data::IsCapturingDisplay);
 }
 
diff --git a/chrome/browser/permissions/chrome_permissions_client.cc b/chrome/browser/permissions/chrome_permissions_client.cc
index 602cb256..58b162e 100644
--- a/chrome/browser/permissions/chrome_permissions_client.cc
+++ b/chrome/browser/permissions/chrome_permissions_client.cc
@@ -290,7 +290,7 @@
           : std::nullopt;
 
   auto prompt_parameters =
-      permissions::PermissionHatsTriggerHelper::PromptParametersForHaTS(
+      permissions::PermissionHatsTriggerHelper::PromptParametersForHats(
           request_type, action, prompt_disposition, prompt_disposition_reason,
           gesture_type,
           std::string(version_info::GetChannelString(chrome::GetChannel())),
@@ -302,20 +302,19 @@
           recorded_gurl);
 
   if (!permissions::PermissionHatsTriggerHelper::
-          ArePromptTriggerCriteriaSatisfied(
-              prompt_parameters, kHatsSurveyTriggerPermissionsPrompt)) {
+          ArePromptTriggerCriteriaSatisfied(prompt_parameters)) {
     return;
   }
 
-  auto trigger_and_probability = permissions::PermissionHatsTriggerHelper::
-      GetPermissionPromptTriggerNameAndProbabilityForRequestType(
-          kHatsSurveyTriggerPermissionsPrompt,
-          permissions::PermissionUmaUtil::GetRequestTypeString(request_type));
+  std::optional<
+      permissions::PermissionHatsTriggerHelper::SurveyParametersForHats>
+      survey_parameters = permissions::PermissionHatsTriggerHelper::
+          GetSurveyParametersForRequestType(request_type);
 
   auto* hats_service =
       HatsServiceFactory::GetForProfile(profile,
                                         /*create_if_necessary=*/true);
-  if (!hats_service || !trigger_and_probability.has_value()) {
+  if (!hats_service || !survey_parameters.has_value()) {
     return;
   }
 
@@ -323,9 +322,12 @@
       SurveyProductSpecificData::PopulateFrom(prompt_parameters);
 
   hats_service->LaunchSurveyForWebContents(
-      trigger_and_probability->first, web_contents,
+      kHatsSurveyTriggerPermissionsPrompt, web_contents,
       survey_data.survey_bits_data, survey_data.survey_string_data,
-      std::move(hats_shown_callback), base::DoNothing());
+      std::move(hats_shown_callback), base::DoNothing(),
+      survey_parameters->supplied_trigger_id,
+      HatsService::SurveyOptions(survey_parameters->custom_survey_invitation,
+                                 survey_parameters->message_identifier));
 }
 
 #if !BUILDFLAG(IS_ANDROID)
diff --git a/chrome/browser/permissions/permission_hats_trigger_unittest.cc b/chrome/browser/permissions/permission_hats_trigger_unittest.cc
index f10ebf4..6b6d3b12 100644
--- a/chrome/browser/permissions/permission_hats_trigger_unittest.cc
+++ b/chrome/browser/permissions/permission_hats_trigger_unittest.cc
@@ -87,7 +87,7 @@
   EXPECT_TRUE(
       permissions::PermissionHatsTriggerHelper::
           ArePromptTriggerCriteriaSatisfied(
-              permissions::PermissionHatsTriggerHelper::PromptParametersForHaTS(
+              permissions::PermissionHatsTriggerHelper::PromptParametersForHats(
                   permissions::RequestType::kNotifications,
                   permissions::PermissionAction::GRANTED,
                   permissions::PermissionPromptDisposition::ANCHORED_BUBBLE,
@@ -97,14 +97,13 @@
                   permissions::kOnPromptResolved, base::Minutes(1),
                   permissions::PermissionHatsTriggerHelper::
                       OneTimePermissionPromptsDecidedBucket::BUCKET_0_1,
-                  trigger_gurl),
-              kHatsSurveyTriggerPermissionsPrompt));
+                  trigger_gurl)));
 
   // // Wrong action, should not trigger
   EXPECT_FALSE(
       permissions::PermissionHatsTriggerHelper::
           ArePromptTriggerCriteriaSatisfied(
-              permissions::PermissionHatsTriggerHelper::PromptParametersForHaTS(
+              permissions::PermissionHatsTriggerHelper::PromptParametersForHats(
                   permissions::RequestType::kNotifications,
                   permissions::PermissionAction::DENIED,
                   permissions::PermissionPromptDisposition::ANCHORED_BUBBLE,
@@ -114,14 +113,13 @@
                   permissions::kOnPromptResolved, base::Minutes(1),
                   permissions::PermissionHatsTriggerHelper::
                       OneTimePermissionPromptsDecidedBucket::BUCKET_0_1,
-                  trigger_gurl),
-              kHatsSurveyTriggerPermissionsPrompt));
+                  trigger_gurl)));
 
   // // Wrong request type, should not trigger
   EXPECT_FALSE(
       permissions::PermissionHatsTriggerHelper::
           ArePromptTriggerCriteriaSatisfied(
-              permissions::PermissionHatsTriggerHelper::PromptParametersForHaTS(
+              permissions::PermissionHatsTriggerHelper::PromptParametersForHats(
                   permissions::RequestType::kCameraStream,
                   permissions::PermissionAction::GRANTED,
                   permissions::PermissionPromptDisposition::ANCHORED_BUBBLE,
@@ -131,14 +129,13 @@
                   permissions::kOnPromptResolved, base::Minutes(1),
                   permissions::PermissionHatsTriggerHelper::
                       OneTimePermissionPromptsDecidedBucket::BUCKET_0_1,
-                  trigger_gurl),
-              kHatsSurveyTriggerPermissionsPrompt));
+                  trigger_gurl)));
 
   // Wrong prompt disposition, should not trigger
   EXPECT_FALSE(
       permissions::PermissionHatsTriggerHelper::
           ArePromptTriggerCriteriaSatisfied(
-              permissions::PermissionHatsTriggerHelper::PromptParametersForHaTS(
+              permissions::PermissionHatsTriggerHelper::PromptParametersForHats(
                   permissions::RequestType::kNotifications,
                   permissions::PermissionAction::GRANTED,
                   permissions::PermissionPromptDisposition::MESSAGE_UI,
@@ -148,14 +145,13 @@
                   permissions::kOnPromptResolved, base::Minutes(1),
                   permissions::PermissionHatsTriggerHelper::
                       OneTimePermissionPromptsDecidedBucket::BUCKET_0_1,
-                  trigger_gurl),
-              kHatsSurveyTriggerPermissionsPrompt));
+                  trigger_gurl)));
 
   // Wrong prompt disposition reason, should not trigger
   EXPECT_FALSE(
       permissions::PermissionHatsTriggerHelper::
           ArePromptTriggerCriteriaSatisfied(
-              permissions::PermissionHatsTriggerHelper::PromptParametersForHaTS(
+              permissions::PermissionHatsTriggerHelper::PromptParametersForHats(
                   permissions::RequestType::kNotifications,
                   permissions::PermissionAction::GRANTED,
                   permissions::PermissionPromptDisposition::ANCHORED_BUBBLE,
@@ -165,14 +161,13 @@
                   permissions::kOnPromptResolved, base::Minutes(1),
                   permissions::PermissionHatsTriggerHelper::
                       OneTimePermissionPromptsDecidedBucket::BUCKET_0_1,
-                  trigger_gurl),
-              kHatsSurveyTriggerPermissionsPrompt));
+                  trigger_gurl)));
 
   // No gesture, should not trigger
   EXPECT_FALSE(
       permissions::PermissionHatsTriggerHelper::
           ArePromptTriggerCriteriaSatisfied(
-              permissions::PermissionHatsTriggerHelper::PromptParametersForHaTS(
+              permissions::PermissionHatsTriggerHelper::PromptParametersForHats(
                   permissions::RequestType::kNotifications,
                   permissions::PermissionAction::GRANTED,
                   permissions::PermissionPromptDisposition::ANCHORED_BUBBLE,
@@ -182,14 +177,13 @@
                   permissions::kOnPromptResolved, base::Minutes(1),
                   permissions::PermissionHatsTriggerHelper::
                       OneTimePermissionPromptsDecidedBucket::BUCKET_0_1,
-                  trigger_gurl),
-              kHatsSurveyTriggerPermissionsPrompt));
+                  trigger_gurl)));
 
   // Wrong channel, should not trigger
   EXPECT_FALSE(
       permissions::PermissionHatsTriggerHelper::
           ArePromptTriggerCriteriaSatisfied(
-              permissions::PermissionHatsTriggerHelper::PromptParametersForHaTS(
+              permissions::PermissionHatsTriggerHelper::PromptParametersForHats(
                   permissions::RequestType::kNotifications,
                   permissions::PermissionAction::GRANTED,
                   permissions::PermissionPromptDisposition::ANCHORED_BUBBLE,
@@ -199,8 +193,7 @@
                   "stable", permissions::kOnPromptResolved, base::Minutes(1),
                   permissions::PermissionHatsTriggerHelper::
                       OneTimePermissionPromptsDecidedBucket::BUCKET_0_1,
-                  trigger_gurl),
-              kHatsSurveyTriggerPermissionsPrompt));
+                  trigger_gurl)));
 }
 
 TEST_F(PermissionHatsTriggerUnitTest, EmptyFiltersShouldAlwaysTrigger) {
@@ -222,7 +215,7 @@
   EXPECT_TRUE(
       permissions::PermissionHatsTriggerHelper::
           ArePromptTriggerCriteriaSatisfied(
-              permissions::PermissionHatsTriggerHelper::PromptParametersForHaTS(
+              permissions::PermissionHatsTriggerHelper::PromptParametersForHats(
                   permissions::RequestType::kNotifications,
                   permissions::PermissionAction::GRANTED,
                   permissions::PermissionPromptDisposition::ANCHORED_BUBBLE,
@@ -232,14 +225,13 @@
                   permissions::kOnPromptResolved, base::Minutes(1),
                   permissions::PermissionHatsTriggerHelper::
                       OneTimePermissionPromptsDecidedBucket::BUCKET_0_1,
-                  trigger_gurl),
-              kHatsSurveyTriggerPermissionsPrompt));
+                  trigger_gurl)));
 
   // Matching call, should trigger
   EXPECT_TRUE(
       permissions::PermissionHatsTriggerHelper::
           ArePromptTriggerCriteriaSatisfied(
-              permissions::PermissionHatsTriggerHelper::PromptParametersForHaTS(
+              permissions::PermissionHatsTriggerHelper::PromptParametersForHats(
                   permissions::RequestType::kNotifications,
                   permissions::PermissionAction::GRANTED_ONCE,
                   permissions::PermissionPromptDisposition::ANCHORED_BUBBLE,
@@ -249,14 +241,13 @@
                   permissions::kOnPromptResolved, base::Minutes(1),
                   permissions::PermissionHatsTriggerHelper::
                       OneTimePermissionPromptsDecidedBucket::BUCKET_0_1,
-                  trigger_gurl),
-              kHatsSurveyTriggerPermissionsPrompt));
+                  trigger_gurl)));
 
   // Matching call, should trigger
   EXPECT_TRUE(
       permissions::PermissionHatsTriggerHelper::
           ArePromptTriggerCriteriaSatisfied(
-              permissions::PermissionHatsTriggerHelper::PromptParametersForHaTS(
+              permissions::PermissionHatsTriggerHelper::PromptParametersForHats(
                   permissions::RequestType::kNotifications,
                   permissions::PermissionAction::DENIED,
                   permissions::PermissionPromptDisposition::ANCHORED_BUBBLE,
@@ -266,14 +257,13 @@
                   permissions::kOnPromptResolved, base::Minutes(1),
                   permissions::PermissionHatsTriggerHelper::
                       OneTimePermissionPromptsDecidedBucket::BUCKET_0_1,
-                  trigger_gurl),
-              kHatsSurveyTriggerPermissionsPrompt));
+                  trigger_gurl)));
 
   // Matching call, should trigger
   EXPECT_TRUE(
       permissions::PermissionHatsTriggerHelper::
           ArePromptTriggerCriteriaSatisfied(
-              permissions::PermissionHatsTriggerHelper::PromptParametersForHaTS(
+              permissions::PermissionHatsTriggerHelper::PromptParametersForHats(
                   permissions::RequestType::kNotifications,
                   permissions::PermissionAction::DISMISSED,
                   permissions::PermissionPromptDisposition::ANCHORED_BUBBLE,
@@ -283,14 +273,13 @@
                   permissions::kOnPromptResolved, base::Minutes(1),
                   permissions::PermissionHatsTriggerHelper::
                       OneTimePermissionPromptsDecidedBucket::BUCKET_0_1,
-                  trigger_gurl),
-              kHatsSurveyTriggerPermissionsPrompt));
+                  trigger_gurl)));
 
   // Matching call, should trigger
   EXPECT_TRUE(
       permissions::PermissionHatsTriggerHelper::
           ArePromptTriggerCriteriaSatisfied(
-              permissions::PermissionHatsTriggerHelper::PromptParametersForHaTS(
+              permissions::PermissionHatsTriggerHelper::PromptParametersForHats(
                   permissions::RequestType::kNotifications,
                   permissions::PermissionAction::IGNORED,
                   permissions::PermissionPromptDisposition::ANCHORED_BUBBLE,
@@ -300,8 +289,7 @@
                   permissions::kOnPromptResolved, base::Minutes(1),
                   permissions::PermissionHatsTriggerHelper::
                       OneTimePermissionPromptsDecidedBucket::BUCKET_0_1,
-                  trigger_gurl),
-              kHatsSurveyTriggerPermissionsPrompt));
+                  trigger_gurl)));
 }
 
 TEST_F(PermissionHatsTriggerUnitTest, CSVFiltersTriggerForAllConfiguredValues) {
@@ -321,7 +309,7 @@
   EXPECT_TRUE(
       permissions::PermissionHatsTriggerHelper::
           ArePromptTriggerCriteriaSatisfied(
-              permissions::PermissionHatsTriggerHelper::PromptParametersForHaTS(
+              permissions::PermissionHatsTriggerHelper::PromptParametersForHats(
                   permissions::RequestType::kNotifications,
                   permissions::PermissionAction::GRANTED,
                   permissions::PermissionPromptDisposition::ANCHORED_BUBBLE,
@@ -331,14 +319,13 @@
                   permissions::kOnPromptResolved, base::Minutes(1),
                   permissions::PermissionHatsTriggerHelper::
                       OneTimePermissionPromptsDecidedBucket::BUCKET_4_5,
-                  trigger_gurl),
-              kHatsSurveyTriggerPermissionsPrompt));
+                  trigger_gurl)));
 
   // Matching call, should trigger
   EXPECT_TRUE(
       permissions::PermissionHatsTriggerHelper::
           ArePromptTriggerCriteriaSatisfied(
-              permissions::PermissionHatsTriggerHelper::PromptParametersForHaTS(
+              permissions::PermissionHatsTriggerHelper::PromptParametersForHats(
                   permissions::RequestType::kNotifications,
                   permissions::PermissionAction::DISMISSED,
                   permissions::PermissionPromptDisposition::ANCHORED_BUBBLE,
@@ -348,14 +335,13 @@
                   permissions::kOnPromptResolved, base::Minutes(1),
                   permissions::PermissionHatsTriggerHelper::
                       OneTimePermissionPromptsDecidedBucket::BUCKET_0_1,
-                  trigger_gurl),
-              kHatsSurveyTriggerPermissionsPrompt));
+                  trigger_gurl)));
 
   // Wrong action, should not trigger
   EXPECT_FALSE(
       permissions::PermissionHatsTriggerHelper::
           ArePromptTriggerCriteriaSatisfied(
-              permissions::PermissionHatsTriggerHelper::PromptParametersForHaTS(
+              permissions::PermissionHatsTriggerHelper::PromptParametersForHats(
                   permissions::RequestType::kNotifications,
                   permissions::PermissionAction::DENIED,
                   permissions::PermissionPromptDisposition::ANCHORED_BUBBLE,
@@ -365,14 +351,13 @@
                   permissions::kOnPromptResolved, base::Minutes(1),
                   permissions::PermissionHatsTriggerHelper::
                       OneTimePermissionPromptsDecidedBucket::BUCKET_0_1,
-                  trigger_gurl),
-              kHatsSurveyTriggerPermissionsPrompt));
+                  trigger_gurl)));
 
   // Wrong action, should not trigger
   EXPECT_FALSE(
       permissions::PermissionHatsTriggerHelper::
           ArePromptTriggerCriteriaSatisfied(
-              permissions::PermissionHatsTriggerHelper::PromptParametersForHaTS(
+              permissions::PermissionHatsTriggerHelper::PromptParametersForHats(
                   permissions::RequestType::kNotifications,
                   permissions::PermissionAction::GRANTED_ONCE,
                   permissions::PermissionPromptDisposition::ANCHORED_BUBBLE,
@@ -382,14 +367,13 @@
                   permissions::kOnPromptResolved, base::Minutes(1),
                   permissions::PermissionHatsTriggerHelper::
                       OneTimePermissionPromptsDecidedBucket::BUCKET_0_1,
-                  trigger_gurl),
-              kHatsSurveyTriggerPermissionsPrompt));
+                  trigger_gurl)));
 
   // Wrong action, should not trigger
   EXPECT_FALSE(
       permissions::PermissionHatsTriggerHelper::
           ArePromptTriggerCriteriaSatisfied(
-              permissions::PermissionHatsTriggerHelper::PromptParametersForHaTS(
+              permissions::PermissionHatsTriggerHelper::PromptParametersForHats(
                   permissions::RequestType::kNotifications,
                   permissions::PermissionAction::IGNORED,
                   permissions::PermissionPromptDisposition::ANCHORED_BUBBLE,
@@ -399,14 +383,13 @@
                   permissions::kOnPromptResolved, base::Minutes(1),
                   permissions::PermissionHatsTriggerHelper::
                       OneTimePermissionPromptsDecidedBucket::BUCKET_0_1,
-                  trigger_gurl),
-              kHatsSurveyTriggerPermissionsPrompt));
+                  trigger_gurl)));
 
   // Wrong one time prompt count bucket, should not trigger
   EXPECT_FALSE(
       permissions::PermissionHatsTriggerHelper::
           ArePromptTriggerCriteriaSatisfied(
-              permissions::PermissionHatsTriggerHelper::PromptParametersForHaTS(
+              permissions::PermissionHatsTriggerHelper::PromptParametersForHats(
                   permissions::RequestType::kNotifications,
                   permissions::PermissionAction::DISMISSED,
                   permissions::PermissionPromptDisposition::ANCHORED_BUBBLE,
@@ -416,8 +399,7 @@
                   permissions::kOnPromptResolved, base::Minutes(1),
                   permissions::PermissionHatsTriggerHelper::
                       OneTimePermissionPromptsDecidedBucket::BUCKET_6_10,
-                  trigger_gurl),
-              kHatsSurveyTriggerPermissionsPrompt));
+                  trigger_gurl)));
 }
 
 TEST_F(PermissionHatsTriggerUnitTest, FilterConfigurationHandlesEdgeCases) {
@@ -437,7 +419,7 @@
   EXPECT_TRUE(
       permissions::PermissionHatsTriggerHelper::
           ArePromptTriggerCriteriaSatisfied(
-              permissions::PermissionHatsTriggerHelper::PromptParametersForHaTS(
+              permissions::PermissionHatsTriggerHelper::PromptParametersForHats(
                   permissions::RequestType::kNotifications,
                   permissions::PermissionAction::GRANTED,
                   permissions::PermissionPromptDisposition::ANCHORED_BUBBLE,
@@ -447,14 +429,13 @@
                   permissions::kOnPromptResolved, base::Minutes(1),
                   permissions::PermissionHatsTriggerHelper::
                       OneTimePermissionPromptsDecidedBucket::BUCKET_0_1,
-                  trigger_gurl),
-              kHatsSurveyTriggerPermissionsPrompt));
+                  trigger_gurl)));
 
   // Matching call, should trigger
   EXPECT_TRUE(
       permissions::PermissionHatsTriggerHelper::
           ArePromptTriggerCriteriaSatisfied(
-              permissions::PermissionHatsTriggerHelper::PromptParametersForHaTS(
+              permissions::PermissionHatsTriggerHelper::PromptParametersForHats(
                   permissions::RequestType::kGeolocation,
                   permissions::PermissionAction::GRANTED,
                   permissions::PermissionPromptDisposition::ANCHORED_BUBBLE,
@@ -464,8 +445,7 @@
                   permissions::kOnPromptResolved, base::Minutes(1),
                   permissions::PermissionHatsTriggerHelper::
                       OneTimePermissionPromptsDecidedBucket::BUCKET_0_1,
-                  trigger_gurl),
-              kHatsSurveyTriggerPermissionsPrompt));
+                  trigger_gurl)));
 }
 
 TEST_F(PermissionHatsTriggerUnitTest, ProductSpecificFieldsAreReported) {
@@ -480,7 +460,7 @@
 
   auto survey_data = permissions::PermissionHatsTriggerHelper::
       SurveyProductSpecificData::PopulateFrom(
-          permissions::PermissionHatsTriggerHelper::PromptParametersForHaTS(
+          permissions::PermissionHatsTriggerHelper::PromptParametersForHats(
               permissions::RequestType::kNotifications,
               permissions::PermissionAction::GRANTED,
               permissions::PermissionPromptDisposition::ANCHORED_BUBBLE,
@@ -535,7 +515,7 @@
   EXPECT_TRUE(
       permissions::PermissionHatsTriggerHelper::
           ArePromptTriggerCriteriaSatisfied(
-              permissions::PermissionHatsTriggerHelper::PromptParametersForHaTS(
+              permissions::PermissionHatsTriggerHelper::PromptParametersForHats(
                   permissions::RequestType::kNotifications,
                   permissions::PermissionAction::IGNORED,
                   permissions::PermissionPromptDisposition::ANCHORED_BUBBLE,
@@ -545,15 +525,14 @@
                   permissions::kOnPromptResolved, base::Minutes(5),
                   permissions::PermissionHatsTriggerHelper::
                       OneTimePermissionPromptsDecidedBucket::BUCKET_0_1,
-                  trigger_gurl),
-              kHatsSurveyTriggerPermissionsPrompt));
+                  trigger_gurl)));
 
   // The safeguard is active, and the display time is higher than the configured
   // value. Thus, this should not trigger.
   EXPECT_FALSE(
       permissions::PermissionHatsTriggerHelper::
           ArePromptTriggerCriteriaSatisfied(
-              permissions::PermissionHatsTriggerHelper::PromptParametersForHaTS(
+              permissions::PermissionHatsTriggerHelper::PromptParametersForHats(
                   permissions::RequestType::kNotifications,
                   permissions::PermissionAction::IGNORED,
                   permissions::PermissionPromptDisposition::ANCHORED_BUBBLE,
@@ -563,8 +542,7 @@
                   permissions::kOnPromptResolved, base::Minutes(15),
                   permissions::PermissionHatsTriggerHelper::
                       OneTimePermissionPromptsDecidedBucket::BUCKET_0_1,
-                  trigger_gurl),
-              kHatsSurveyTriggerPermissionsPrompt));
+                  trigger_gurl)));
 }
 
 TEST_F(PermissionHatsTriggerUnitTest, VerifyUnconfiguredFiltersSafeguard) {
@@ -583,7 +561,7 @@
   EXPECT_FALSE(
       permissions::PermissionHatsTriggerHelper::
           ArePromptTriggerCriteriaSatisfied(
-              permissions::PermissionHatsTriggerHelper::PromptParametersForHaTS(
+              permissions::PermissionHatsTriggerHelper::PromptParametersForHats(
                   permissions::RequestType::kNotifications,
                   permissions::PermissionAction::IGNORED,
                   permissions::PermissionPromptDisposition::ANCHORED_BUBBLE,
@@ -593,13 +571,7 @@
                   permissions::kOnPromptResolved, base::Minutes(1),
                   permissions::PermissionHatsTriggerHelper::
                       OneTimePermissionPromptsDecidedBucket::BUCKET_0_1,
-                  trigger_gurl),
-              permissions::PermissionHatsTriggerHelper::
-                  GetPermissionPromptTriggerNameAndProbabilityForRequestType(
-                      kHatsSurveyTriggerPermissionsPrompt,
-                      permissions::PermissionUmaUtil::GetRequestTypeString(
-                          permissions::RequestType::kNotifications))
-                      ->first));
+                  trigger_gurl)));
 }
 
 TEST_F(PermissionHatsTriggerUnitTest, VerifyMisconfiguredFiltersSafeguard) {
@@ -616,7 +588,7 @@
   EXPECT_FALSE(
       permissions::PermissionHatsTriggerHelper::
           ArePromptTriggerCriteriaSatisfied(
-              permissions::PermissionHatsTriggerHelper::PromptParametersForHaTS(
+              permissions::PermissionHatsTriggerHelper::PromptParametersForHats(
                   permissions::RequestType::kNotifications,
                   permissions::PermissionAction::IGNORED,
                   permissions::PermissionPromptDisposition::ANCHORED_BUBBLE,
@@ -626,8 +598,7 @@
                   permissions::kOnPromptResolved, base::Minutes(1),
                   permissions::PermissionHatsTriggerHelper::
                       OneTimePermissionPromptsDecidedBucket::BUCKET_0_1,
-                  trigger_gurl),
-              kHatsSurveyTriggerPermissionsPrompt));
+                  trigger_gurl)));
 }
 
 TEST_F(PermissionHatsTriggerUnitTest, MultipleTriggersShouldWorkCorrectly) {
@@ -642,7 +613,7 @@
   EXPECT_TRUE(
       permissions::PermissionHatsTriggerHelper::
           ArePromptTriggerCriteriaSatisfied(
-              permissions::PermissionHatsTriggerHelper::PromptParametersForHaTS(
+              permissions::PermissionHatsTriggerHelper::PromptParametersForHats(
                   permissions::RequestType::kGeolocation,
                   permissions::PermissionAction::GRANTED,
                   permissions::PermissionPromptDisposition::ANCHORED_BUBBLE,
@@ -652,14 +623,13 @@
                   permissions::kOnPromptResolved, base::Minutes(1),
                   permissions::PermissionHatsTriggerHelper::
                       OneTimePermissionPromptsDecidedBucket::BUCKET_4_5,
-                  trigger_gurl),
-              kHatsSurveyTriggerPermissionsPrompt));
+                  trigger_gurl)));
 
   // Matching call, should trigger
   EXPECT_TRUE(
       permissions::PermissionHatsTriggerHelper::
           ArePromptTriggerCriteriaSatisfied(
-              permissions::PermissionHatsTriggerHelper::PromptParametersForHaTS(
+              permissions::PermissionHatsTriggerHelper::PromptParametersForHats(
                   permissions::RequestType::kMicStream,
                   permissions::PermissionAction::GRANTED,
                   permissions::PermissionPromptDisposition::ANCHORED_BUBBLE,
@@ -669,15 +639,14 @@
                   permissions::kOnPromptResolved, base::Minutes(1),
                   permissions::PermissionHatsTriggerHelper::
                       OneTimePermissionPromptsDecidedBucket::BUCKET_4_5,
-                  trigger_gurl),
-              kHatsSurveyTriggerPermissionsPrompt));
+                  trigger_gurl)));
 
   // Matching call, but 0.0 probability configured for camera, should not
   // trigger
   EXPECT_FALSE(
       permissions::PermissionHatsTriggerHelper::
           ArePromptTriggerCriteriaSatisfied(
-              permissions::PermissionHatsTriggerHelper::PromptParametersForHaTS(
+              permissions::PermissionHatsTriggerHelper::PromptParametersForHats(
                   permissions::RequestType::kCameraStream,
                   permissions::PermissionAction::DISMISSED,
                   permissions::PermissionPromptDisposition::ANCHORED_BUBBLE,
@@ -687,14 +656,13 @@
                   permissions::kOnPromptResolved, base::Minutes(1),
                   permissions::PermissionHatsTriggerHelper::
                       OneTimePermissionPromptsDecidedBucket::BUCKET_0_1,
-                  trigger_gurl),
-              kHatsSurveyTriggerPermissionsPrompt));
+                  trigger_gurl)));
 
   // Request type doesn't match, should not trigger
   EXPECT_FALSE(
       permissions::PermissionHatsTriggerHelper::
           ArePromptTriggerCriteriaSatisfied(
-              permissions::PermissionHatsTriggerHelper::PromptParametersForHaTS(
+              permissions::PermissionHatsTriggerHelper::PromptParametersForHats(
                   permissions::RequestType::kCameraPanTiltZoom,
                   permissions::PermissionAction::DISMISSED,
                   permissions::PermissionPromptDisposition::ANCHORED_BUBBLE,
@@ -704,8 +672,7 @@
                   permissions::kOnPromptResolved, base::Minutes(1),
                   permissions::PermissionHatsTriggerHelper::
                       OneTimePermissionPromptsDecidedBucket::BUCKET_0_1,
-                  trigger_gurl),
-              kHatsSurveyTriggerPermissionsPrompt));
+                  trigger_gurl)));
 }
 
 TEST_F(PermissionHatsTriggerUnitTest,
@@ -726,7 +693,7 @@
   EXPECT_FALSE(
       permissions::PermissionHatsTriggerHelper::
           ArePromptTriggerCriteriaSatisfied(
-              permissions::PermissionHatsTriggerHelper::PromptParametersForHaTS(
+              permissions::PermissionHatsTriggerHelper::PromptParametersForHats(
                   permissions::RequestType::kGeolocation,
                   permissions::PermissionAction::GRANTED,
                   permissions::PermissionPromptDisposition::ANCHORED_BUBBLE,
@@ -736,13 +703,12 @@
                   permissions::kOnPromptResolved, base::Minutes(1),
                   permissions::PermissionHatsTriggerHelper::
                       OneTimePermissionPromptsDecidedBucket::BUCKET_4_5,
-                  trigger_gurl),
-              kHatsSurveyTriggerPermissionsPrompt));
+                  trigger_gurl)));
 
   EXPECT_FALSE(
       permissions::PermissionHatsTriggerHelper::
           ArePromptTriggerCriteriaSatisfied(
-              permissions::PermissionHatsTriggerHelper::PromptParametersForHaTS(
+              permissions::PermissionHatsTriggerHelper::PromptParametersForHats(
                   permissions::RequestType::kMicStream,
                   permissions::PermissionAction::GRANTED,
                   permissions::PermissionPromptDisposition::ANCHORED_BUBBLE,
@@ -752,13 +718,12 @@
                   permissions::kOnPromptResolved, base::Minutes(1),
                   permissions::PermissionHatsTriggerHelper::
                       OneTimePermissionPromptsDecidedBucket::BUCKET_4_5,
-                  trigger_gurl),
-              kHatsSurveyTriggerPermissionsPrompt));
+                  trigger_gurl)));
 
   EXPECT_FALSE(
       permissions::PermissionHatsTriggerHelper::
           ArePromptTriggerCriteriaSatisfied(
-              permissions::PermissionHatsTriggerHelper::PromptParametersForHaTS(
+              permissions::PermissionHatsTriggerHelper::PromptParametersForHats(
                   permissions::RequestType::kCameraStream,
                   permissions::PermissionAction::DISMISSED,
                   permissions::PermissionPromptDisposition::ANCHORED_BUBBLE,
@@ -768,8 +733,7 @@
                   permissions::kOnPromptResolved, base::Minutes(1),
                   permissions::PermissionHatsTriggerHelper::
                       OneTimePermissionPromptsDecidedBucket::BUCKET_0_1,
-                  trigger_gurl),
-              kHatsSurveyTriggerPermissionsPrompt));
+                  trigger_gurl)));
 }
 
 TEST_F(PermissionHatsTriggerUnitTest,
@@ -785,7 +749,7 @@
   EXPECT_FALSE(
       permissions::PermissionHatsTriggerHelper::
           ArePromptTriggerCriteriaSatisfied(
-              permissions::PermissionHatsTriggerHelper::PromptParametersForHaTS(
+              permissions::PermissionHatsTriggerHelper::PromptParametersForHats(
                   permissions::RequestType::kGeolocation,
                   permissions::PermissionAction::GRANTED,
                   permissions::PermissionPromptDisposition::ANCHORED_BUBBLE,
@@ -795,13 +759,12 @@
                   permissions::kOnPromptResolved, base::Minutes(1),
                   permissions::PermissionHatsTriggerHelper::
                       OneTimePermissionPromptsDecidedBucket::BUCKET_4_5,
-                  trigger_gurl),
-              kHatsSurveyTriggerPermissionsPrompt));
+                  trigger_gurl)));
 
   EXPECT_FALSE(
       permissions::PermissionHatsTriggerHelper::
           ArePromptTriggerCriteriaSatisfied(
-              permissions::PermissionHatsTriggerHelper::PromptParametersForHaTS(
+              permissions::PermissionHatsTriggerHelper::PromptParametersForHats(
                   permissions::RequestType::kMicStream,
                   permissions::PermissionAction::GRANTED,
                   permissions::PermissionPromptDisposition::ANCHORED_BUBBLE,
@@ -811,13 +774,12 @@
                   permissions::kOnPromptResolved, base::Minutes(1),
                   permissions::PermissionHatsTriggerHelper::
                       OneTimePermissionPromptsDecidedBucket::BUCKET_4_5,
-                  trigger_gurl),
-              kHatsSurveyTriggerPermissionsPrompt));
+                  trigger_gurl)));
 
   EXPECT_FALSE(
       permissions::PermissionHatsTriggerHelper::
           ArePromptTriggerCriteriaSatisfied(
-              permissions::PermissionHatsTriggerHelper::PromptParametersForHaTS(
+              permissions::PermissionHatsTriggerHelper::PromptParametersForHats(
                   permissions::RequestType::kCameraStream,
                   permissions::PermissionAction::DISMISSED,
                   permissions::PermissionPromptDisposition::ANCHORED_BUBBLE,
@@ -827,8 +789,7 @@
                   permissions::kOnPromptResolved, base::Minutes(1),
                   permissions::PermissionHatsTriggerHelper::
                       OneTimePermissionPromptsDecidedBucket::BUCKET_0_1,
-                  trigger_gurl),
-              kHatsSurveyTriggerPermissionsPrompt));
+                  trigger_gurl)));
 }
 
 TEST_F(PermissionHatsTriggerUnitTest,
@@ -843,7 +804,7 @@
   EXPECT_FALSE(
       permissions::PermissionHatsTriggerHelper::
           ArePromptTriggerCriteriaSatisfied(
-              permissions::PermissionHatsTriggerHelper::PromptParametersForHaTS(
+              permissions::PermissionHatsTriggerHelper::PromptParametersForHats(
                   permissions::RequestType::kGeolocation,
                   permissions::PermissionAction::GRANTED,
                   permissions::PermissionPromptDisposition::ANCHORED_BUBBLE,
@@ -853,13 +814,12 @@
                   permissions::kOnPromptResolved, base::Minutes(1),
                   permissions::PermissionHatsTriggerHelper::
                       OneTimePermissionPromptsDecidedBucket::BUCKET_4_5,
-                  trigger_gurl),
-              kHatsSurveyTriggerPermissionsPrompt));
+                  trigger_gurl)));
 
   EXPECT_FALSE(
       permissions::PermissionHatsTriggerHelper::
           ArePromptTriggerCriteriaSatisfied(
-              permissions::PermissionHatsTriggerHelper::PromptParametersForHaTS(
+              permissions::PermissionHatsTriggerHelper::PromptParametersForHats(
                   permissions::RequestType::kMicStream,
                   permissions::PermissionAction::GRANTED,
                   permissions::PermissionPromptDisposition::ANCHORED_BUBBLE,
@@ -869,13 +829,12 @@
                   permissions::kOnPromptResolved, base::Minutes(1),
                   permissions::PermissionHatsTriggerHelper::
                       OneTimePermissionPromptsDecidedBucket::BUCKET_4_5,
-                  trigger_gurl),
-              kHatsSurveyTriggerPermissionsPrompt));
+                  trigger_gurl)));
 
   EXPECT_FALSE(
       permissions::PermissionHatsTriggerHelper::
           ArePromptTriggerCriteriaSatisfied(
-              permissions::PermissionHatsTriggerHelper::PromptParametersForHaTS(
+              permissions::PermissionHatsTriggerHelper::PromptParametersForHats(
                   permissions::RequestType::kCameraStream,
                   permissions::PermissionAction::DISMISSED,
                   permissions::PermissionPromptDisposition::ANCHORED_BUBBLE,
@@ -885,8 +844,7 @@
                   permissions::kOnPromptResolved, base::Minutes(1),
                   permissions::PermissionHatsTriggerHelper::
                       OneTimePermissionPromptsDecidedBucket::BUCKET_0_1,
-                  trigger_gurl),
-              kHatsSurveyTriggerPermissionsPrompt));
+                  trigger_gurl)));
 }
 
 TEST_F(PermissionHatsTriggerUnitTest,
@@ -902,7 +860,7 @@
   EXPECT_FALSE(
       permissions::PermissionHatsTriggerHelper::
           ArePromptTriggerCriteriaSatisfied(
-              permissions::PermissionHatsTriggerHelper::PromptParametersForHaTS(
+              permissions::PermissionHatsTriggerHelper::PromptParametersForHats(
                   permissions::RequestType::kMicStream,
                   permissions::PermissionAction::GRANTED,
                   permissions::PermissionPromptDisposition::ANCHORED_BUBBLE,
@@ -912,8 +870,7 @@
                   permissions::kOnPromptResolved, base::Minutes(1),
                   permissions::PermissionHatsTriggerHelper::
                       OneTimePermissionPromptsDecidedBucket::BUCKET_4_5,
-                  trigger_gurl),
-              kHatsSurveyTriggerPermissionsPrompt));
+                  trigger_gurl)));
 }
 
 TEST_F(PermissionHatsTriggerUnitTest,
@@ -929,7 +886,7 @@
   EXPECT_TRUE(
       permissions::PermissionHatsTriggerHelper::
           ArePromptTriggerCriteriaSatisfied(
-              permissions::PermissionHatsTriggerHelper::PromptParametersForHaTS(
+              permissions::PermissionHatsTriggerHelper::PromptParametersForHats(
                   permissions::RequestType::kMicStream,
                   permissions::PermissionAction::GRANTED,
                   permissions::PermissionPromptDisposition::ANCHORED_BUBBLE,
@@ -939,6 +896,5 @@
                   permissions::kOnPromptResolved, base::Minutes(1),
                   permissions::PermissionHatsTriggerHelper::
                       OneTimePermissionPromptsDecidedBucket::BUCKET_4_5,
-                  trigger_gurl),
-              kHatsSurveyTriggerPermissionsPrompt));
+                  trigger_gurl)));
 }
diff --git a/chrome/browser/preferences/BUILD.gn b/chrome/browser/preferences/BUILD.gn
index 53879e2d..de5bf80 100644
--- a/chrome/browser/preferences/BUILD.gn
+++ b/chrome/browser/preferences/BUILD.gn
@@ -48,11 +48,11 @@
     "//components/history/core/common/pref_names.cc",
     "//components/language/core/browser/pref_names.h",
     "//components/password_manager/core/common/password_manager_pref_names.h",
-    "//components/payments/core/payment_prefs.cc",
+    "//components/payments/core/payment_prefs.h",
     "//components/policy/core/common/policy_pref_names.cc",
     "//components/privacy_sandbox/privacy_sandbox_prefs.h",
     "//components/privacy_sandbox/tracking_protection_prefs.h",
-    "//components/safe_browsing/core/common/safe_browsing_prefs.cc",
+    "//components/safe_browsing/core/common/safe_browsing_prefs.h",
     "//components/signin/public/base/signin_pref_names.cc",
     "//components/supervised_user/core/common/pref_names.h",
     "//components/translate/core/browser/translate_pref_names.cc",
diff --git a/chrome/browser/preloading/prerender/prerender_manager.cc b/chrome/browser/preloading/prerender/prerender_manager.cc
index 7913b5cd..890e4c5 100644
--- a/chrome/browser/preloading/prerender/prerender_manager.cc
+++ b/chrome/browser/preloading/prerender/prerender_manager.cc
@@ -11,7 +11,6 @@
 #include "base/location.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/time/time.h"
-#include "base/timer/timer.h"
 #include "chrome/browser/browser_features.h"
 #include "chrome/browser/page_load_metrics/observers/bookmark_navigation_handle_user_data.h"
 #include "chrome/browser/preloading/chrome_preloading.h"
@@ -44,14 +43,6 @@
 
 using content::PreloadingTriggeringOutcome;
 
-// Prerendered pages are considered stale after a fixed duration.
-// TODO(https://crbug.com/1295170): Use the search prefetch setting for now. The
-// timedelta should be calculated by SearchPrefetchService after search
-// prerender reuses the prefetched responses.
-base::TimeDelta GetSearchPrerenderExpiryDuration() {
-  return SearchPrefetchCachingLimit();
-}
-
 void MarkPreloadingAttemptAsDuplicate(
     content::PreloadingAttempt* preloading_attempt) {
   CHECK(!preloading_attempt->ShouldHoldback());
@@ -84,11 +75,8 @@
       const GURL& canonical_search_url,
       std::unique_ptr<content::PrerenderHandle> search_prerender_handle)
       : search_prerender_handle_(std::move(search_prerender_handle)),
-        prerendered_canonical_search_url_(canonical_search_url) {
-    expiry_timer_.Start(FROM_HERE, GetSearchPrerenderExpiryDuration(),
-                        base::BindOnce(&SearchPrerenderTask::OnTimerTriggered,
-                                       base::Unretained(this)));
-  }
+        task_started_timestamp_(base::TimeTicks::Now()),
+        prerendered_canonical_search_url_(canonical_search_url) {}
 
   ~SearchPrerenderTask() {
     // Record whether or not the prediction is correct when prerendering for
@@ -104,8 +92,9 @@
   }
 
   void SetFailureReason(PrerenderPredictionStatus status) {
-    if (!search_prerender_handle_)
+    if (!search_prerender_handle_) {
       return;
+    }
     switch (status) {
       case PrerenderPredictionStatus::kNotStarted:
       case PrerenderPredictionStatus::kCancelled:
@@ -150,27 +139,21 @@
 
   void RecordTimestampOnDidStartNavigation(
       base::TimeTicks start_navigation_timestamp) {
-    lastest_start_navigation_event_timestamp_ = start_navigation_timestamp;
+    latest_start_navigation_event_timestamp_ = start_navigation_timestamp;
   }
 
   void RecordLifeTimeMetric() {
     // Record the lifetime of this prerender.
-    // |<------------GetSearchPrerenderExpiryDuration()------------>|
-    // @ PrerenderHintReceived    @ Activation/NavigationStarted    @ Expire
+    // @ PrerenderHintReceived    @ Activation/NavigationStarted
     // |<---------delta---------->|
     // where:
-    // expiry_timer_.desired_run_time() = Timestamp@Expire.
-    // lastest_start_navigation_event_timestamp_ =
+    // task_started_timestamp_ = Timestamp@PrerenderHintReceived
+    // latest_start_navigation_event_timestamp_ =
     //   Timestamp@Activation/NavigationStarted
     base::TimeDelta delta =
-        GetSearchPrerenderExpiryDuration() -
-        std::max(base::TimeDelta(),
-                 expiry_timer_.desired_run_time() -
-                     lastest_start_navigation_event_timestamp_);
+        latest_start_navigation_event_timestamp_ - task_started_timestamp_;
     // The upper-bound of this histogram is decided by the default duration of
     // the search prefetch setting. See `prefetch_caching_limit_ms`.
-    // TODO(https://crbug.com/1278634): Reconsider the duration after
-    // PrerenderManager supports to re-prerender the search results.
     base::UmaHistogramCustomTimes(
         "Prerender.Experimental.Search."
         "FirstCorrectPrerenderHintReceivedToRealSearchNavigationStartedDuratio"
@@ -188,22 +171,15 @@
   }
 
  private:
-  // Called by OneShotTimer. Will cancel the ongoing prerender to ensure the
-  // content displayed to users is up-to-date.
-  void OnTimerTriggered() {
-    SetFailureReason(prediction_status_);
-    search_prerender_handle_.reset();
-  }
-
   std::unique_ptr<content::PrerenderHandle> search_prerender_handle_;
 
   // Recorded on OnDidStartNavigation and used on PrimaryPageChanged. Only the
   // latest recorded TimeTicks is meaningful. See the comment in
   // PrerenderManager::DidStartNavigation for more information.
-  base::TimeTicks lastest_start_navigation_event_timestamp_;
+  base::TimeTicks latest_start_navigation_event_timestamp_;
 
-  // Stops the ongoing prerender when the prerendered result is out-of-date.
-  base::OneShotTimer expiry_timer_;
+  // Recorded upon starting the task.
+  const base::TimeTicks task_started_timestamp_;
 
   // A task is associated with a prediction, this tracks the correctness of the
   // prediction.
diff --git a/chrome/browser/resources/ash/settings/os_privacy_page/privacy_hub_camera_subpage.ts b/chrome/browser/resources/ash/settings/os_privacy_page/privacy_hub_camera_subpage.ts
index cfbc455..6066298a 100644
--- a/chrome/browser/resources/ash/settings/os_privacy_page/privacy_hub_camera_subpage.ts
+++ b/chrome/browser/resources/ash/settings/os_privacy_page/privacy_hub_camera_subpage.ts
@@ -241,7 +241,7 @@
           this.i18n('cameraToggleFallbackSubtext') :
           this.i18n('cameraToggleSubtext');
     } else {
-      return this.i18n('blockedForAllText');
+      return this.i18n('privacyHubCameraAccessBlockedText');
     }
   }
 
diff --git a/chrome/browser/resources/ash/settings/os_privacy_page/privacy_hub_microphone_subpage.ts b/chrome/browser/resources/ash/settings/os_privacy_page/privacy_hub_microphone_subpage.ts
index 78ac883..50b0a66 100644
--- a/chrome/browser/resources/ash/settings/os_privacy_page/privacy_hub_microphone_subpage.ts
+++ b/chrome/browser/resources/ash/settings/os_privacy_page/privacy_hub_microphone_subpage.ts
@@ -209,8 +209,9 @@
   private computeOnOffSubtext_(): string {
     const microphoneAllowed =
         this.getPref<string>('ash.user.microphone_allowed').value;
-    return microphoneAllowed ? this.i18n('microphoneToggleSubtext') :
-                               this.i18n('blockedForAllText');
+    return microphoneAllowed ?
+        this.i18n('microphoneToggleSubtext') :
+        this.i18n('privacyHubMicrophoneAccessBlockedText');
   }
 
   private computeShouldDisableMicrophoneToggle_(): boolean {
diff --git a/chrome/browser/resources/password_manager/images/move_passwords_promo_dark.svg b/chrome/browser/resources/password_manager/images/move_passwords_promo_dark.svg
index ae8fc09..d62796b 100644
--- a/chrome/browser/resources/password_manager/images/move_passwords_promo_dark.svg
+++ b/chrome/browser/resources/password_manager/images/move_passwords_promo_dark.svg
@@ -1 +1 @@
-<svg xmlns="http://www.w3.org/2000/svg" width="248" height="120" fill="none" viewBox="0 0 248 120"><path fill="#1F1F1F" d="M0 0h248v120H0z"/><circle cx="58" cy="20" r="1" fill="#5F6368"/><circle cx="62" cy="20" r="1" fill="#5F6368"/><circle cx="50" cy="20" r="1" fill="#5F6368"/><circle cx="54" cy="20" r="1" fill="#5F6368"/><circle cx="66" cy="20" r="1" fill="#5F6368"/><circle cx="1" cy="1" r="1" fill="#5F6368" transform="matrix(-1 0 0 1 36 92)"/><circle cx="1" cy="1" r="1" fill="#5F6368" transform="matrix(-1 0 0 1 44 92)"/><circle cx="1" cy="1" r="1" fill="#5F6368" transform="matrix(-1 0 0 1 40 92)"/><circle cx="1" cy="1" r="1" fill="#5F6368" transform="matrix(-1 0 0 1 32 92)"/><circle cx="1" cy="1" r="1" fill="#5F6368" transform="matrix(-1 0 0 1 28 92)"/><circle cx="1" cy="1" r="1" fill="#5F6368" transform="matrix(-1 0 0 1 198 25)"/><circle cx="1" cy="1" r="1" fill="#5F6368" transform="matrix(-1 0 0 1 194 25)"/><circle cx="42" cy="24" r="1" fill="#5F6368"/><circle cx="46" cy="24" r="1" fill="#5F6368"/><circle cx="30" cy="24" r="1" fill="#5F6368"/><circle cx="50" cy="24" r="1" fill="#5F6368"/><circle cx="54" cy="24" r="1" fill="#5F6368"/><circle cx="1" cy="1" r="1" fill="#5F6368" transform="matrix(-1 0 0 1 206 25)"/><circle cx="38" cy="24" r="1" fill="#5F6368"/><circle cx="1" cy="1" r="1" fill="#5F6368" transform="matrix(-1 0 0 1 202 25)"/><circle cx="1" cy="1" r="1" fill="#5F6368" transform="matrix(-1 0 0 1 190 25)"/><circle cx="1" cy="1" r="1" fill="#5F6368" transform="matrix(-1 0 0 1 186 25)"/><circle cx="1" cy="1" r="1" fill="#5F6368" transform="matrix(-1 0 0 1 182 25)"/><circle cx="1" cy="1" r="1" fill="#5F6368" transform="matrix(-1 0 0 1 52 96)"/><circle cx="1" cy="1" r="1" fill="#5F6368" transform="matrix(-1 0 0 1 48 100)"/><circle cx="1" cy="1" r="1" fill="#5F6368" transform="matrix(-1 0 0 1 48 96)"/><circle cx="1" cy="1" r="1" fill="#5F6368" transform="matrix(-1 0 0 1 44 100)"/><circle cx="1" cy="1" r="1" fill="#5F6368" transform="matrix(-1 0 0 1 44 96)"/><circle cx="1" cy="1" r="1" fill="#5F6368" transform="matrix(-1 0 0 1 40 96)"/><circle cx="1" cy="1" r="1" fill="#5F6368" transform="matrix(-1 0 0 1 36 96)"/><circle cx="1" cy="1" r="1" fill="#5F6368" transform="matrix(-1 0 0 1 56 96)"/><circle cx="1" cy="1" r="1" fill="#5F6368" transform="matrix(-1 0 0 1 214 29)"/><circle cx="1" cy="1" r="1" fill="#5F6368" transform="matrix(-1 0 0 1 210 29)"/><circle cx="1" cy="1" r="1" fill="#5F6368" transform="matrix(-1 0 0 1 206 29)"/><circle cx="183" cy="107" r="1" fill="#5F6368"/><circle cx="187" cy="107" r="1" fill="#5F6368"/><circle cx="179" cy="103" r="1" fill="#5F6368"/><circle cx="183" cy="103" r="1" fill="#5F6368"/><circle cx="191" cy="107" r="1" fill="#5F6368"/><circle cx="187" cy="103" r="1" fill="#5F6368"/><circle cx="163" cy="107" r="1" fill="#5F6368"/><circle cx="179" cy="107" r="1" fill="#5F6368"/><circle cx="167" cy="107" r="1" fill="#5F6368"/><circle cx="171" cy="107" r="1" fill="#5F6368"/><circle cx="175" cy="107" r="1" fill="#5F6368"/><circle cx="175" cy="103" r="1" fill="#5F6368"/><circle cx="1" cy="1" r="1" fill="#5F6368" transform="matrix(-1 0 0 1 218 29)"/><circle cx="1" cy="1" r="1" fill="#5F6368" transform="matrix(-1 0 0 1 202 29)"/><circle cx="1" cy="1" r="1" fill="#5F6368" transform="matrix(-1 0 0 1 198 29)"/><circle cx="37" cy="53" r="3" fill="#5F6368"/><circle cx="45" cy="53" r="3" fill="#5F6368"/><circle cx="53" cy="53" r="3" fill="#5F6368"/><circle cx="21" cy="53" r="3" fill="#5F6368"/><circle cx="199" cy="72" r="3" fill="#5F6368"/><circle cx="207" cy="72" r="3" fill="#5F6368"/><circle cx="215" cy="72" r="3" fill="#5F6368"/><circle cx="231" cy="72" r="3" fill="#5F6368"/><path fill="#1F1F1F" d="M76 16h90v88H76z"/><circle cx="120.5" cy="60" r="43" fill="#3C4043"/><path fill="url(#a)" fill-rule="evenodd" d="m141.628 79.999.206.001c8.837 0 16-7.163 16-16s-7.163-16-16-16c-.453 0-.902.019-1.346.056C137.209 38.706 128.305 32 117.834 32c-12.436 0-22.663 9.459-23.879 21.575-6.146 1.188-10.788 6.597-10.788 13.09C83.167 74.03 89.137 80 96.5 80h21.081a23.844 23.844 0 0 0 .505 0h23.542Z" clip-rule="evenodd"/><path fill="#188038" d="M142.5 57h-12v6h12v-6Z"/><path fill="#4285F4" d="M109.5 71c-6.05 0-11-4.95-11-11s4.95-11 11-11 11 4.95 11 11-4.95 11-11 11Zm0-16c-2.75 0-5 2.25-5 5s2.25 5 5 5 5-2.25 5-5-2.25-5-5-5Z"/><path fill="#34A853" d="M138.5 63h-8v6h4v-2c0-1.1.9-2 2-2s2 .9 2 2v2h4v-6h-4Z"/><path fill="#EA4335" d="M120.1 57h-6.6c.6.85 1 1.85 1 3s-.4 2.15-1 3h6.6c.25-.95.4-1.95.4-3s-.15-2.05-.4-3Z"/><path fill="#FBBC04" d="M130.5 57h-10.4c.25.95.4 1.95.4 3s-.15 2.05-.4 3h10.4v-6Z"/><defs><linearGradient id="a" x1="106.5" x2="94.057" y1="46.08" y2="76.856" gradientUnits="userSpaceOnUse"><stop offset=".354" stop-color="#DADCE0"/><stop offset=".953" stop-color="#9AA0A6"/></linearGradient></defs></svg>
\ No newline at end of file
+<svg xmlns="http://www.w3.org/2000/svg" width="248" height="120" fill="none" viewBox="0 0 248 120"><circle cx="58" cy="20" r="1" fill="#5F6368"/><circle cx="62" cy="20" r="1" fill="#5F6368"/><circle cx="50" cy="20" r="1" fill="#5F6368"/><circle cx="54" cy="20" r="1" fill="#5F6368"/><circle cx="66" cy="20" r="1" fill="#5F6368"/><circle cx="1" cy="1" r="1" fill="#5F6368" transform="matrix(-1 0 0 1 36 92)"/><circle cx="1" cy="1" r="1" fill="#5F6368" transform="matrix(-1 0 0 1 44 92)"/><circle cx="1" cy="1" r="1" fill="#5F6368" transform="matrix(-1 0 0 1 40 92)"/><circle cx="1" cy="1" r="1" fill="#5F6368" transform="matrix(-1 0 0 1 32 92)"/><circle cx="1" cy="1" r="1" fill="#5F6368" transform="matrix(-1 0 0 1 28 92)"/><circle cx="1" cy="1" r="1" fill="#5F6368" transform="matrix(-1 0 0 1 198 25)"/><circle cx="1" cy="1" r="1" fill="#5F6368" transform="matrix(-1 0 0 1 194 25)"/><circle cx="42" cy="24" r="1" fill="#5F6368"/><circle cx="46" cy="24" r="1" fill="#5F6368"/><circle cx="30" cy="24" r="1" fill="#5F6368"/><circle cx="50" cy="24" r="1" fill="#5F6368"/><circle cx="54" cy="24" r="1" fill="#5F6368"/><circle cx="1" cy="1" r="1" fill="#5F6368" transform="matrix(-1 0 0 1 206 25)"/><circle cx="38" cy="24" r="1" fill="#5F6368"/><circle cx="1" cy="1" r="1" fill="#5F6368" transform="matrix(-1 0 0 1 202 25)"/><circle cx="1" cy="1" r="1" fill="#5F6368" transform="matrix(-1 0 0 1 190 25)"/><circle cx="1" cy="1" r="1" fill="#5F6368" transform="matrix(-1 0 0 1 186 25)"/><circle cx="1" cy="1" r="1" fill="#5F6368" transform="matrix(-1 0 0 1 182 25)"/><circle cx="1" cy="1" r="1" fill="#5F6368" transform="matrix(-1 0 0 1 52 96)"/><circle cx="1" cy="1" r="1" fill="#5F6368" transform="matrix(-1 0 0 1 48 100)"/><circle cx="1" cy="1" r="1" fill="#5F6368" transform="matrix(-1 0 0 1 48 96)"/><circle cx="1" cy="1" r="1" fill="#5F6368" transform="matrix(-1 0 0 1 44 100)"/><circle cx="1" cy="1" r="1" fill="#5F6368" transform="matrix(-1 0 0 1 44 96)"/><circle cx="1" cy="1" r="1" fill="#5F6368" transform="matrix(-1 0 0 1 40 96)"/><circle cx="1" cy="1" r="1" fill="#5F6368" transform="matrix(-1 0 0 1 36 96)"/><circle cx="1" cy="1" r="1" fill="#5F6368" transform="matrix(-1 0 0 1 56 96)"/><circle cx="1" cy="1" r="1" fill="#5F6368" transform="matrix(-1 0 0 1 214 29)"/><circle cx="1" cy="1" r="1" fill="#5F6368" transform="matrix(-1 0 0 1 210 29)"/><circle cx="1" cy="1" r="1" fill="#5F6368" transform="matrix(-1 0 0 1 206 29)"/><circle cx="183" cy="107" r="1" fill="#5F6368"/><circle cx="187" cy="107" r="1" fill="#5F6368"/><circle cx="179" cy="103" r="1" fill="#5F6368"/><circle cx="183" cy="103" r="1" fill="#5F6368"/><circle cx="191" cy="107" r="1" fill="#5F6368"/><circle cx="187" cy="103" r="1" fill="#5F6368"/><circle cx="163" cy="107" r="1" fill="#5F6368"/><circle cx="179" cy="107" r="1" fill="#5F6368"/><circle cx="167" cy="107" r="1" fill="#5F6368"/><circle cx="171" cy="107" r="1" fill="#5F6368"/><circle cx="175" cy="107" r="1" fill="#5F6368"/><circle cx="175" cy="103" r="1" fill="#5F6368"/><circle cx="1" cy="1" r="1" fill="#5F6368" transform="matrix(-1 0 0 1 218 29)"/><circle cx="1" cy="1" r="1" fill="#5F6368" transform="matrix(-1 0 0 1 202 29)"/><circle cx="1" cy="1" r="1" fill="#5F6368" transform="matrix(-1 0 0 1 198 29)"/><circle cx="37" cy="53" r="3" fill="#5F6368"/><circle cx="45" cy="53" r="3" fill="#5F6368"/><circle cx="53" cy="53" r="3" fill="#5F6368"/><circle cx="21" cy="53" r="3" fill="#5F6368"/><circle cx="199" cy="72" r="3" fill="#5F6368"/><circle cx="207" cy="72" r="3" fill="#5F6368"/><circle cx="215" cy="72" r="3" fill="#5F6368"/><circle cx="231" cy="72" r="3" fill="#5F6368"/><circle cx="120.5" cy="60" r="43" fill="#3C4043"/><path fill="url(#a)" fill-rule="evenodd" d="m141.628 79.999.206.001c8.837 0 16-7.163 16-16s-7.163-16-16-16c-.453 0-.902.019-1.346.056C137.209 38.706 128.305 32 117.834 32c-12.436 0-22.663 9.459-23.879 21.575-6.146 1.188-10.788 6.597-10.788 13.09C83.167 74.03 89.137 80 96.5 80h21.081a23.844 23.844 0 0 0 .505 0h23.542Z" clip-rule="evenodd"/><path fill="#188038" d="M142.5 57h-12v6h12v-6Z"/><path fill="#4285F4" d="M109.5 71c-6.05 0-11-4.95-11-11s4.95-11 11-11 11 4.95 11 11-4.95 11-11 11Zm0-16c-2.75 0-5 2.25-5 5s2.25 5 5 5 5-2.25 5-5-2.25-5-5-5Z"/><path fill="#34A853" d="M138.5 63h-8v6h4v-2c0-1.1.9-2 2-2s2 .9 2 2v2h4v-6h-4Z"/><path fill="#EA4335" d="M120.1 57h-6.6c.6.85 1 1.85 1 3s-.4 2.15-1 3h6.6c.25-.95.4-1.95.4-3s-.15-2.05-.4-3Z"/><path fill="#FBBC04" d="M130.5 57h-10.4c.25.95.4 1.95.4 3s-.15 2.05-.4 3h10.4v-6Z"/><defs><linearGradient id="a" x1="106.5" x2="94.057" y1="46.08" y2="76.856" gradientUnits="userSpaceOnUse"><stop offset=".354" stop-color="#fff"/><stop offset=".953" stop-color="#AECBFA"/></linearGradient></defs></svg>
\ No newline at end of file
diff --git a/chrome/browser/resources/side_panel/read_anything/icons.html b/chrome/browser/resources/side_panel/read_anything/icons.html
index ceff276..221ac24 100644
--- a/chrome/browser/resources/side_panel/read_anything/icons.html
+++ b/chrome/browser/resources/side_panel/read_anything/icons.html
@@ -13,12 +13,6 @@
       <g id="font-size-decrease-old" viewBox="0 0 14 8">
         <path d="M0.383333 8L3.46667 -9.53674e-07H5.06667L8.13333 8H6.53333L5.83333 6.01667H2.66667L1.93333 8H0.383333ZM3.08333 4.8H5.4L4.28333 1.58333H4.21667L3.08333 4.8ZM8.61667 4.66667V3.33333H13.5V4.66667H8.61667Z"></path>
       </g>
-      <g id="links-enabled" viewBox="0 0 18 18">
-        <path d="M8.25 12.75H5.25C4.2125 12.75 3.32813 12.3844 2.59688 11.6531C1.86563 10.9219 1.5 10.0375 1.5 9C1.5 7.9625 1.86563 7.07813 2.59688 6.34688C3.32813 5.61563 4.2125 5.25 5.25 5.25H8.25V6.75H5.25C4.625 6.75 4.09375 6.96875 3.65625 7.40625C3.21875 7.84375 3 8.375 3 9C3 9.625 3.21875 10.1563 3.65625 10.5938C4.09375 11.0313 4.625 11.25 5.25 11.25H8.25V12.75ZM6 9.75V8.25H12V9.75H6ZM9.75 12.75V11.25H12.75C13.375 11.25 13.9063 11.0313 14.3438 10.5938C14.7813 10.1563 15 9.625 15 9C15 8.375 14.7813 7.84375 14.3438 7.40625C13.9063 6.96875 13.375 6.75 12.75 6.75H9.75V5.25H12.75C13.7875 5.25 14.6719 5.61563 15.4031 6.34688C16.1344 7.07813 16.5 7.9625 16.5 9C16.5 10.0375 16.1344 10.9219 15.4031 11.6531C14.6719 12.3844 13.7875 12.75 12.75 12.75H9.75Z">
-      </g>
-      <g id="links-disabled" viewBox="0 0 18 18">
-        <path d="M14.4375 12.3375L13.3125 11.175C13.8125 11.0375 14.2187 10.7719 14.5312 10.3781C14.8437 9.98437 15 9.525 15 9C15 8.375 14.7812 7.84375 14.3437 7.40625C13.9062 6.96875 13.375 6.75 12.75 6.75H9.75V5.25H12.75C13.7875 5.25 14.6719 5.61562 15.4031 6.34687C16.1344 7.07812 16.5 7.9625 16.5 9C16.5 9.7125 16.3156 10.3687 15.9469 10.9687C15.5781 11.5687 15.075 12.025 14.4375 12.3375ZM11.8875 9.75L10.3875 8.25H12V9.75H11.8875ZM14.85 16.95L1.05 3.15L2.1 2.1L15.9 15.9L14.85 16.95ZM8.25 12.75H5.25C4.2125 12.75 3.32812 12.3844 2.59687 11.6531C1.86562 10.9219 1.5 10.0375 1.5 9C1.5 8.1375 1.7625 7.36875 2.2875 6.69375C2.8125 6.01875 3.4875 5.575 4.3125 5.3625L5.7 6.75H5.25C4.625 6.75 4.09375 6.96875 3.65625 7.40625C3.21875 7.84375 3 8.375 3 9C3 9.625 3.21875 10.1562 3.65625 10.5937C4.09375 11.0312 4.625 11.25 5.25 11.25H8.25V12.75ZM6 9.75V8.25H7.21875L8.7 9.75H6Z">
-      </g>
       <g id="font" viewBox="0 0 16 16">
         <path d="M4.21667 12H5.68333L6.4 9.98333H9.61667L10.3167 12H11.7833L8.76667 4H7.23333L4.21667 12ZM6.8 8.78333L7.96667 5.5H8.01667L9.16667 8.78333H6.8ZM2.85 14.5333C2.46111 14.5333 2.13333 14.4 1.86667 14.1333C1.6 13.8667 1.46667 13.5389 1.46667 13.15V2.85C1.46667 2.46111 1.6 2.13333 1.86667 1.86667C2.13333 1.6 2.46111 1.46667 2.85 1.46667H13.15C13.5389 1.46667 13.8667 1.6 14.1333 1.86667C14.4 2.13333 14.5333 2.46111 14.5333 2.85V13.15C14.5333 13.5389 14.4 13.8667 14.1333 14.1333C13.8667 14.4 13.5389 14.5333 13.15 14.5333H2.85ZM2.85 13.15H13.15V2.85H2.85V13.15ZM2.85 2.85V13.15V2.85Z"></path>
       </g>
diff --git a/chrome/browser/resources/side_panel/read_anything/read_anything.d.ts b/chrome/browser/resources/side_panel/read_anything/read_anything.d.ts
index 9fddfd9..a6f6aa9 100644
--- a/chrome/browser/resources/side_panel/read_anything/read_anything.d.ts
+++ b/chrome/browser/resources/side_panel/read_anything/read_anything.d.ts
@@ -140,9 +140,6 @@
     function onFontSizeChanged(increase: boolean): void;
     function onFontSizeReset(): void;
 
-    // Called when a user toggles links via the webui toolbar.
-    function onLinksEnabledToggled(): void;
-
     // Called when the letter spacing is changed via the webui toolbar.
     function onStandardLetterSpacing(): void;
     function onWideLetterSpacing(): void;
diff --git a/chrome/browser/resources/side_panel/read_anything/read_anything_toolbar.html b/chrome/browser/resources/side_panel/read_anything/read_anything_toolbar.html
index 03b2c97..c1da78e 100644
--- a/chrome/browser/resources/side_panel/read_anything/read_anything_toolbar.html
+++ b/chrome/browser/resources/side_panel/read_anything/read_anything_toolbar.html
@@ -204,17 +204,6 @@
 
   <hr class="separator" aria-hidden="true">
 
-  <template is="dom-repeat" items="[[textStyleToggles_]]">
-    <cr-icon-button
-    tabindex="-1"
-    class="toolbar-button"
-    id="[[item.id]]"
-      aria-label="[[item.ariaLabel]]"
-      iron-icon="[[item.icon]]"
-      on-click="onToggleButtonClick_">
-    </cr-icon-button>
-  </template>
-
   <template is="dom-repeat" items="[[textStyleOptions_]]">
     <cr-icon-button
       class="toolbar-button"
diff --git a/chrome/browser/resources/side_panel/read_anything/read_anything_toolbar.ts b/chrome/browser/resources/side_panel/read_anything/read_anything_toolbar.ts
index 93c0ee2..b59f048 100644
--- a/chrome/browser/resources/side_panel/read_anything/read_anything_toolbar.ts
+++ b/chrome/browser/resources/side_panel/read_anything/read_anything_toolbar.ts
@@ -11,7 +11,6 @@
 import './icons.html.js';
 
 import {AnchorAlignment, CrActionMenuElement, ShowAtPositionConfig} from '//resources/cr_elements/cr_action_menu/cr_action_menu.js';
-import {CrIconButtonElement} from '//resources/cr_elements/cr_icon_button/cr_icon_button.js';
 import {WebUiListenerMixin} from '//resources/cr_elements/web_ui_listener_mixin.js';
 import {assert} from '//resources/js/assert.js';
 import {loadTimeData} from '//resources/js/load_time_data.js';
@@ -57,13 +56,6 @@
   menuToOpen: () => CrActionMenuElement;
 }
 
-interface ToggleButton {
-  id: string;
-  icon: string;
-  ariaLabel: string;
-  callback: (event: DomRepeatEvent<ToggleButton>) => void;
-}
-
 // Enum for logging when a text style setting is changed.
 // These values are persisted to logs. Entries should not be renumbered and
 // numeric values should never be reused.
@@ -73,25 +65,18 @@
   THEME_CHANGE = 2,
   LINE_HEIGHT_CHANGE = 3,
   LETTER_SPACING_CHANGE = 4,
-  LINKS_ENABLED_CHANGE = 5,
 
   // Must be last.
-  COUNT = 6,
+  COUNT = 5,
 }
 
 const SETTINGS_CHANGE_UMA = 'Accessibility.ReadAnything.SettingsChange';
 const moreOptionsClass = '.more-options-icon';
 const activeClass = ' active';
 
-// Link toggle button constants.
-const LINKS_ENABLED_ICON = 'read-anything:links-enabled';
-const LINKS_DISABLED_ICON = 'read-anything:links-disabled';
-const LINK_TOGGLE_BUTTON_ID = 'link-toggle-button';
-
 const ReadAnythingToolbarElementBase = WebUiListenerMixin(PolymerElement);
 export class ReadAnythingToolbarElement extends ReadAnythingToolbarElementBase {
   contentPage = document.querySelector('read-anything-app');
-
   static get is() {
     return 'read-anything-toolbar';
   }
@@ -108,7 +93,6 @@
       colorOptions_: Array,
       rateOptions_: Array,
       textStyleOptions_: Array,
-      textStyleToggles_: Array,
     };
   }
 
@@ -209,18 +193,6 @@
     },
   ];
 
-  private textStyleToggles_: ToggleButton[] = [
-    {
-      id: LINK_TOGGLE_BUTTON_ID,
-      icon: chrome.readingMode.linksEnabled?
-      LINKS_ENABLED_ICON: LINKS_DISABLED_ICON,
-      ariaLabel: chrome.readingMode.linksEnabled?
-               loadTimeData.getString('disableLinksLabel'):
-                   loadTimeData.getString('enableLinksLabel'),
-      callback: this.onToggleLinksClick_.bind(this),
-    },
-  ];
-
   private colorOptions_: Array<MenuStateItem<string>> = [
     {
       title: loadTimeData.getString('defaultColorTitle'),
@@ -367,8 +339,6 @@
   restoreSettingsFromPrefs(colorSuffix?: string) {
     this.restoreFontMenu_();
 
-    this.updateLinkToggleButton();
-
     if (this.isReadAloudEnabled_) {
       const speechRate = parseFloat(chrome.readingMode.speechRate.toFixed(1));
       this.setRateIcon_(speechRate);
@@ -742,37 +712,6 @@
     this.updateFontSize_(false);
   }
 
-  private onToggleButtonClick_(event: DomRepeatEvent<ToggleButton>) {
-    event.model.item.callback(event);
-  }
-
-  private onToggleLinksClick_(event: DomRepeatEvent<ToggleButton>) {
-    if (!event.target) {
-      return;
-    }
-
-    chrome.metricsPrivate.recordEnumerationValue(
-        SETTINGS_CHANGE_UMA, ReadAnythingSettingsChange.LINKS_ENABLED_CHANGE,
-        ReadAnythingSettingsChange.COUNT);
-
-
-    chrome.readingMode.onLinksEnabledToggled();
-    this.contentPage?.updateContent();
-    this.updateLinkToggleButton();
-  }
-
-  private updateLinkToggleButton() {
-    const button = this.shadowRoot?.getElementById(LINK_TOGGLE_BUTTON_ID) as
-        CrIconButtonElement;
-    if (button) {
-      button.ironIcon = chrome.readingMode.linksEnabled ? LINKS_ENABLED_ICON :
-                                                          LINKS_DISABLED_ICON;
-      button.ariaLabel = chrome.readingMode.linksEnabled ?
-          loadTimeData.getString('disableLinksLabel') :
-          loadTimeData.getString('enableLinksLabel');
-    }
-  }
-
   private updateFontSize_(increase: boolean) {
     chrome.metricsPrivate.recordEnumerationValue(
         SETTINGS_CHANGE_UMA, ReadAnythingSettingsChange.FONT_SIZE_CHANGE,
diff --git a/chrome/browser/search_engine_choice/search_engine_choice_dialog_service_unittest.cc b/chrome/browser/search_engine_choice/search_engine_choice_dialog_service_unittest.cc
index 37a7fb8f..2ef7e8c 100644
--- a/chrome/browser/search_engine_choice/search_engine_choice_dialog_service_unittest.cc
+++ b/chrome/browser/search_engine_choice/search_engine_choice_dialog_service_unittest.cc
@@ -40,13 +40,13 @@
         profile(),
         base::BindRepeating(&TemplateURLServiceFactory::BuildInstanceFor));
 
-    PrefService* pref_service = profile()->GetPrefs();
     // The search engine choice feature is only enabled for countries in the
     // EEA region.
     const int kBelgiumCountryId =
         country_codes::CountryCharsToCountryID('B', 'E');
-    pref_service->SetInteger(country_codes::kCountryIDAtInstall,
-                             kBelgiumCountryId);
+    base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
+        switches::kSearchEngineChoiceCountry,
+        country_codes::CountryIDToCountryString(kBelgiumCountryId));
   }
 
   std::unique_ptr<BrowserWindow> CreateBrowserWindow() override {
diff --git a/chrome/browser/sync/test/integration/two_client_web_apps_integration_test_base.cc b/chrome/browser/sync/test/integration/two_client_web_apps_integration_test_base.cc
index 13a71cb..3261490 100644
--- a/chrome/browser/sync/test/integration/two_client_web_apps_integration_test_base.cc
+++ b/chrome/browser/sync/test/integration/two_client_web_apps_integration_test_base.cc
@@ -13,10 +13,21 @@
 #include "chrome/test/base/in_process_browser_test.h"
 #include "components/sync/base/user_selectable_type.h"
 
+#if BUILDFLAG(IS_CHROMEOS)
+#include "chromeos/constants/chromeos_features.h"
+#endif
+
 namespace web_app::integration_tests {
 
 TwoClientWebAppsIntegrationTestBase::TwoClientWebAppsIntegrationTestBase()
-    : WebAppsSyncTestBase(TWO_CLIENT), helper_(this) {}
+    : WebAppsSyncTestBase(TWO_CLIENT), helper_(this) {
+#if BUILDFLAG(IS_CHROMEOS)
+  // TODO(b/321620363): Add two client sync integration for shortcuts with
+  // shortstand enabled.
+  scoped_feature_list_.InitAndDisableFeature(
+      chromeos::features::kCrosShortstand);
+#endif
+}
 
 // WebAppIntegrationTestDriver::TestDelegate
 Browser* TwoClientWebAppsIntegrationTestBase::CreateBrowser(Profile* profile) {
diff --git a/chrome/browser/sync/test/integration/two_client_web_apps_integration_test_base.h b/chrome/browser/sync/test/integration/two_client_web_apps_integration_test_base.h
index 8fe548c1..e154586 100644
--- a/chrome/browser/sync/test/integration/two_client_web_apps_integration_test_base.h
+++ b/chrome/browser/sync/test/integration/two_client_web_apps_integration_test_base.h
@@ -8,6 +8,10 @@
 #include "chrome/browser/sync/test/integration/web_apps_sync_test_base.h"
 #include "chrome/browser/ui/views/web_apps/web_app_integration_test_driver.h"
 
+#if BUILDFLAG(IS_CHROMEOS)
+#include "base/test/scoped_feature_list.h"
+#endif
+
 namespace base {
 class CommandLine;
 }
@@ -42,6 +46,11 @@
   void SetUpCommandLine(base::CommandLine* command_line) override;
 
   WebAppIntegrationTestDriver helper_;
+
+#if BUILDFLAG(IS_CHROMEOS)
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+#endif
 };
 
 }  // namespace web_app::integration_tests
diff --git a/chrome/browser/ui/android/hats/hats_service_android.cc b/chrome/browser/ui/android/hats/hats_service_android.cc
index a6fcfb9..2c27687 100644
--- a/chrome/browser/ui/android/hats/hats_service_android.cc
+++ b/chrome/browser/ui/android/hats/hats_service_android.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/ui/android/hats/hats_service_android.h"
 
 #include <memory>
+#include <string>
 #include <utility>
 
 #include "base/functional/bind.h"
@@ -39,7 +40,8 @@
     const SurveyStringData& product_specific_string_data,
     base::OnceClosure success_callback,
     base::OnceClosure failure_callback,
-    std::optional<std::string_view> supplied_trigger_id)
+    const std::optional<std::string>& supplied_trigger_id,
+    const SurveyOptions& survey_options)
     : web_contents_(web_contents),
       hats_service_(hats_service),
       trigger_(trigger),
@@ -47,7 +49,8 @@
       product_specific_string_data_(product_specific_string_data),
       success_callback_(std::move(success_callback)),
       failure_callback_(std::move(failure_callback)),
-      supplied_trigger_id_(std::move(supplied_trigger_id)) {}
+      supplied_trigger_id_(supplied_trigger_id),
+      survey_options_(survey_options) {}
 
 HatsServiceAndroid::DelayedSurveyTask::~DelayedSurveyTask() = default;
 
@@ -59,16 +62,22 @@
   }
 
   message_ = std::make_unique<messages::MessageWrapper>(
-      messages::MessageIdentifier::CHROME_SURVEY, std::move(success_callback_),
+      survey_options_.message_identifier.value_or(
+          messages::MessageIdentifier::CHROME_SURVEY),
+      std::move(success_callback_),
       base::BindOnce(&HatsServiceAndroid::DelayedSurveyTask::DismissCallback,
                      weak_ptr_factory_.GetWeakPtr()));
 
+  if (survey_options_.custom_invitation.has_value()) {
+    message_->SetTitle(survey_options_.custom_invitation.value());
+  }
+
   hats::SurveyUiDelegateAndroid delegate(
       message_.get(), web_contents()->GetTopLevelNativeWindow());
 
   // Create survey client with delegate.
-  hats::SurveyClientAndroid survey_client(trigger_, &delegate,
-                                          hats_service_->profile());
+  hats::SurveyClientAndroid survey_client(
+      trigger_, &delegate, hats_service_->profile(), supplied_trigger_id_);
   survey_client.LaunchSurvey(web_contents()->GetTopLevelNativeWindow(),
                              product_specific_bits_data_,
                              product_specific_string_data_);
@@ -148,14 +157,15 @@
     const SurveyStringData& product_specific_string_data,
     base::OnceClosure success_callback,
     base::OnceClosure failure_callback,
-    const std::optional<std::string_view>& supplied_trigger_id) {
+    const std::optional<std::string>& supplied_trigger_id,
+    const SurveyOptions& survey_options) {
   // By using a delayed survey with a delay of 0, we can centralize the object
   // lifecycle management duties for native clank survey triggers.
   LaunchDelayedSurveyForWebContents(
       trigger, web_contents, 0, product_specific_bits_data,
       product_specific_string_data, HatsService::NavigationBehaviour::ALLOW_ANY,
       std::move(success_callback), std::move(failure_callback),
-      supplied_trigger_id);
+      supplied_trigger_id, survey_options);
 }
 
 bool HatsServiceAndroid::LaunchDelayedSurvey(
@@ -176,7 +186,8 @@
     NavigationBehaviour navigation_behaviour,
     base::OnceClosure success_callback,
     base::OnceClosure failure_callback,
-    const std::optional<std::string_view>& supplied_trigger_id) {
+    const std::optional<std::string>& supplied_trigger_id,
+    const SurveyOptions& survey_options) {
   CHECK(web_contents);
   CHECK(navigation_behaviour ==
         NavigationBehaviour::ALLOW_ANY);  // Currently only ALLOW_ANY is
@@ -193,7 +204,7 @@
   auto result = pending_tasks_.emplace(
       this, trigger, web_contents, product_specific_bits_data,
       product_specific_string_data, std::move(success_callback),
-      std::move(failure_callback), supplied_trigger_id);
+      std::move(failure_callback), supplied_trigger_id, survey_options);
   if (!result.second) {
     return false;
   }
diff --git a/chrome/browser/ui/android/hats/hats_service_android.h b/chrome/browser/ui/android/hats/hats_service_android.h
index 2aab1ad..26f7238 100644
--- a/chrome/browser/ui/android/hats/hats_service_android.h
+++ b/chrome/browser/ui/android/hats/hats_service_android.h
@@ -44,7 +44,8 @@
                       const SurveyStringData& product_specific_string_data,
                       base::OnceClosure success_callback,
                       base::OnceClosure failure_callback,
-                      std::optional<std::string_view> supplied_trigger_id);
+                      const std::optional<std::string>& supplied_trigger_id,
+                      const SurveyOptions& survey_options);
 
     // Not copyable or movable
     DelayedSurveyTask(const DelayedSurveyTask&) = delete;
@@ -80,7 +81,8 @@
     SurveyStringData product_specific_string_data_;
     base::OnceClosure success_callback_;
     base::OnceClosure failure_callback_;
-    std::optional<std::string_view> supplied_trigger_id_;
+    std::optional<std::string> supplied_trigger_id_;
+    SurveyOptions survey_options_;
     base::WeakPtrFactory<DelayedSurveyTask> weak_ptr_factory_{this};
   };
 
@@ -129,8 +131,8 @@
       const SurveyStringData& product_specific_string_data,
       base::OnceClosure success_callback = base::DoNothing(),
       base::OnceClosure failure_callback = base::DoNothing(),
-      const std::optional<std::string_view>& supplied_trigger_id =
-          std::nullopt) override;
+      const std::optional<std::string>& supplied_trigger_id = std::nullopt,
+      const SurveyOptions& survey_options = SurveyOptions()) override;
 
   bool LaunchDelayedSurvey(
       const std::string& trigger,
@@ -147,8 +149,8 @@
       NavigationBehaviour navigation_behaviour = NavigationBehaviour::ALLOW_ANY,
       base::OnceClosure success_callback = base::DoNothing(),
       base::OnceClosure failure_callback = base::DoNothing(),
-      const std::optional<std::string_view>& supplied_trigger_id =
-          std::nullopt) override;
+      const std::optional<std::string>& supplied_trigger_id = std::nullopt,
+      const SurveyOptions& survey_options = SurveyOptions()) override;
 
   // Currently not implemented
   bool CanShowAnySurvey(bool user_prompted) const override;
diff --git a/chrome/browser/ui/android/hats/internal/java/src/org/chromium/chrome/browser/ui/hats/SurveyClientBridge.java b/chrome/browser/ui/android/hats/internal/java/src/org/chromium/chrome/browser/ui/hats/SurveyClientBridge.java
index 8d2149a..d3183e3 100644
--- a/chrome/browser/ui/android/hats/internal/java/src/org/chromium/chrome/browser/ui/hats/SurveyClientBridge.java
+++ b/chrome/browser/ui/android/hats/internal/java/src/org/chromium/chrome/browser/ui/hats/SurveyClientBridge.java
@@ -24,8 +24,9 @@
  */
 @JNINamespace("hats")
 class SurveyClientBridge implements SurveyClient {
+
     private final SurveyClient mDelegate;
-    private long mNativeSurveyClient;
+    private final long mNativeSurveyClient;
 
     private SurveyClientBridge(long nativeSurveyClient, SurveyClient delegate) {
         mNativeSurveyClient = nativeSurveyClient;
@@ -35,10 +36,16 @@
     @CalledByNative
     @VisibleForTesting
     static SurveyClientBridge create(
-            long nativeSurveyClient, String trigger, SurveyUiDelegate uiDelegate, Profile profile) {
+            long nativeSurveyClient,
+            String trigger,
+            SurveyUiDelegate uiDelegate,
+            Profile profile,
+            String suppliedTriggerId) {
         assert SurveyClientFactory.getInstance() != null;
-        SurveyConfig config = SurveyConfig.get(trigger);
-        if (config == null) return null;
+        SurveyConfig config = SurveyConfig.get(trigger, suppliedTriggerId);
+        if (config == null) {
+            return null;
+        }
 
         return new SurveyClientBridge(
                 nativeSurveyClient,
diff --git a/chrome/browser/ui/android/hats/internal/java/src/org/chromium/chrome/browser/ui/hats/SurveyClientBridgeUnitTest.java b/chrome/browser/ui/android/hats/internal/java/src/org/chromium/chrome/browser/ui/hats/SurveyClientBridgeUnitTest.java
index fc3fe82..4447a8e 100644
--- a/chrome/browser/ui/android/hats/internal/java/src/org/chromium/chrome/browser/ui/hats/SurveyClientBridgeUnitTest.java
+++ b/chrome/browser/ui/android/hats/internal/java/src/org/chromium/chrome/browser/ui/hats/SurveyClientBridgeUnitTest.java
@@ -37,8 +37,10 @@
 /** Unit test for {@link SurveyClientBridge}. */
 @RunWith(BaseRobolectricTestRunner.class)
 public class SurveyClientBridgeUnitTest {
+
     private static final long TEST_NATIVE_POINTER = 45312L;
     private static final String TEST_TRIGGER = "trigger";
+    private static final String SUPPLIED_TRIGGER_ID = "SomeOtherSurveyTriggerId";
 
     @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
 
@@ -69,11 +71,43 @@
                 TEST_TRIGGER, new String[] {}, new String[] {});
         SurveyClientBridge bridge =
                 SurveyClientBridge.create(
-                        TEST_NATIVE_POINTER, TEST_TRIGGER, testDelegate, mProfile);
+                        TEST_NATIVE_POINTER, TEST_TRIGGER, testDelegate, mProfile, "");
         assertNotNull(bridge);
 
         bridge.showSurvey(mActivity, mActivityLifecycleDispatcher);
         verify(mDelegateSurveyClient).showSurvey(mActivity, mActivityLifecycleDispatcher);
+
+        ArgumentCaptor<SurveyConfig> surveyConfigArgumentCaptor =
+                ArgumentCaptor.forClass(SurveyConfig.class);
+        verify(mFactory).createClient(surveyConfigArgumentCaptor.capture(), any(), any());
+
+        assertEquals(
+                TestSurveyUtils.TEST_TRIGGER_ID_FOO,
+                surveyConfigArgumentCaptor.getValue().mTriggerId);
+    }
+
+    @Test
+    public void showSurveyFromJavaWithSuppliedTriggerId() {
+        TestSurveyUtils.TestSurveyUiDelegate testDelegate =
+                new TestSurveyUtils.TestSurveyUiDelegate();
+        TestSurveyUtils.setTestSurveyConfigForTrigger(
+                TEST_TRIGGER, new String[] {}, new String[] {});
+        SurveyClientBridge bridge =
+                SurveyClientBridge.create(
+                        TEST_NATIVE_POINTER,
+                        TEST_TRIGGER,
+                        testDelegate,
+                        mProfile,
+                        SUPPLIED_TRIGGER_ID);
+        assertNotNull(bridge);
+
+        bridge.showSurvey(mActivity, mActivityLifecycleDispatcher);
+        verify(mDelegateSurveyClient).showSurvey(mActivity, mActivityLifecycleDispatcher);
+
+        ArgumentCaptor<SurveyConfig> surveyConfigArgumentCaptor =
+                ArgumentCaptor.forClass(SurveyConfig.class);
+        verify(mFactory).createClient(surveyConfigArgumentCaptor.capture(), any(), any());
+        assertEquals(SUPPLIED_TRIGGER_ID, surveyConfigArgumentCaptor.getValue().mTriggerId);
     }
 
     @Test
@@ -84,7 +118,7 @@
                 TEST_TRIGGER, new String[] {"bit1", "bit2"}, new String[] {"string1", "string2"});
         SurveyClientBridge bridge =
                 SurveyClientBridge.create(
-                        TEST_NATIVE_POINTER, TEST_TRIGGER, testDelegate, mProfile);
+                        TEST_NATIVE_POINTER, TEST_TRIGGER, testDelegate, mProfile, "");
         assertNotNull(bridge);
 
         Map<String, Boolean> bitValues = Map.of("bit1", true, "bit2", false);
@@ -103,7 +137,7 @@
         TestSurveyUtils.setTestSurveyConfigForTrigger(TEST_TRIGGER, bitFields, stringFields);
         SurveyClientBridge bridge =
                 SurveyClientBridge.create(
-                        TEST_NATIVE_POINTER, TEST_TRIGGER, testDelegate, mProfile);
+                        TEST_NATIVE_POINTER, TEST_TRIGGER, testDelegate, mProfile, "");
         assertNotNull(bridge);
 
         WindowAndroid window = mock(WindowAndroid.class);
diff --git a/chrome/browser/ui/android/hats/java/src/org/chromium/chrome/browser/ui/hats/SurveyConfig.java b/chrome/browser/ui/android/hats/java/src/org/chromium/chrome/browser/ui/hats/SurveyConfig.java
index be3f59e1..b52eb7c4 100644
--- a/chrome/browser/ui/android/hats/java/src/org/chromium/chrome/browser/ui/hats/SurveyConfig.java
+++ b/chrome/browser/ui/android/hats/java/src/org/chromium/chrome/browser/ui/hats/SurveyConfig.java
@@ -4,6 +4,8 @@
 
 package org.chromium.chrome.browser.ui.hats;
 
+import android.text.TextUtils;
+
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 
@@ -17,18 +19,24 @@
 import java.util.Map;
 
 /**
- * Java equivalent of C++ survey::SurveyConfig. All member variable are kept package protected,
- * as they should not be access outside of survey code.
- * To add a new SurveyConfig, see //chrome/browser/ui/hats/survey_config.*
+ * Java equivalent of C++ survey::SurveyConfig. All member variable are kept package protected, as
+ * they should not be access outside of survey code. To add a new SurveyConfig, see
+ * //chrome/browser/ui/hats/survey_config.*
  */
 @JNINamespace("hats")
 public class SurveyConfig {
+
     private static SurveyConfig sConfigForTesting;
 
     /** Unique key associate with the config. */
     final String mTrigger;
 
-    /** TriggerId used to download & present the associated survey from HaTS service. */
+    /**
+     * TriggerId used to download & present the associated survey from HaTS service. The triggerId
+     * can be configured in survey_config.cc. TriggerIds can be supplied dynamically as well, in
+     * which case they temporarily hide the statically configured triggerId. @see
+     * Holder#getSurveyConfig.
+     */
     final String mTriggerId;
 
     /** Probability [0,1] of how likely a chosen user will see the survey. */
@@ -46,31 +54,6 @@
     /** Product Specific String Data fields which are sent with the survey response. */
     final String[] mPsdStringDataFields;
 
-    /**
-     * Get the survey config with the associate trigger if the survey config exists and enabled;
-     * returns null otherwise.
-     *
-     * @param trigger The trigger associated with the SurveyConfig.
-     * @return SurveyConfig if the survey exists and is enabled.
-     */
-    @Nullable
-    public static SurveyConfig get(String trigger) {
-        if (sConfigForTesting != null && sConfigForTesting.mTrigger.equals(trigger)) {
-            return sConfigForTesting;
-        }
-        return Holder.getInstance().getSurveyConfig(trigger);
-    }
-
-    static void setSurveyConfigForTesting(SurveyConfig config) {
-        sConfigForTesting = config;
-        ResettersForTesting.register(() -> sConfigForTesting = null);
-    }
-
-    /** Clear all the initialized configs. */
-    static void clearAll() {
-        Holder.getInstance().destroy();
-    }
-
     /** Not generated from java. */
     @VisibleForTesting
     SurveyConfig(
@@ -88,6 +71,62 @@
         mPsdStringDataFields = psdStringDataFields;
     }
 
+    /**
+     * Get the survey config with the associate trigger if the survey config exists and enabled;
+     * returns null otherwise.
+     *
+     * @param trigger The trigger associated with the SurveyConfig.
+     * @return SurveyConfig if the survey exists and is enabled.
+     */
+    @Nullable
+    public static SurveyConfig get(String trigger) {
+        return get(trigger, "");
+    }
+
+    /**
+     * Get the survey config with the associate trigger if the survey config exists and enabled;
+     * returns null otherwise. Allows dynamically supplying triggerIds for survey configs.
+     *
+     * @param trigger The trigger associated with the SurveyConfig.
+     * @param suppliedTriggerId if non-empty, creates an otherwise equivalent config with the
+     *     triggerId set to suppliedTriggerId
+     * @return SurveyConfig if the survey exists and is enabled.
+     */
+    @Nullable
+    public static SurveyConfig get(String trigger, String suppliedTriggerId) {
+        SurveyConfig config;
+        if (sConfigForTesting != null && sConfigForTesting.mTrigger.equals(trigger)) {
+            config = sConfigForTesting;
+        } else {
+            config = Holder.getInstance().getSurveyConfig(trigger);
+        }
+        return getConfigWithSuppliedTriggerIdIfPresent(config, suppliedTriggerId);
+    }
+
+    static void setSurveyConfigForTesting(SurveyConfig config) {
+        sConfigForTesting = config;
+        ResettersForTesting.register(() -> sConfigForTesting = null);
+    }
+
+    /** Clear all the initialized configs. */
+    static void clearAll() {
+        Holder.getInstance().destroy();
+    }
+
+    static SurveyConfig getConfigWithSuppliedTriggerIdIfPresent(
+            SurveyConfig config, String suppliedTriggerId) {
+        if (config != null && !TextUtils.isEmpty(suppliedTriggerId)) {
+            return new SurveyConfig(
+                    config.mTrigger,
+                    suppliedTriggerId,
+                    config.mProbability,
+                    config.mUserPrompted,
+                    config.mPsdBitDataFields,
+                    config.mPsdStringDataFields);
+        }
+        return config;
+    }
+
     @CalledByNative
     private static void addActiveSurveyConfigToHolder(
             Holder holder,
@@ -110,9 +149,15 @@
 
     /** Holder that stores all the active surveys for Android. */
     static class Holder {
+
         private static Holder sInstance;
-        private long mNativeInstance;
         private final Map<String, SurveyConfig> mTriggers;
+        private long mNativeInstance;
+
+        private Holder() {
+            mTriggers = new HashMap<>();
+            mNativeInstance = SurveyConfigJni.get().initHolder(this);
+        }
 
         static Holder getInstance() {
             if (sInstance == null) {
@@ -121,11 +166,6 @@
             return sInstance;
         }
 
-        private Holder() {
-            mTriggers = new HashMap<>();
-            mNativeInstance = SurveyConfigJni.get().initHolder(this);
-        }
-
         SurveyConfig getSurveyConfig(String trigger) {
             return mTriggers.get(trigger);
         }
@@ -138,6 +178,7 @@
 
     @NativeMethods
     interface Natives {
+
         long initHolder(Holder caller);
 
         void destroy(long nativeSurveyConfigHolder);
diff --git a/chrome/browser/ui/android/hats/survey_client_android.cc b/chrome/browser/ui/android/hats/survey_client_android.cc
index fd0126e..bf25fa6 100644
--- a/chrome/browser/ui/android/hats/survey_client_android.cc
+++ b/chrome/browser/ui/android/hats/survey_client_android.cc
@@ -23,16 +23,23 @@
 namespace hats {
 
 // static
-SurveyClientAndroid::SurveyClientAndroid(const std::string& trigger,
-                                         SurveyUiDelegateAndroid* ui_delegate,
-                                         Profile* profile) {
+SurveyClientAndroid::SurveyClientAndroid(
+    const std::string& trigger,
+    SurveyUiDelegateAndroid* ui_delegate,
+    Profile* profile,
+    const std::optional<std::string>& supplied_trigger_id) {
   JNIEnv* env = base::android::AttachCurrentThread();
   ScopedJavaLocalRef<jstring> java_trigger =
       ConvertUTF8ToJavaString(env, trigger);
+  ScopedJavaLocalRef<jstring> java_supplied_trigger_id =
+      ConvertUTF8ToJavaString(env, supplied_trigger_id.has_value()
+                                       ? supplied_trigger_id.value()
+                                       : base::StringPiece());
   jobj_ = Java_SurveyClientBridge_create(
       env, reinterpret_cast<int64_t>(this), java_trigger,
       ui_delegate->GetJavaObject(env),
-      ProfileAndroid::FromProfile(profile)->GetJavaObject());
+      ProfileAndroid::FromProfile(profile)->GetJavaObject(),
+      java_supplied_trigger_id);
 }
 
 SurveyClientAndroid::~SurveyClientAndroid() = default;
diff --git a/chrome/browser/ui/android/hats/survey_client_android.h b/chrome/browser/ui/android/hats/survey_client_android.h
index e08b633..eab31ce 100644
--- a/chrome/browser/ui/android/hats/survey_client_android.h
+++ b/chrome/browser/ui/android/hats/survey_client_android.h
@@ -33,9 +33,11 @@
 // C++ equivalent of Java SurveyClient.
 class SurveyClientAndroid {
  public:
-  explicit SurveyClientAndroid(const std::string& trigger,
-                               SurveyUiDelegateAndroid* ui_delegate,
-                               Profile* profile);
+  explicit SurveyClientAndroid(
+      const std::string& trigger,
+      SurveyUiDelegateAndroid* ui_delegate,
+      Profile* profile,
+      const std::optional<std::string>& supplied_trigger_id);
 
   SurveyClientAndroid(const SurveyClientAndroid&) = delete;
   SurveyClientAndroid& operator=(const SurveyClientAndroid&) = delete;
diff --git a/chrome/browser/ui/android/hats/survey_client_android_unittest.cc b/chrome/browser/ui/android/hats/survey_client_android_unittest.cc
index 169180cd..68bb3f2 100644
--- a/chrome/browser/ui/android/hats/survey_client_android_unittest.cc
+++ b/chrome/browser/ui/android/hats/survey_client_android_unittest.cc
@@ -95,14 +95,15 @@
   raw_ptr<TestingProfile> profile_;
 };
 
-TEST_F(SurveyClientAndroidTest, CreateSurveyClient) {
+TEST_F(SurveyClientAndroidTest, CreateSurveyClientWithStaticTriggerId) {
   std::unique_ptr<ui::WindowAndroid::ScopedWindowAndroidForTesting> window =
       ui::WindowAndroid::CreateForTesting();
   JNIEnv* env = base::android::AttachCurrentThread();
   std::unique_ptr<TestSurveyUiDelegate> delegate =
       std::make_unique<TestSurveyUiDelegate>(env);
   survey_client_ = std::make_unique<SurveyClientAndroid>(
-      kTestSurveyTrigger, delegate.get(), profile_);
+      kTestSurveyTrigger, delegate.get(), profile_,
+      /*supplied_trigger_id=*/std::nullopt);
 
   survey_client_->LaunchSurvey(window->get(),
                                kTestSurveyProductSpecificBitsData,
@@ -113,6 +114,31 @@
   std::string last_shown_trigger =
       TestSurveyUtilsBridge::GetLastShownSurveyTriggerId();
   ASSERT_FALSE(last_shown_trigger.empty());
+  ASSERT_EQ(last_shown_trigger, kHatsNextSurveyTriggerIDTesting);
+}
+
+TEST_F(SurveyClientAndroidTest, CreateSurveyClientWithDynamicTriggerId) {
+  std::unique_ptr<ui::WindowAndroid::ScopedWindowAndroidForTesting> window =
+      ui::WindowAndroid::CreateForTesting();
+  JNIEnv* env = base::android::AttachCurrentThread();
+  std::unique_ptr<TestSurveyUiDelegate> delegate =
+      std::make_unique<TestSurveyUiDelegate>(env);
+
+  const std::string kSuppliedTriggerId = "SomeOtherId";
+  survey_client_ = std::make_unique<SurveyClientAndroid>(
+      kTestSurveyTrigger, delegate.get(), profile_,
+      /*supplied_trigger_id=*/kSuppliedTriggerId);
+
+  survey_client_->LaunchSurvey(window->get(),
+                               kTestSurveyProductSpecificBitsData,
+                               kTestSurveyProductSpecificStringData);
+
+  delegate->AcceptSurvey();
+
+  std::string last_shown_trigger =
+      TestSurveyUtilsBridge::GetLastShownSurveyTriggerId();
+  ASSERT_FALSE(last_shown_trigger.empty());
+  ASSERT_EQ(last_shown_trigger, kSuppliedTriggerId);
 }
 
 }  // namespace hats
diff --git a/chrome/browser/ui/ash/glanceables/glanceables_browsertest.cc b/chrome/browser/ui/ash/glanceables/glanceables_browsertest.cc
index 36fe7b5..036a3601 100644
--- a/chrome/browser/ui/ash/glanceables/glanceables_browsertest.cc
+++ b/chrome/browser/ui/ash/glanceables/glanceables_browsertest.cc
@@ -41,6 +41,7 @@
 #include "ui/events/test/event_generator.h"
 #include "ui/views/controls/button/label_button.h"
 #include "ui/views/controls/label.h"
+#include "ui/views/controls/scroll_view.h"
 #include "ui/views/controls/textfield/textfield.h"
 #include "ui/views/view_utils.h"
 
@@ -147,6 +148,11 @@
         base::to_underlying(GlanceablesViewId::kTasksBubbleComboBox)));
   }
 
+  views::ScrollView* GetTasksScrollView() const {
+    return views::AsViewClass<views::ScrollView>(GetTasksView()->GetViewByID(
+        base::to_underlying(GlanceablesViewId::kTasksBubbleListScrollView)));
+  }
+
   views::View* GetTasksItemContainerView() const {
     return views::AsViewClass<views::View>(GetTasksView()->GetViewByID(
         base::to_underlying(GlanceablesViewId::kTasksBubbleListContainer)));
@@ -652,4 +658,61 @@
   }
 }
 
+IN_PROC_BROWSER_TEST_F(GlanceablesWithAddEditBrowserTest, TasksViewLayout) {
+  // Click the date tray to show the glanceable bubbles.
+  GetEventGenerator()->MoveMouseTo(
+      GetDateTray()->GetBoundsInScreen().CenterPoint());
+  GetEventGenerator()->ClickLeftButton();
+
+  ASSERT_TRUE(GetGlanceableTrayBubble());
+  ASSERT_TRUE(GetTasksView());
+
+  // Calculate the available space for tasks and make sure there is enough for
+  // additional task view.
+  auto display = display::Screen::GetScreen()->GetPrimaryDisplay();
+  const int kGlanceableMargins = 8;
+  const int kCalendarHeight = 340;
+  const int available_height_for_tasks =
+      display.work_area().height() - kCalendarHeight - kGlanceableMargins;
+  const int original_task_view_height = GetTasksView()->height();
+  ASSERT_GT(available_height_for_tasks, original_task_view_height);
+
+  const auto* const add_task_button =
+      views::AsViewClass<views::LabelButton>(GetTasksView()->GetViewByID(
+          base::to_underlying(GlanceablesViewId::kTasksBubbleAddNewButton)));
+  ASSERT_TRUE(add_task_button);
+
+  const auto* const task_items_container = GetTasksItemContainerView();
+  ASSERT_TRUE(task_items_container);
+
+  // Use the visibility of the scroll bar to determine if the contents of the
+  // scroll view is larger than its viewport. In this case, they should have the
+  // same sizes.
+  const auto* scroll_bar = GetTasksScrollView()->vertical_scroll_bar();
+  EXPECT_FALSE(scroll_bar->GetVisible());
+
+  // Click on `add_task_button` and verify that `task_items_container` has the
+  // new "pending" item.
+  EXPECT_EQ(task_items_container->children().size(), 2u);
+  GetEventGenerator()->MoveMouseTo(
+      add_task_button->GetBoundsInScreen().CenterPoint());
+  GetEventGenerator()->ClickLeftButton();
+  EXPECT_EQ(task_items_container->children().size(), 3u);
+
+  // The tasks view should update its height if there is space available.
+  EXPECT_GT(GetTasksView()->height(), original_task_view_height);
+  EXPECT_FALSE(scroll_bar->GetVisible());
+
+  // Commit the empty new task, which removes the temporary task view.
+  GetEventGenerator()->PressAndReleaseKey(ui::VKEY_ESCAPE);
+  base::RunLoop().RunUntilIdle();
+  GetTasksView()->GetWidget()->LayoutRootViewIfNecessary();
+  EXPECT_EQ(task_items_container->children().size(), 2u);
+
+  // Verify that the tasks view height is resized to its original height without
+  // the new task.
+  EXPECT_EQ(GetTasksView()->height(), original_task_view_height);
+  EXPECT_FALSE(scroll_bar->GetVisible());
+}
+
 }  // namespace ash
diff --git a/chrome/browser/ui/ash/shelf/chrome_shelf_controller_unittest.cc b/chrome/browser/ui/ash/shelf/chrome_shelf_controller_unittest.cc
index e3479fa..430035e1 100644
--- a/chrome/browser/ui/ash/shelf/chrome_shelf_controller_unittest.cc
+++ b/chrome/browser/ui/ash/shelf/chrome_shelf_controller_unittest.cc
@@ -1485,37 +1485,14 @@
     ChromeShelfControllerTestBase::SetUp();
 
     ASSERT_TRUE(ash::ProfileHelper::Get()->IsPrimaryProfile(profile()));
-  }
-
- private:
-  base::test::ScopedFeatureList feature_list_;
-  std::unique_ptr<user_manager::ScopedUserManager> scoped_user_manager_;
-};
-
-// TODO(hidehiko): Merge this base class into ChromeShelfControllerLacrosTest.
-class ChromeShelfControllerLacrosOnlyTest
-    : public ChromeShelfControllerLacrosTest {
- public:
-  ChromeShelfControllerLacrosOnlyTest() {
-    scoped_feature_list_.InitWithFeatures(
-        /*enabled_features=*/ash::standalone_browser::GetFeatureRefs(),
-        /*disabled_features=*/{});
-  }
-  ChromeShelfControllerLacrosOnlyTest(
-      const ChromeShelfControllerLacrosOnlyTest&) = delete;
-  ChromeShelfControllerLacrosOnlyTest& operator=(
-      const ChromeShelfControllerLacrosOnlyTest&) = delete;
-  ~ChromeShelfControllerLacrosOnlyTest() override = default;
-
-  void SetUp() override {
-    ChromeShelfControllerLacrosTest::SetUp();
-
     proxy_ = apps::AppServiceProxyFactory::GetForProfile(profile());
     ASSERT_TRUE(proxy_);
   }
 
   void TearDown() override {
-    ChromeShelfControllerLacrosTest::TearDown();
+    proxy_ = nullptr;
+    chrome_app_shelf_item_ = nullptr;
+    ChromeShelfControllerTestBase::TearDown();
     // Some test sets this so unsetting.
     ChromeShelfPrefs::SetSkipPinnedAppsFromSyncForTest(false);
   }
@@ -1552,11 +1529,11 @@
   apps::AppServiceProxy* proxy() { return proxy_; }
 
  private:
+  base::test::ScopedFeatureList feature_list_;
+  std::unique_ptr<user_manager::ScopedUserManager> scoped_user_manager_;
+  raw_ptr<apps::AppServiceProxy> proxy_ = nullptr;
   raw_ptr<StandaloneBrowserExtensionAppShelfItemController, DanglingUntriaged>
       chrome_app_shelf_item_ = nullptr;
-  raw_ptr<apps::AppServiceProxy, DanglingUntriaged> proxy_ = nullptr;
-
-  base::test::ScopedFeatureList scoped_feature_list_;
 };
 
 // A V1 windowed application.
@@ -1961,7 +1938,7 @@
 
 // Checks that AppService instance is updated appropriately for one Chrome app
 // window.
-TEST_F(ChromeShelfControllerLacrosOnlyTest, ChromeAppWindow) {
+TEST_F(ChromeShelfControllerLacrosTest, ChromeAppWindow) {
   InitShelfController();
 
   auto window = std::make_unique<aura::Window>(nullptr);
@@ -2014,7 +1991,7 @@
 
 // Checks that AppService instance is updated appropriately for multiple Chrome
 // app windows.
-TEST_F(ChromeShelfControllerLacrosOnlyTest, ChromeAppWindows) {
+TEST_F(ChromeShelfControllerLacrosTest, ChromeAppWindows) {
   InitShelfController();
 
   auto window1 = std::make_unique<aura::Window>(nullptr);
@@ -2082,7 +2059,7 @@
 }
 
 // Regression test for crash. crbug.com/1296949
-TEST_F(ChromeShelfControllerLacrosOnlyTest, WithoutAppService) {
+TEST_F(ChromeShelfControllerLacrosTest, WithoutAppService) {
   Profile* const controller_profile = profile()->GetOffTheRecordProfile(
       Profile::OTRProfileID::CreateUniqueForTesting(),
       /*create_if_needed=*/true);
diff --git a/chrome/browser/ui/autofill/chrome_autofill_client_unittest.cc b/chrome/browser/ui/autofill/chrome_autofill_client_unittest.cc
index 819a7bee..b2561ef 100644
--- a/chrome/browser/ui/autofill/chrome_autofill_client_unittest.cc
+++ b/chrome/browser/ui/autofill/chrome_autofill_client_unittest.cc
@@ -235,7 +235,7 @@
   EXPECT_CALL(*mock_hats_service,
               LaunchDelayedSurveyForWebContents(
                   kHatsSurveyTriggerAutofillAddressUserPerception, _, _,
-                  expected_bits, Ref(field_filling_stats_data), _, _, _, _));
+                  expected_bits, Ref(field_filling_stats_data), _, _, _, _, _));
 
   client()->TriggerUserPerceptionOfAutofillSurvey(field_filling_stats_data);
 }
diff --git a/chrome/browser/ui/hats/hats_service.cc b/chrome/browser/ui/hats/hats_service.cc
index 5964190..a3747d2 100644
--- a/chrome/browser/ui/hats/hats_service.cc
+++ b/chrome/browser/ui/hats/hats_service.cc
@@ -8,6 +8,14 @@
 
 HatsService::SurveyMetadata::~SurveyMetadata() = default;
 
+HatsService::SurveyOptions::SurveyOptions(
+    std::optional<std::u16string> custom_invitation,
+    std::optional<messages::MessageIdentifier> message_identifier)
+    : custom_invitation(custom_invitation),
+      message_identifier(message_identifier) {}
+HatsService::SurveyOptions::SurveyOptions(const SurveyOptions& other) = default;
+HatsService::SurveyOptions::~SurveyOptions() = default;
+
 HatsService::HatsService(Profile* profile) : profile_(profile) {
   hats::GetActiveSurveyConfigs(survey_configs_by_triggers_);
 }
diff --git a/chrome/browser/ui/hats/hats_service.h b/chrome/browser/ui/hats/hats_service.h
index 9a2b6cae..02405253 100644
--- a/chrome/browser/ui/hats/hats_service.h
+++ b/chrome/browser/ui/hats/hats_service.h
@@ -18,6 +18,7 @@
 #include "base/time/time.h"
 #include "chrome/browser/ui/hats/survey_config.h"
 #include "components/keyed_service/core/keyed_service.h"
+#include "components/messages/android/message_enums.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_contents_observer.h"
 
@@ -52,6 +53,18 @@
     std::optional<base::Time> any_last_survey_started_time;
   };
 
+  struct SurveyOptions {
+    explicit SurveyOptions(
+        std::optional<std::u16string> custom_invitation = std::nullopt,
+        std::optional<messages::MessageIdentifier> message_identifier =
+            std::nullopt);
+    SurveyOptions(const SurveyOptions& other);
+    ~SurveyOptions();
+
+    std::optional<std::u16string> custom_invitation;
+    std::optional<messages::MessageIdentifier> message_identifier;
+  };
+
   enum NavigationBehaviour {
     ALLOW_ANY = 0,              // allow any navigation
     REQUIRE_SAME_ORIGIN = 1,    // abort survey on cross-origin navigation
@@ -93,8 +106,8 @@
       const SurveyStringData& product_specific_string_data,
       base::OnceClosure success_callback = base::DoNothing(),
       base::OnceClosure failure_callback = base::DoNothing(),
-      const std::optional<std::string_view>& supplied_trigger_id =
-          std::nullopt) = 0;
+      const std::optional<std::string>& supplied_trigger_id = std::nullopt,
+      const SurveyOptions& survey_options = SurveyOptions()) = 0;
 
   // Launches survey (with id |trigger|) with a timeout |timeout_ms| if
   // appropriate.
@@ -127,8 +140,8 @@
       NavigationBehaviour navigation_behaviour = NavigationBehaviour::ALLOW_ANY,
       base::OnceClosure success_callback = base::DoNothing(),
       base::OnceClosure failure_callback = base::DoNothing(),
-      const std::optional<std::string_view>& supplied_trigger_id =
-          std::nullopt) = 0;
+      const std::optional<std::string>& supplied_trigger_id = std::nullopt,
+      const SurveyOptions& survey_options = SurveyOptions()) = 0;
 
   // Whether the user is eligible for any survey (of the type |user_prompted|
   // or not) to be shown. A return value of false is always a true-negative,
diff --git a/chrome/browser/ui/hats/hats_service_desktop.cc b/chrome/browser/ui/hats/hats_service_desktop.cc
index 73618bb..781ec8c5 100644
--- a/chrome/browser/ui/hats/hats_service_desktop.cc
+++ b/chrome/browser/ui/hats/hats_service_desktop.cc
@@ -84,7 +84,7 @@
 
 HatsServiceDesktop::DelayedSurveyTask::DelayedSurveyTask(
     HatsServiceDesktop* hats_service,
-    const std::string& trigger,
+    std::string trigger,
     content::WebContents* web_contents,
     const SurveyBitsData& product_specific_bits_data,
     const SurveyStringData& product_specific_string_data,
@@ -189,7 +189,12 @@
     const SurveyStringData& product_specific_string_data,
     base::OnceClosure success_callback,
     base::OnceClosure failure_callback,
-    const std::optional<std::string_view>& supplied_trigger_id) {
+    const std::optional<std::string>& supplied_trigger_id,
+    const SurveyOptions& survey_options) {
+  CHECK(!survey_options.custom_invitation.has_value() &&
+        !survey_options.message_identifier.has_value())
+      << "Custom invitation strings and message types are not supported on "
+         "desktop.";
   if (ShouldShowSurvey(trigger) && web_contents &&
       web_contents->GetVisibility() == content::Visibility::VISIBLE) {
     LaunchSurveyForBrowser(chrome::FindBrowserWithTab(web_contents), trigger,
@@ -223,7 +228,12 @@
     NavigationBehaviour navigation_behaviour,
     base::OnceClosure success_callback,
     base::OnceClosure failure_callback,
-    const std::optional<std::string_view>& supplied_trigger_id) {
+    const std::optional<std::string>& supplied_trigger_id,
+    const SurveyOptions& survey_options) {
+  CHECK(!survey_options.custom_invitation.has_value() &&
+        !survey_options.message_identifier.has_value())
+      << "Custom invitation strings and message types are not supported on "
+         "desktop.";
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   if (survey_configs_by_triggers_.find(trigger) ==
       survey_configs_by_triggers_.end()) {
diff --git a/chrome/browser/ui/hats/hats_service_desktop.h b/chrome/browser/ui/hats/hats_service_desktop.h
index 6c858ceb..501c317 100644
--- a/chrome/browser/ui/hats/hats_service_desktop.h
+++ b/chrome/browser/ui/hats/hats_service_desktop.h
@@ -40,7 +40,7 @@
   class DelayedSurveyTask : public content::WebContentsObserver {
    public:
     DelayedSurveyTask(HatsServiceDesktop* hats_service,
-                      const std::string& trigger,
+                      std::string trigger,
                       content::WebContents* web_contents,
                       const SurveyBitsData& product_specific_bits_data,
                       const SurveyStringData& product_specific_string_data,
@@ -131,8 +131,8 @@
       const SurveyStringData& product_specific_string_data,
       base::OnceClosure success_callback = base::DoNothing(),
       base::OnceClosure failure_callback = base::DoNothing(),
-      const std::optional<std::string_view>& supplied_trigger_id =
-          std::nullopt) override;
+      const std::optional<std::string>& supplied_trigger_id = std::nullopt,
+      const SurveyOptions& survey_options = SurveyOptions()) override;
 
   bool LaunchDelayedSurvey(
       const std::string& trigger,
@@ -149,8 +149,8 @@
       NavigationBehaviour navigation_behaviour = NavigationBehaviour::ALLOW_ANY,
       base::OnceClosure success_callback = base::DoNothing(),
       base::OnceClosure failure_callback = base::DoNothing(),
-      const std::optional<std::string_view>& supplied_trigger_id =
-          std::nullopt) override;
+      const std::optional<std::string>& supplied_trigger_id = std::nullopt,
+      const SurveyOptions& survey_options = SurveyOptions()) override;
 
   void SetSurveyMetadataForTesting(const HatsService::SurveyMetadata& metadata);
   void GetSurveyMetadataForTesting(HatsService::SurveyMetadata* metadata) const;
diff --git a/chrome/browser/ui/hats/mock_hats_service.h b/chrome/browser/ui/hats/mock_hats_service.h
index 6166865..5efb436 100644
--- a/chrome/browser/ui/hats/mock_hats_service.h
+++ b/chrome/browser/ui/hats/mock_hats_service.h
@@ -39,7 +39,8 @@
                (const SurveyStringData&)survey_specific_string_data,
                base::OnceClosure success_callback,
                base::OnceClosure failure_callback,
-               const std::optional<std::string_view>& supplied_trigger_id),
+               const std::optional<std::string>& supplied_trigger_id,
+               const HatsService::SurveyOptions& survey_options),
               (override));
   MOCK_METHOD(bool,
               LaunchDelayedSurvey,
@@ -58,7 +59,8 @@
                (HatsService::NavigationBehaviour)navigation_behaviour,
                base::OnceClosure success_callback,
                base::OnceClosure failure_callback,
-               const std::optional<std::string_view>& supplied_trigger_id),
+               const std::optional<std::string>& supplied_trigger_id,
+               const HatsService::SurveyOptions& survey_options),
               (override));
   MOCK_METHOD(void, HatsNextDialogClosed, (), (override));
   MOCK_METHOD(bool, CanShowAnySurvey, (bool user_prompted), (const override));
diff --git a/chrome/browser/ui/hats/survey_config.cc b/chrome/browser/ui/hats/survey_config.cc
index 989c0f42..6c53306 100644
--- a/chrome/browser/ui/hats/survey_config.cc
+++ b/chrome/browser/ui/hats/survey_config.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "survey_config.h"
+#include <optional>
 
 #include "base/feature_list.h"
 #include "base/features.h"
@@ -139,28 +140,22 @@
   default_survey.product_specific_string_data_fields = {"Test Field 3"};
   survey_configs.emplace_back(default_survey);
 
-  // Permissions surveys.
-  for (auto& trigger_id_pair : permissions::PermissionHatsTriggerHelper::
-           GetPermissionPromptTriggerIdPairs(
-               kHatsSurveyTriggerPermissionsPrompt)) {
-    // trigger_id_pair has structure <trigger_name, trigger_id>. trigger_name is
-    // a unique name used by the HaTS service integration, and trigger_id is an
-    // ID that specifies a survey in the Listnr backend.
-    survey_configs.emplace_back(
-        &permissions::features::kPermissionsPromptSurvey, trigger_id_pair.first,
-        trigger_id_pair.second,
-        std::vector<std::string>{
-            permissions::kPermissionsPromptSurveyHadGestureKey},
-        std::vector<std::string>{
-            permissions::kPermissionsPromptSurveyPromptDispositionKey,
-            permissions::kPermissionsPromptSurveyPromptDispositionReasonKey,
-            permissions::kPermissionsPromptSurveyActionKey,
-            permissions::kPermissionsPromptSurveyRequestTypeKey,
-            permissions::kPermissionsPromptSurveyReleaseChannelKey,
-            permissions::kPermissionsPromptSurveyDisplayTimeKey,
-            permissions::kPermissionPromptSurveyOneTimePromptsDecidedBucketKey,
-            permissions::kPermissionPromptSurveyUrlKey});
-  }
+  // Permission prompt survey
+  survey_configs.emplace_back(
+      &permissions::features::kPermissionsPromptSurvey,
+      kHatsSurveyTriggerPermissionsPrompt,
+      /*presupplied_trigger_id=*/std::nullopt,
+      std::vector<std::string>{
+          permissions::kPermissionsPromptSurveyHadGestureKey},
+      std::vector<std::string>{
+          permissions::kPermissionsPromptSurveyPromptDispositionKey,
+          permissions::kPermissionsPromptSurveyPromptDispositionReasonKey,
+          permissions::kPermissionsPromptSurveyActionKey,
+          permissions::kPermissionsPromptSurveyRequestTypeKey,
+          permissions::kPermissionsPromptSurveyReleaseChannelKey,
+          permissions::kPermissionsPromptSurveyDisplayTimeKey,
+          permissions::kPermissionPromptSurveyOneTimePromptsDecidedBucketKey,
+          permissions::kPermissionPromptSurveyUrlKey});
 
 #if !BUILDFLAG(IS_ANDROID)
   // Dev tools surveys.
diff --git a/chrome/browser/ui/startup/first_run_service.cc b/chrome/browser/ui/startup/first_run_service.cc
index 6e0890cd..c5f8786 100644
--- a/chrome/browser/ui/startup/first_run_service.cc
+++ b/chrome/browser/ui/startup/first_run_service.cc
@@ -201,6 +201,23 @@
     return;
   }
 
+  auto policy_effect = ComputeDevicePolicyEffect(*profile_);
+  // This check should be done prior to the profile already set up check below,
+  // to include the case where the feature `kForceSigninFlowInProfilePicker` is
+  // enabled which would cause the profile to be signed in already at this
+  // point.
+  if (policy_effect != PolicyEffect::kNone &&
+      signin_util::IsForceSigninEnabled() &&
+      base::FeatureList::IsEnabled(kForceSigninFlowInProfilePicker)) {
+    // When ForceSignin is enabled and the flows are going through the profile
+    // picker, the final profile setup should not yet be reached. The
+    // rest of the flow is still happening within the Profile Picker, either
+    // the management acceptance screen for Managed accounts, or the Sync
+    // Confirmation screen for Consumer accounts.
+    FinishFirstRun(FinishedReason::kForceSignin);
+    return;
+  }
+
   bool has_set_up_profile =
 #if BUILDFLAG(IS_CHROMEOS_LACROS)
       // Indicates that the profile was likely migrated from pre-Lacros Ash.
@@ -217,8 +234,6 @@
     return;
   }
 
-  auto policy_effect = ComputeDevicePolicyEffect(*profile_);
-
 #if BUILDFLAG(IS_CHROMEOS_LACROS)
   switch (policy_effect) {
     case PolicyEffect::kDisabled:
@@ -241,18 +256,7 @@
 #endif
 
   if (policy_effect != PolicyEffect::kNone) {
-    FinishedReason reason = FinishedReason::kSkippedByPolicies;
-    if (signin_util::IsForceSigninEnabled() &&
-        base::FeatureList::IsEnabled(kForceSigninFlowInProfilePicker)) {
-      // When ForceSignin is enabled and the flows are going through the profile
-      // picker, the final profile setup should not yet be reached. The
-      // rest of the flow is still happening within the Profile Picker, either
-      // the management acceptance screen for Managed accounts, or the Sync
-      // Confirmation screen for Consumer accounts.
-      reason = FinishedReason::kForceSignin;
-    }
-
-    FinishFirstRun(reason);
+    FinishFirstRun(FinishedReason::kSkippedByPolicies);
     return;
   }
 
@@ -353,7 +357,10 @@
   }
 #endif
 
-  if (identity_manager_->HasPrimaryAccount(signin::ConsentLevel::kSignin)) {
+  // If the reason is `FinishedReason::kForceSignin` the profile is already
+  // signed in and finalized. It should not finish the setup again.
+  if (identity_manager_->HasPrimaryAccount(signin::ConsentLevel::kSignin) &&
+      reason != FinishedReason::kForceSignin) {
     // Noting that we expect that the name should already be available, as
     // after sign-in, the extended info is fetched and used for the sync
     // opt-in screen.
diff --git a/chrome/browser/ui/tabs/tab_strip_model_browsertest.cc b/chrome/browser/ui/tabs/tab_strip_model_browsertest.cc
index 13cc951..658e00f7 100644
--- a/chrome/browser/ui/tabs/tab_strip_model_browsertest.cc
+++ b/chrome/browser/ui/tabs/tab_strip_model_browsertest.cc
@@ -9,6 +9,7 @@
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/values.h"
+#include "build/chromeos_buildflags.h"
 #include "chrome/browser/policy/policy_test_utils.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
@@ -129,7 +130,7 @@
 }
 
 // TODO(b/321593065): enable this flaky test.
-#if BUILDFLAG(IS_CHROMEOS)
+#if BUILDFLAG(IS_CHROMEOS_ASH)
 #define MAYBE_PreventCloseEnforcedByPolicyTabbedAppShallBeClosable \
   DISABLED_PreventCloseEnforcedByPolicyTabbedAppShallBeClosable
 #else
diff --git a/chrome/browser/ui/unload_controller_browsertest.cc b/chrome/browser/ui/unload_controller_browsertest.cc
index a759193..b00760f 100644
--- a/chrome/browser/ui/unload_controller_browsertest.cc
+++ b/chrome/browser/ui/unload_controller_browsertest.cc
@@ -7,6 +7,7 @@
 #include "base/json/json_reader.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/values.h"
+#include "build/chromeos_buildflags.h"
 #include "chrome/browser/policy/policy_test_utils.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
@@ -84,7 +85,7 @@
 }
 
 // TODO(b/321593065): enable this flaky test.
-#if BUILDFLAG(IS_CHROMEOS)
+#if BUILDFLAG(IS_CHROMEOS_ASH)
 #define MAYBE_PreventCloseEnforcedByPolicyTabbedAppShallBeClosable \
   DISABLED_PreventCloseEnforcedByPolicyTabbedAppShallBeClosable
 #else
diff --git a/chrome/browser/ui/views/autofill/popup/popup_row_factory_utils.cc b/chrome/browser/ui/views/autofill/popup/popup_row_factory_utils.cc
index 8a328d75..bc517bf 100644
--- a/chrome/browser/ui/views/autofill/popup/popup_row_factory_utils.cc
+++ b/chrome/browser/ui/views/autofill/popup/popup_row_factory_utils.cc
@@ -212,9 +212,8 @@
     FillingProduct main_filling_product) {
   auto view = std::make_unique<PopupRowContentView>();
   std::unique_ptr<views::Label> main_text_label =
-      popup_cell_utils::CreateMainTextLabel(
-          suggestion.main_text,
-          GetMainTextStyleForPopupItemId(suggestion.popup_item_id));
+      popup_cell_utils::CreateMainTextLabel(suggestion.main_text,
+                                            GetPrimaryTextStyle());
   popup_cell_utils::FormatLabel(
       *main_text_label, suggestion.main_text, main_filling_product,
       popup_cell_utils::GetMaxPopupAddressProfileWidth());
diff --git a/chrome/browser/ui/views/autofill/popup/popup_row_factory_utils_browsertest.cc b/chrome/browser/ui/views/autofill/popup/popup_row_factory_utils_browsertest.cc
index 7004f12..12e55bc 100644
--- a/chrome/browser/ui/views/autofill/popup/popup_row_factory_utils_browsertest.cc
+++ b/chrome/browser/ui/views/autofill/popup/popup_row_factory_utils_browsertest.cc
@@ -53,6 +53,11 @@
                "label",
                Suggestion::Icon::kLocation,
                PopupItemId::kAddressEntry),
+    Suggestion("Fill_Full_Email_entry",
+               "Minor text",
+               "label",
+               Suggestion::Icon::kNoIcon,
+               PopupItemId::kFillFullEmail),
     CreatePasswordSuggestion(u"Password_entry"),
     Suggestion("Autofill_options",
                "Minor text",
diff --git a/chrome/browser/ui/views/autofill/popup/popup_view_utils.cc b/chrome/browser/ui/views/autofill/popup/popup_view_utils.cc
index 747fd07fa..e0144fe 100644
--- a/chrome/browser/ui/views/autofill/popup/popup_view_utils.cc
+++ b/chrome/browser/ui/views/autofill/popup/popup_view_utils.cc
@@ -537,55 +537,6 @@
   return arrow;
 }
 
-int GetMainTextStyleForPopupItemId(PopupItemId popup_item_id) {
-  switch (popup_item_id) {
-    case PopupItemId::kFillFullEmail:
-    case PopupItemId::kFillFullPhoneNumber:
-    case PopupItemId::kFillFullAddress:
-    case PopupItemId::kFillFullName:
-      // The style for non-footer suggestions with lowered visual prominence.
-      return views::style::TextStyle::STYLE_BODY_3;
-    case PopupItemId::kAccountStoragePasswordEntry:
-    case PopupItemId::kAccountStorageUsernameEntry:
-    case PopupItemId::kAddressEntry:
-    case PopupItemId::kAddressFieldByFieldFilling:
-    case PopupItemId::kAllSavedPasswordsEntry:
-    case PopupItemId::kAutocompleteEntry:
-    case PopupItemId::kAutofillOptions:
-    case PopupItemId::kClearForm:
-    case PopupItemId::kCompose:
-    case PopupItemId::kCreateNewPlusAddress:
-    case PopupItemId::kCreditCardEntry:
-    case PopupItemId::kCreditCardFieldByFieldFilling:
-    case PopupItemId::kDatalistEntry:
-    case PopupItemId::kDeleteAddressProfile:
-    case PopupItemId::kDevtoolsTestAddressEntry:
-    case PopupItemId::kDevtoolsTestAddresses:
-    case PopupItemId::kEditAddressProfile:
-    case PopupItemId::kFillEverythingFromAddressProfile:
-    case PopupItemId::kFillExistingPlusAddress:
-    case PopupItemId::kGeneratePasswordEntry:
-    case PopupItemId::kIbanEntry:
-    case PopupItemId::kInsecureContextPaymentDisabledMessage:
-    case PopupItemId::kMerchantPromoCodeEntry:
-    case PopupItemId::kMixedFormMessage:
-    case PopupItemId::kPasswordAccountStorageEmpty:
-    case PopupItemId::kPasswordAccountStorageOptIn:
-    case PopupItemId::kPasswordAccountStorageOptInAndGenerate:
-    case PopupItemId::kPasswordAccountStorageReSignin:
-    case PopupItemId::kPasswordEntry:
-    case PopupItemId::kScanCreditCard:
-    case PopupItemId::kSeePromoCodeDetails:
-    case PopupItemId::kSeparator:
-    case PopupItemId::kShowAccountCards:
-    case PopupItemId::kUsernameEntry:
-    case PopupItemId::kVirtualCreditCardEntry:
-    case PopupItemId::kWebauthnCredential:
-    case PopupItemId::kWebauthnSignInWithAnotherDevice:
-      return GetPrimaryTextStyle();
-  }
-}
-
 bool IsFooterPopupItemId(PopupItemId popup_item_id) {
   switch (popup_item_id) {
     case PopupItemId::kScanCreditCard:
diff --git a/chrome/browser/ui/views/autofill/popup/popup_view_utils.h b/chrome/browser/ui/views/autofill/popup/popup_view_utils.h
index 936dec6..2f8f72d 100644
--- a/chrome/browser/ui/views/autofill/popup/popup_view_utils.h
+++ b/chrome/browser/ui/views/autofill/popup/popup_view_utils.h
@@ -168,9 +168,6 @@
 // either to the main or the footer section.
 bool IsFooterPopupItemId(PopupItemId popup_item_id);
 
-// TODO(crbug.com/1491373): Move to popup_cell_utils.h/cc.
-int GetMainTextStyleForPopupItemId(PopupItemId popup_item_id);
-
 // Return whether the suggestion with this `popup_item_id` can have child
 // suggestions.
 bool IsExpandablePopupItemId(PopupItemId popup_item_id);
diff --git a/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_frame_toolbar_browsertest.cc b/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_frame_toolbar_browsertest.cc
index 14222ab..4fb5118 100644
--- a/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_frame_toolbar_browsertest.cc
+++ b/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_frame_toolbar_browsertest.cc
@@ -17,6 +17,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/bind.h"
 #include "base/test/icu_test_util.h"
+#include "base/test/run_until.h"
 #include "base/test/test_future.h"
 #include "base/test/test_timeouts.h"
 #include "build/build_config.h"
@@ -1775,6 +1776,11 @@
         browser(), std::move(web_app_info), start_url);
   }
 
+  bool MatchMediaMatches(content::WebContents* web_contents,
+                         std::string match_media_script) {
+    return EvalJs(web_contents, match_media_script).ExtractBool();
+  }
+
   void SetResizableAndWait(content::WebContents* web_contents,
                            bool resizable,
                            bool expected) {
@@ -1783,13 +1789,11 @@
     EXPECT_TRUE(ExecJs(web_contents, set_resizable_script));
     content::WaitForLoadStop(web_contents);
 
-    auto MatchMediaMatches = [&web_contents, &expected]() {
-      auto match_media_script = content::JsReplace(
-          "window.matchMedia('(resizable: $1)').matches", expected);
-      return EvalJs(web_contents, match_media_script).ExtractBool();
-    };
-
-    while (!MatchMediaMatches()) {
+    // TODO(crbug.com/1519130): `base::test::RunUntil` times out on mac.
+    while (!MatchMediaMatches(
+        web_contents,
+        content::JsReplace("window.matchMedia('(resizable: $1)').matches",
+                           expected))) {
       base::RunLoop run_loop;
       base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
           FROM_HERE, run_loop.QuitClosure(), TestTimeouts::tiny_timeout());
@@ -2022,6 +2026,68 @@
   EXPECT_EQ(EvalJs(web_contents, "window.screenY").ExtractInt(), initial_pos_y);
 }
 #endif  // defined(USE_AURA)
+
+#if !BUILDFLAG(IS_MAC)
+IN_PROC_BROWSER_TEST_F(
+    WebAppFrameToolbarBrowserTest_AdditionalWindowingControls,
+    MinimizeWindowWithApi) {
+  InstallAndLaunchWebApp();
+  helper()->GrantWindowManagementPermission();
+  auto* web_contents = helper()->browser_view()->GetActiveWebContents();
+
+  // Ensure minimizing is allowed.
+  helper()->browser_view()->SetCanMinimize(true);
+  EXPECT_TRUE(helper()->browser_view()->CanMinimize());
+  content::WaitForLoadStop(web_contents);
+
+  // Minimize window
+  EXPECT_TRUE(ExecJs(web_contents, "window.minimize()"));
+  EXPECT_TRUE(base::test::RunUntil(
+      [&]() { return helper()->browser_view()->IsMinimized(); }));
+
+  // On Windows the minimizing seems to be so fast that it doesn't have
+  // sufficient time to update the CSS before it already minimized.
+#if !BUILDFLAG(IS_WIN)
+  EXPECT_TRUE(base::test::RunUntil([&]() {
+    return MatchMediaMatches(
+        web_contents,
+        "window.matchMedia('(display-state: minimized)').matches");
+  }));
+#endif
+}
+
+IN_PROC_BROWSER_TEST_F(
+    WebAppFrameToolbarBrowserTest_AdditionalWindowingControls,
+    MaximizeAndRestoreWindowWithApi) {
+  InstallAndLaunchWebApp();
+  helper()->GrantWindowManagementPermission();
+  auto* web_contents = helper()->browser_view()->GetActiveWebContents();
+
+  // Ensure maximizing is allowed.
+  helper()->browser_view()->SetCanMaximize(true);
+  EXPECT_TRUE(helper()->browser_view()->CanMaximize());
+  content::WaitForLoadStop(web_contents);
+
+  // Maximize window
+  EXPECT_TRUE(ExecJs(web_contents, "window.maximize()"));
+  EXPECT_TRUE(base::test::RunUntil(
+      [&]() { return helper()->browser_view()->IsMaximized(); }));
+  EXPECT_TRUE(base::test::RunUntil([&]() {
+    return MatchMediaMatches(
+        web_contents,
+        "window.matchMedia('(display-state: maximized)').matches");
+  }));
+
+  // Restore window
+  EXPECT_TRUE(ExecJs(web_contents, "window.restore()"));
+  EXPECT_TRUE(base::test::RunUntil(
+      [&]() { return !helper()->browser_view()->IsMaximized(); }));
+  EXPECT_TRUE(base::test::RunUntil([&]() {
+    return MatchMediaMatches(
+        web_contents, "window.matchMedia('(display-state: normal)').matches");
+  }));
+}
+#endif  // !BUILDFLAG(IS_MAC)
 #endif  // !BUILDFLAG(IS_ANDROID)
 
 class OriginTextVisibilityWaiter : public views::ViewObserver {
diff --git a/chrome/browser/ui/webui/ash/settings/pages/privacy/privacy_section.cc b/chrome/browser/ui/webui/ash/settings/pages/privacy/privacy_section.cc
index b252436c..2340e32 100644
--- a/chrome/browser/ui/webui/ash/settings/pages/privacy/privacy_section.cc
+++ b/chrome/browser/ui/webui/ash/settings/pages/privacy/privacy_section.cc
@@ -534,7 +534,10 @@
        IDS_OS_SETTINGS_PRIVACY_HUB_NO_APP_CAN_USE_CAMERA_TEXT},
       {"noAppCanUseGeolocationText",
        IDS_OS_SETTINGS_PRIVACY_HUB_NO_APP_CAN_USE_LOCATION_TEXT},
-      {"blockedForAllText", IDS_OS_SETTINGS_PRIVACY_HUB_BLOCKED_FOR_ALL_TEXT},
+      {"privacyHubCameraAccessBlockedText",
+       IDS_OS_SETTINGS_PRIVACY_HUB_CAMERA_ACCESS_BLOCKED_TEXT},
+      {"privacyHubMicrophoneAccessBlockedText",
+       IDS_OS_SETTINGS_PRIVACY_HUB_MICROPHONE_ACCESS_BLOCKED_TEXT},
       {"privacyHubSystemServicesSectionTitle",
        IDS_OS_SETTINGS_PRIVACY_HUB_SYSTEM_SERVICES_SECTION_TITLE},
       {"privacyHubSystemServicesAllowedText",
diff --git a/chrome/browser/ui/webui/extensions/extensions_hats_handler_unittest.cc b/chrome/browser/ui/webui/extensions/extensions_hats_handler_unittest.cc
index 13d6a39b..03a11f5 100644
--- a/chrome/browser/ui/webui/extensions/extensions_hats_handler_unittest.cc
+++ b/chrome/browser/ui/webui/extensions/extensions_hats_handler_unittest.cc
@@ -155,7 +155,7 @@
       LaunchDelayedSurveyForWebContents(
           "HappinessTrackingSurveysExtensionsSafetyHub", web_contents(), 15000,
           _, expected_product_specific_data,
-          HatsService::NavigationBehaviour::REQUIRE_SAME_ORIGIN, _, _, _))
+          HatsService::NavigationBehaviour::REQUIRE_SAME_ORIGIN, _, _, _, _))
       .Times(1);
   ExtensionsSafetyHubTriggerSurvey();
   task_environment()->RunUntilIdle();
@@ -181,7 +181,7 @@
       LaunchDelayedSurveyForWebContents(
           "HappinessTrackingSurveysExtensionsSafetyHub", web_contents(), 15000,
           _, expected_product_specific_data,
-          HatsService::NavigationBehaviour::REQUIRE_SAME_ORIGIN, _, _, _))
+          HatsService::NavigationBehaviour::REQUIRE_SAME_ORIGIN, _, _, _, _))
       .Times(1);
 
   ExtensionsSafetyHubExtensionKept();
diff --git a/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler_unittest.cc b/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler_unittest.cc
index 874191f..9b62389 100644
--- a/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler_unittest.cc
+++ b/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler_unittest.cc
@@ -1028,7 +1028,7 @@
       {});
 
   EXPECT_CALL(*mock_hats_service(),
-              LaunchDelayedSurveyForWebContents(_, _, _, _, _, _, _, _, _))
+              LaunchDelayedSurveyForWebContents(_, _, _, _, _, _, _, _, _, _))
       .Times(1);
   const std::vector<std::string> module_ids = {"recipe_tasks", "cart"};
   handler_->OnModulesLoadedWithData(module_ids);
@@ -1051,7 +1051,7 @@
       {});
 
   EXPECT_CALL(*mock_hats_service(),
-              LaunchDelayedSurveyForWebContents(_, _, _, _, _, _, _, _, _))
+              LaunchDelayedSurveyForWebContents(_, _, _, _, _, _, _, _, _, _))
       .Times(0);
   const std::vector<std::string> module_ids = {"recipe_tasks"};
   handler_->OnModulesLoadedWithData(module_ids);
@@ -1357,11 +1357,11 @@
                                                       "use"};
   for (const auto& interaction : kInteractionNames) {
     int timeout_ms;
-    std::optional<std::string_view> supplied_trigger_id;
+    std::optional<std::string> supplied_trigger_id;
     EXPECT_CALL(*mock_hats_service(),
                 LaunchDelayedSurveyForWebContents(kHatsSurveyTriggerNtpModules,
                                                   web_contents_.get(), _, _, _,
-                                                  _, _, _, _))
+                                                  _, _, _, _, _))
         .Times(1)
         .WillOnce(DoAll(SaveArg<2>(&timeout_ms),
                         SaveArg<8>(&supplied_trigger_id),
@@ -1398,11 +1398,11 @@
       base::Value::Dict().Set(NewTabPageHandlerHaTSTest::kSampleModuleId, 0));
 
   int timeout_ms;
-  std::optional<std::string_view> supplied_trigger_id;
+  std::optional<std::string> supplied_trigger_id;
   EXPECT_CALL(*mock_hats_service(),
               LaunchDelayedSurveyForWebContents(kHatsSurveyTriggerNtpModules,
                                                 web_contents_.get(), _, _, _, _,
-                                                _, _, _))
+                                                _, _, _, _))
       .Times(1)
       .WillOnce(DoAll(SaveArg<2>(&timeout_ms), SaveArg<8>(&supplied_trigger_id),
                       testing::Return(true)));
@@ -1427,7 +1427,7 @@
   EXPECT_CALL(*mock_hats_service(),
               LaunchDelayedSurveyForWebContents(kHatsSurveyTriggerNtpModules,
                                                 web_contents_.get(), _, _, _, _,
-                                                _, _, _))
+                                                _, _, _, _))
       .Times(0);
   const std::vector<std::string> module_ids = {
       NewTabPageHandlerHaTSTest::kSampleModuleId};
diff --git a/chrome/browser/ui/webui/settings/hats_handler_unittest.cc b/chrome/browser/ui/webui/settings/hats_handler_unittest.cc
index c02d454..ff3c626 100644
--- a/chrome/browser/ui/webui/settings/hats_handler_unittest.cc
+++ b/chrome/browser/ui/webui/settings/hats_handler_unittest.cc
@@ -110,7 +110,7 @@
       LaunchDelayedSurveyForWebContents(
           kHatsSurveyTriggerSettingsPrivacy, web_contents(), 15000,
           expected_product_specific_data, _,
-          HatsService::NavigationBehaviour::REQUIRE_SAME_ORIGIN, _, _, _))
+          HatsService::NavigationBehaviour::REQUIRE_SAME_ORIGIN, _, _, _, _))
       .Times(2);
   base::Value::List args;
   args.Append(
@@ -130,7 +130,7 @@
       *mock_hats_service_,
       LaunchDelayedSurveyForWebContents(
           kHatsSurveyTriggerPrivacyGuide, web_contents(), 15000, _, _,
-          HatsService::NavigationBehaviour::REQUIRE_SAME_ORIGIN, _, _, _))
+          HatsService::NavigationBehaviour::REQUIRE_SAME_ORIGIN, _, _, _, _))
       .Times(1);
   base::Value::List args;
   args.Append(static_cast<int>(
@@ -147,7 +147,7 @@
       *mock_hats_service_,
       LaunchDelayedSurveyForWebContents(
           kHatsSurveyTriggerGetMostChrome, web_contents(), _, _, _,
-          HatsService::NavigationBehaviour::REQUIRE_SAME_DOCUMENT, _, _, _))
+          HatsService::NavigationBehaviour::REQUIRE_SAME_DOCUMENT, _, _, _, _))
       .Times(1);
   base::Value::List args;
   args.Append(static_cast<int>(
@@ -348,7 +348,7 @@
         *mock_hats_service_,
         LaunchDelayedSurveyForWebContents(
             survey, web_contents(), 20000, expected_product_specific_data, _,
-            HatsService::NavigationBehaviour::REQUIRE_SAME_ORIGIN, _, _, _));
+            HatsService::NavigationBehaviour::REQUIRE_SAME_ORIGIN, _, _, _, _));
     base::Value::List args;
     args.Append(static_cast<int>(interaction));
     handler()->HandleTrustSafetyInteractionOccurred(args);
diff --git a/chrome/browser/ui/webui/settings/search_engines_handler_unittest.cc b/chrome/browser/ui/webui/settings/search_engines_handler_unittest.cc
index 5969e51..51b1122a 100644
--- a/chrome/browser/ui/webui/settings/search_engines_handler_unittest.cc
+++ b/chrome/browser/ui/webui/settings/search_engines_handler_unittest.cc
@@ -125,13 +125,13 @@
     SearchEnginesHandlerTestBase::SetUp();
 
     if (WithSearchEnginesChoiceEnabled()) {
-      PrefService* pref_service = profile()->GetPrefs();
       // The search engine choice feature is only enabled for countries in the
       // EEA region.
       const int kBelgiumCountryId =
           country_codes::CountryCharsToCountryID('B', 'E');
-      pref_service->SetInteger(country_codes::kCountryIDAtInstall,
-                               kBelgiumCountryId);
+      base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
+          switches::kSearchEngineChoiceCountry,
+          country_codes::CountryIDToCountryString(kBelgiumCountryId));
     }
   }
 
@@ -205,13 +205,13 @@
   void SetUp() override {
     SearchEnginesHandlerTestBase::SetUp();
 
-    PrefService* pref_service = profile()->GetPrefs();
     // The search engine choice feature is only enabled for countries in the
     // EEA region.
     const int kBelgiumCountryId =
         country_codes::CountryCharsToCountryID('B', 'E');
-    pref_service->SetInteger(country_codes::kCountryIDAtInstall,
-                             kBelgiumCountryId);
+    base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
+        switches::kSearchEngineChoiceCountry,
+        country_codes::CountryIDToCountryString(kBelgiumCountryId));
   }
 };
 
@@ -222,8 +222,9 @@
   // region.
   const int kBelgiumCountryId =
       country_codes::CountryCharsToCountryID('B', 'E');
-  pref_service->SetInteger(country_codes::kCountryIDAtInstall,
-                           kBelgiumCountryId);
+  base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
+      switches::kSearchEngineChoiceCountry,
+      country_codes::CountryIDToCountryString(kBelgiumCountryId));
 
   EXPECT_FALSE(pref_service->HasPrefPath(
       prefs::kDefaultSearchProviderChoiceScreenCompletionTimestamp));
@@ -248,15 +249,15 @@
 
 TEST_F(SearchEnginesHandlerTestWithSearchEngineChoiceEnabled,
        RecordingSearchEngineShouldBeDoneAfterSettingDefault) {
-  PrefService* pref_service = profile()->GetPrefs();
   TemplateURLService* template_url_service =
       TemplateURLServiceFactory::GetForProfile(profile());
   // The search engine choice feature is only enabled for countries in the EEA
   // region.
   const int kBelgiumCountryId =
       country_codes::CountryCharsToCountryID('B', 'E');
-  pref_service->SetInteger(country_codes::kCountryIDAtInstall,
-                           kBelgiumCountryId);
+  base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
+      switches::kSearchEngineChoiceCountry,
+      country_codes::CountryIDToCountryString(kBelgiumCountryId));
 
   const TemplateURL* default_search_engine =
       template_url_service->GetDefaultSearchProvider();
diff --git a/chrome/browser/ui/webui/settings/settings_ui_browsertest.cc b/chrome/browser/ui/webui/settings/settings_ui_browsertest.cc
index 8d4b847..24b7710 100644
--- a/chrome/browser/ui/webui/settings/settings_ui_browsertest.cc
+++ b/chrome/browser/ui/webui/settings/settings_ui_browsertest.cc
@@ -56,7 +56,7 @@
           browser()->profile(), base::BindRepeating(&BuildMockHatsService)));
   EXPECT_CALL(*mock_hats_service_,
               LaunchDelayedSurveyForWebContents(kHatsSurveyTriggerSettings, _,
-                                                _, _, _, _, _, _, _));
+                                                _, _, _, _, _, _, _, _));
   ASSERT_TRUE(NavigateToURL(browser(), GURL(chrome::kChromeUISettingsURL)));
   base::RunLoop().RunUntilIdle();
 }
diff --git a/chrome/browser/ui/webui/settings/site_settings_handler.cc b/chrome/browser/ui/webui/settings/site_settings_handler.cc
index deeed02f..5ad75538 100644
--- a/chrome/browser/ui/webui/settings/site_settings_handler.cc
+++ b/chrome/browser/ui/webui/settings/site_settings_handler.cc
@@ -308,7 +308,9 @@
     }
   }
   group->second.insert({origin, is_partitioned});
-  auto placeholder = group->second.find({placeholder_origin, is_partitioned});
+  // Find the placeholder with unpartitioned state as it's no longer needed.
+  auto placeholder =
+      group->second.find({placeholder_origin, /*is_partitioned=*/false});
   if (placeholder != group->second.end()) {
     group->second.erase(placeholder);
   }
@@ -471,12 +473,18 @@
     model->DeleteCookieNode(node);
 }
 
-// Returns the registable domain (eTLD+1) for the `origin`. If it doesn't exist,
+// Returns the registrable domain (eTLD+1) for the `host`. If it doesn't exist,
 // returns the host.
-std::string GetEtldPlusOne(const url::Origin& origin) {
+std::string GetEtldPlusOneForHost(const std::string& host) {
   auto eltd_plus_one = net::registry_controlled_domains::GetDomainAndRegistry(
-      origin, net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES);
-  return eltd_plus_one.empty() ? origin.host() : eltd_plus_one;
+      host, net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES);
+  return eltd_plus_one.empty() ? host : eltd_plus_one;
+}
+
+// Returns the registrable domain (eTLD+1) for the `origin`. If it doesn't
+// exist, returns the host.
+std::string GetEtldPlusOne(const url::Origin& origin) {
+  return GetEtldPlusOneForHost(origin.host());
 }
 
 // Converts |etld_plus1| into an HTTPS SchemefulSite.
@@ -486,18 +494,20 @@
                                  "/"));
 }
 
-// Iterates over host nodes in `tree_model` which contains all sites that have
-// storage set and uses them to retrieve first party set membership information.
-// Returns a map of site eTLD+1 matched with their FPS owner and count of first
-// party set members.
+// Iterates over host nodes in `cookies_tree_model` and data owners in
+// `browsing_data_model` which contains all sites that have storage set and uses
+// them to retrieve first party set membership information. Returns a map of
+// site eTLD+1 matched with their FPS owner and count of first party set
+// members.
 std::map<std::string, std::pair<std::string, int>> GetFpsMap(
     PrivacySandboxService* privacy_sandbox_service,
-    CookiesTreeModel* tree_model) {
+    CookiesTreeModel* cookies_tree_model,
+    BrowsingDataModel* browsing_data_model) {
   // Used to count unique eTLD+1 owned by a FPS owner.
   std::map<std::string, std::set<std::string>> fps_owner_to_members;
 
   // Count members by unique eTLD+1 for each first party set.
-  for (const auto& host_node : tree_model->GetRoot()->children()) {
+  for (const auto& host_node : cookies_tree_model->GetRoot()->children()) {
     std::string etld_plus1 =
         GetEtldPlusOne(host_node->GetDetailedInfo().origin);
     auto schemeful_site = ConvertEtldToSchemefulSite(etld_plus1);
@@ -508,6 +518,19 @@
     }
   }
 
+  if (browsing_data_model) {
+    for (const auto& entry : *browsing_data_model) {
+      std::string etld_plus1 = GetEtldPlusOneForHost(
+          BrowsingDataModel::GetHost(entry.data_owner.get()));
+      auto schemeful_site = ConvertEtldToSchemefulSite(etld_plus1);
+      auto fps_owner = privacy_sandbox_service->GetFirstPartySetOwner(
+          schemeful_site.GetURL());
+      if (fps_owner.has_value()) {
+        fps_owner_to_members[fps_owner->GetURL().host()].insert(etld_plus1);
+      }
+    }
+  }
+
   // site eTLD+1 : {owner site eTLD+1, # of sites in that first party set}
   std::map<std::string, std::pair<std::string, int>> fps_map;
   for (auto fps : fps_owner_to_members) {
@@ -540,11 +563,13 @@
     const std::set<url::Origin>& origin_permission_set,
     base::Value::List* list_value,
     Profile* profile,
-    CookiesTreeModel* tree_model) {
+    CookiesTreeModel* tree_model,
+    BrowsingDataModel* browsing_data_model) {
   DCHECK(profile);
   auto* privacy_sandbox_service =
       PrivacySandboxServiceFactory::GetForProfile(profile);
-  auto fps_map = GetFpsMap(privacy_sandbox_service, tree_model);
+  auto fps_map =
+      GetFpsMap(privacy_sandbox_service, tree_model, browsing_data_model);
   base::flat_set<url::Origin> installed_origins =
       GetInstalledAppOrigins(profile);
   site_engagement::SiteEngagementService* engagement_service =
@@ -904,6 +929,7 @@
   // the browsing data (hostname is insufficient) in CookieTreeModel or the new
   // BrowsingDataModel.
   std::string usage_hostname = GURL(usage_origin_).host();
+  int num_cookies = 0;
   for (const auto& site : root->children()) {
     std::string title = base::UTF16ToUTF8(site->GetTitle());
     if (title != usage_hostname) {
@@ -915,7 +941,6 @@
     // inspected.
     // TODO (crbug.com/1271155): This is slow, the replacement for the
     // CookiesTreeModel should improve this significantly.
-    int num_cookies = 0;
     for (const auto& site_child : site->children()) {
       if (site_child->GetDetailedInfo().node_type !=
           CookieTreeNode::DetailedInfo::TYPE_COOKIES) {
@@ -932,42 +957,47 @@
             return !detailed_info.cookie->IsPartitioned();
           });
     }
-    if (num_cookies != 0) {
-      cookie_string = base::UTF16ToUTF8(l10n_util::GetPluralStringFUTF16(
-          IDS_SETTINGS_SITE_SETTINGS_NUM_COOKIES, num_cookies));
-    }
-
-    auto* privacy_sandbox_service =
-        PrivacySandboxServiceFactory::GetForProfile(profile_);
-    auto fps_map =
-        GetFpsMap(privacy_sandbox_service, cookies_tree_model_.get());
-    auto etld_plus1 = GetEtldPlusOne(site->GetDetailedInfo().origin);
-    if (fps_map.count(etld_plus1)) {
-      fps_string =
-          base::UTF16ToUTF8(base::i18n::MessageFormatter::FormatWithNamedArgs(
-              l10n_util::GetStringUTF16(
-                  IDS_SETTINGS_SITE_SETTINGS_FIRST_PARTY_SETS_MEMBERSHIP_LABEL),
-              "MEMBERS", static_cast<int>(fps_map[etld_plus1].second),
-              "FPS_OWNER", fps_map[etld_plus1].first));
-      fpsPolicy = privacy_sandbox_service->IsPartOfManagedFirstPartySet(
-          ConvertEtldToSchemefulSite(etld_plus1));
-    }
     break;
   }
 
+  auto usage_origin = url::Origin::Create(GURL(usage_origin_));
   for (const BrowsingDataModel::BrowsingDataEntryView& entry :
        *browsing_data_model_) {
-    auto usage_origin = url::Origin::Create(GURL(usage_origin_));
     if (!entry.Matches(usage_origin)) {
       continue;
     }
     size += entry.data_details->storage_size;
+    // Display only first party cookies.
+    if (!entry.GetThirdPartyPartitioningSite().has_value()) {
+      num_cookies += entry.data_details->cookie_count;
+    }
+  }
+
+  if (num_cookies > 0) {
+    cookie_string = base::UTF16ToUTF8(l10n_util::GetPluralStringFUTF16(
+        IDS_SETTINGS_SITE_SETTINGS_NUM_COOKIES, num_cookies));
   }
 
   if (size > 0) {
     usage_string = base::UTF16ToUTF8(ui::FormatBytes(size));
   }
 
+  auto* privacy_sandbox_service =
+      PrivacySandboxServiceFactory::GetForProfile(profile_);
+  auto fps_map = GetFpsMap(privacy_sandbox_service, cookies_tree_model_.get(),
+                           browsing_data_model_.get());
+  auto etld_plus1 = GetEtldPlusOne(usage_origin);
+  if (fps_map.count(etld_plus1)) {
+    fps_string =
+        base::UTF16ToUTF8(base::i18n::MessageFormatter::FormatWithNamedArgs(
+            l10n_util::GetStringUTF16(
+                IDS_SETTINGS_SITE_SETTINGS_FIRST_PARTY_SETS_MEMBERSHIP_LABEL),
+            "MEMBERS", static_cast<int>(fps_map[etld_plus1].second),
+            "FPS_OWNER", fps_map[etld_plus1].first));
+    fpsPolicy = privacy_sandbox_service->IsPartOfManagedFirstPartySet(
+        ConvertEtldToSchemefulSite(etld_plus1));
+  }
+
   FireWebUIListener("usage-total-changed", base::Value(usage_origin_),
                     base::Value(usage_string), base::Value(cookie_string),
                     base::Value(fps_string), base::Value(fpsPolicy));
@@ -1291,7 +1321,8 @@
 
   // Respond with currently available data.
   ConvertSiteGroupMapToList(all_sites_map_, origin_permission_set_, &result,
-                            profile, cookies_tree_model_.get());
+                            profile, cookies_tree_model_.get(),
+                            browsing_data_model_.get());
 
   LogAllSitesAction(AllSitesAction2::kLoadPage);
 
@@ -1369,7 +1400,8 @@
   GetOriginStorage(&all_sites_map_, &origin_size_map);
   GetHostCookies(&all_sites_map_, &host_cookie_map);
   ConvertSiteGroupMapToList(all_sites_map_, origin_permission_set_, &list_value,
-                            profile, cookies_tree_model_.get());
+                            profile, cookies_tree_model_.get(),
+                            browsing_data_model_.get());
 
   // Merge the origin usage and cookies number into |list_value|.
   for (base::Value& item : list_value) {
diff --git a/chrome/browser/ui/webui/settings/site_settings_handler_unittest.cc b/chrome/browser/ui/webui/settings/site_settings_handler_unittest.cc
index d82ea2b..ca5ae21 100644
--- a/chrome/browser/ui/webui/settings/site_settings_handler_unittest.cc
+++ b/chrome/browser/ui/webui/settings/site_settings_handler_unittest.cc
@@ -2085,9 +2085,7 @@
         unpartitioned_origin_info.FindBool("isPartitioned").value_or(true));
   }
 
-  // TODO(crbug.com/1518460): Re-enable section after fixing how groups are
-  // created in the all sites map based on scheme and order of insertion.
-  if (!IsDeprecateCookiesTreeModelEnabled()) {
+  {
     const base::Value& site_group_val = storage_and_cookie_list[2];
     ASSERT_TRUE(site_group_val.is_dict());
     const base::Value::Dict& site_group = site_group_val.GetDict();
@@ -6549,11 +6547,6 @@
 }
 
 TEST_P(SiteSettingsHandlerTest, HandleGetUsageInfo) {
-  if (IsDeprecateCookiesTreeModelEnabled()) {
-    // TODO(crbug.com/1509434): Re-enable and fix test when browsing data model
-    // extraction for FPS is implemented.
-    return;
-  }
   SetupDefaultFirstPartySets(mock_privacy_sandbox_service());
 
   EXPECT_CALL(*mock_privacy_sandbox_service(), IsPartOfManagedFirstPartySet(_))
@@ -6568,13 +6561,19 @@
   // Confirm that usage info only returns unpartitioned storage.
   SetupModels();
 
-  EXPECT_EQ(28u, handler()
-                     ->GetCookiesTreeModelForTesting()
-                     ->GetRoot()
-                     ->GetTotalNodeCount());
-  EXPECT_EQ(5,
-            std::distance(handler()->GetBrowsingDataModelForTesting()->begin(),
+  if (IsDeprecateCookiesTreeModelEnabled()) {
+    EXPECT_EQ(
+        17, std::distance(handler()->GetBrowsingDataModelForTesting()->begin(),
                           handler()->GetBrowsingDataModelForTesting()->end()));
+  } else {
+    EXPECT_EQ(28u, handler()
+                       ->GetCookiesTreeModelForTesting()
+                       ->GetRoot()
+                       ->GetTotalNodeCount());
+    EXPECT_EQ(
+        5, std::distance(handler()->GetBrowsingDataModelForTesting()->begin(),
+                         handler()->GetBrowsingDataModelForTesting()->end()));
+  }
 
   base::Value::List args;
   args.Append("http://www.example.com");
@@ -6613,13 +6612,6 @@
 }
 
 TEST_P(SiteSettingsHandlerTest, FirstPartySetsMembership) {
-  if (IsDeprecateCookiesTreeModelEnabled()) {
-    // Currently first-party sets are not being defined using the browsing data
-    // model. TODO(crbug.com/1509434): Implement BDM FPS list extraction and
-    // re-enable the test.
-    return;
-  }
-
   SetupDefaultFirstPartySets(mock_privacy_sandbox_service());
 
   EXPECT_CALL(*mock_privacy_sandbox_service(), IsPartOfManagedFirstPartySet(_))
diff --git a/chrome/browser/ui/webui/side_panel/read_anything/read_anything_app_toolbar_browsertest.cc b/chrome/browser/ui/webui/side_panel/read_anything/read_anything_app_toolbar_browsertest.cc
index 4a6ac5c..f699e59 100644
--- a/chrome/browser/ui/webui/side_panel/read_anything/read_anything_app_toolbar_browsertest.cc
+++ b/chrome/browser/ui/webui/side_panel/read_anything/read_anything_app_toolbar_browsertest.cc
@@ -121,10 +121,6 @@
   ASSERT_TRUE(RunTest("font_size_buttons_without_read_aloud.js"));
 }
 
-IN_PROC_BROWSER_TEST_F(ReadAnythingAppToolbarTest, LinksToggleButtonOnToolbar) {
-  ASSERT_TRUE(RunTest("links_toggle_button.js"));
-}
-
 // TODO(crbug.com/1474951): Remove this test once Read Aloud flag is removed.
 IN_PROC_BROWSER_TEST_F(ReadAnythingAppToolbarTest, ReadAloud_Hidden) {
   ASSERT_TRUE(RunTest("toolbar_without_flag_hides_read_aloud.js"));
diff --git a/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler.cc b/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler.cc
index e7779d8f..d5657c0 100644
--- a/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler.cc
+++ b/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler.cc
@@ -215,13 +215,6 @@
         prefs::kAccessibilityReadAnythingFontScale, saved_font_size);
   }
 }
-void ReadAnythingUntrustedPageHandler::OnLinksEnabledChanged(bool enabled) {
-  if (browser_) {
-    browser_->profile()->GetPrefs()->SetBoolean(
-        prefs::kAccessibilityReadAnythingLinksEnabled,
-        static_cast<size_t>(enabled));
-  }
-}
 void ReadAnythingUntrustedPageHandler::OnColorChange(
     read_anything::mojom::Colors color) {
   if (browser_) {
diff --git a/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler.h b/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler.h
index 50e05c97..261cd4a 100644
--- a/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler.h
+++ b/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler.h
@@ -104,7 +104,6 @@
       read_anything::mojom::LetterSpacing letter_spacing) override;
   void OnFontChange(const std::string& font) override;
   void OnFontSizeChange(double font_size) override;
-  void OnLinksEnabledChanged(bool enabled) override;
   void OnColorChange(read_anything::mojom::Colors color) override;
   void OnSpeechRateChange(double rate) override;
   void OnVoiceChange(const std::string& voice,
diff --git a/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_ui.cc b/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_ui.cc
index 2e671e85..f9fa3a59 100644
--- a/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_ui.cc
+++ b/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_ui.cc
@@ -87,8 +87,6 @@
        IDS_READING_MODE_INCREASE_FONT_SIZE_BUTTON_LABEL},
       {"decreaseFontSizeLabel",
        IDS_READING_MODE_DECREASE_FONT_SIZE_BUTTON_LABEL},
-      {"disableLinksLabel", IDS_READING_MODE_DISABLE_LINKS_BUTTON_LABEL},
-      {"enableLinksLabel", IDS_READING_MODE_ENABLE_LINKS_BUTTON_LABEL},
       {"readingModeToolbarLabel", IDS_READING_MODE_TOOLBAR_LABEL},
       {"readingModeVoicePreviewText", IDS_READING_MODE_VOICE_PREVIEW_STRING},
   };
diff --git a/chrome/browser/web_applications/app_service/BUILD.gn b/chrome/browser/web_applications/app_service/BUILD.gn
index c298aa7c..0e09c909 100644
--- a/chrome/browser/web_applications/app_service/BUILD.gn
+++ b/chrome/browser/web_applications/app_service/BUILD.gn
@@ -83,6 +83,7 @@
   testonly = true
 
   sources = [
+    "publisher_helper_unittest.cc",
     "web_app_publisher_helper_unittest.cc",
     "web_app_publisher_unittest.cc",
     "web_apps_with_shortcuts_test.cc",
diff --git a/chrome/browser/web_applications/app_service/browser_shortcuts.cc b/chrome/browser/web_applications/app_service/browser_shortcuts.cc
index f3b31f7..f7eaca4 100644
--- a/chrome/browser/web_applications/app_service/browser_shortcuts.cc
+++ b/chrome/browser/web_applications/app_service/browser_shortcuts.cc
@@ -26,6 +26,7 @@
 #include "components/services/app_service/public/cpp/icon_types.h"
 #include "components/services/app_service/public/cpp/shortcut/shortcut.h"
 #include "components/services/app_service/public/cpp/shortcut/shortcut_registry_cache.h"
+#include "components/webapps/browser/installable/installable_metrics.h"
 
 namespace {
 
@@ -96,7 +97,8 @@
       app_constants::kChromeAppId, web_app->app_id());
   shortcut->name =
       provider_->registrar_unsafe().GetAppShortName(web_app->app_id());
-  shortcut->shortcut_source = apps::ShortcutSource::kUser;
+  shortcut->shortcut_source = ConvertWebAppManagementTypeToShortcutSource(
+      web_app->GetHighestPrioritySource());
 
   apps::IconEffects icon_effects = apps::IconEffects::kRoundCorners;
   icon_effects |= web_app->is_generated_icon()
diff --git a/chrome/browser/web_applications/app_service/lacros_browser_shortcuts_controller.cc b/chrome/browser/web_applications/app_service/lacros_browser_shortcuts_controller.cc
index bee2df00..4d3a6419 100644
--- a/chrome/browser/web_applications/app_service/lacros_browser_shortcuts_controller.cc
+++ b/chrome/browser/web_applications/app_service/lacros_browser_shortcuts_controller.cc
@@ -207,7 +207,8 @@
         app_constants::kLacrosAppId, web_app->app_id());
     shortcut->name =
         provider_->registrar_unsafe().GetAppShortName(web_app->app_id());
-    shortcut->shortcut_source = apps::ShortcutSource::kUser;
+    shortcut->shortcut_source = ConvertWebAppManagementTypeToShortcutSource(
+        web_app->GetHighestPrioritySource());
 
     apps::IconEffects icon_effects = apps::IconEffects::kRoundCorners;
     icon_effects |= web_app->is_generated_icon()
diff --git a/chrome/browser/web_applications/app_service/publisher_helper.cc b/chrome/browser/web_applications/app_service/publisher_helper.cc
index bb4f80e..cd1264d 100644
--- a/chrome/browser/web_applications/app_service/publisher_helper.cc
+++ b/chrome/browser/web_applications/app_service/publisher_helper.cc
@@ -4,9 +4,11 @@
 
 #include "chrome/browser/web_applications/app_service/publisher_helper.h"
 
+#include "chrome/browser/web_applications/web_app_constants.h"
 #include "chrome/browser/web_applications/web_app_provider.h"
 #include "chrome/browser/web_applications/web_app_registrar.h"
 #include "components/services/app_service/public/cpp/app_types.h"
+#include "components/services/app_service/public/cpp/shortcut/shortcut.h"
 #include "components/webapps/browser/installable/installable_metrics.h"
 
 #if BUILDFLAG(IS_CHROMEOS)
@@ -43,4 +45,25 @@
   return false;
 }
 
+apps::ShortcutSource ConvertWebAppManagementTypeToShortcutSource(
+    WebAppManagement::Type management_type) {
+  switch (management_type) {
+    case WebAppManagement::Type::kSync:
+    case WebAppManagement::Type::kWebAppStore:
+    case WebAppManagement::Type::kOneDriveIntegration:
+      return apps::ShortcutSource::kUser;
+    case WebAppManagement::Type::kPolicy:
+      return apps::ShortcutSource::kPolicy;
+    case WebAppManagement::Type::kOem:
+    case WebAppManagement::Type::kApsDefault:
+    case WebAppManagement::Type::kDefault:
+      return apps::ShortcutSource::kDefault;
+    case WebAppManagement::Type::kKiosk:
+    case WebAppManagement::Type::kSystem:
+    case WebAppManagement::Type::kCommandLine:
+    case WebAppManagement::Type::kSubApp:
+      return apps::ShortcutSource::kUnknown;
+  }
+}
+
 }  // namespace web_app
diff --git a/chrome/browser/web_applications/app_service/publisher_helper.h b/chrome/browser/web_applications/app_service/publisher_helper.h
index 494bcb4..1a3a0ef 100644
--- a/chrome/browser/web_applications/app_service/publisher_helper.h
+++ b/chrome/browser/web_applications/app_service/publisher_helper.h
@@ -5,10 +5,15 @@
 #ifndef CHROME_BROWSER_WEB_APPLICATIONS_APP_SERVICE_PUBLISHER_HELPER_H_
 #define CHROME_BROWSER_WEB_APPLICATIONS_APP_SERVICE_PUBLISHER_HELPER_H_
 
+#include "chrome/browser/web_applications/web_app_constants.h"
 #include "components/services/app_service/public/cpp/app_types.h"
 #include "components/webapps/browser/installable/installable_metrics.h"
 #include "components/webapps/common/web_app_id.h"
 
+namespace apps {
+enum class ShortcutSource;
+}
+
 namespace web_app {
 class WebAppProvider;
 
@@ -21,6 +26,10 @@
 bool IsAppServiceShortcut(const webapps::AppId& web_app_id,
                           const WebAppProvider& provider);
 
+// Converts WebAppManagement Type to ShortcutSource.
+apps::ShortcutSource ConvertWebAppManagementTypeToShortcutSource(
+    WebAppManagement::Type management_type);
+
 }  // namespace web_app
 
 #endif  // CHROME_BROWSER_WEB_APPLICATIONS_APP_SERVICE_PUBLISHER_HELPER_H_
diff --git a/chrome/browser/web_applications/app_service/publisher_helper_unittest.cc b/chrome/browser/web_applications/app_service/publisher_helper_unittest.cc
new file mode 100644
index 0000000..0c1f399
--- /dev/null
+++ b/chrome/browser/web_applications/app_service/publisher_helper_unittest.cc
@@ -0,0 +1,49 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+#include "chrome/browser/web_applications/app_service/publisher_helper.h"
+
+#include "chrome/browser/web_applications/web_app_constants.h"
+#include "components/services/app_service/public/cpp/shortcut/shortcut.h"
+#include "components/webapps/browser/installable/installable_metrics.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace web_app {
+
+TEST(PublisherHelperTest, ConvertWebAppManagementTypeToShortcutSource) {
+  ASSERT_EQ(ConvertWebAppManagementTypeToShortcutSource(
+                WebAppManagement::Type::kSync),
+            apps::ShortcutSource::kUser);
+  ASSERT_EQ(ConvertWebAppManagementTypeToShortcutSource(
+                WebAppManagement::Type::kWebAppStore),
+            apps::ShortcutSource::kUser);
+  ASSERT_EQ(ConvertWebAppManagementTypeToShortcutSource(
+                WebAppManagement::Type::kOneDriveIntegration),
+            apps::ShortcutSource::kUser);
+  ASSERT_EQ(ConvertWebAppManagementTypeToShortcutSource(
+                WebAppManagement::Type::kPolicy),
+            apps::ShortcutSource::kPolicy);
+  ASSERT_EQ(
+      ConvertWebAppManagementTypeToShortcutSource(WebAppManagement::Type::kOem),
+      apps::ShortcutSource::kDefault);
+  ASSERT_EQ(ConvertWebAppManagementTypeToShortcutSource(
+                WebAppManagement::Type::kApsDefault),
+            apps::ShortcutSource::kDefault);
+  ASSERT_EQ(ConvertWebAppManagementTypeToShortcutSource(
+                WebAppManagement::Type::kDefault),
+            apps::ShortcutSource::kDefault);
+  ASSERT_EQ(ConvertWebAppManagementTypeToShortcutSource(
+                WebAppManagement::Type::kKiosk),
+            apps::ShortcutSource::kUnknown);
+  ASSERT_EQ(ConvertWebAppManagementTypeToShortcutSource(
+                WebAppManagement::Type::kSystem),
+            apps::ShortcutSource::kUnknown);
+  ASSERT_EQ(ConvertWebAppManagementTypeToShortcutSource(
+                WebAppManagement::Type::kCommandLine),
+            apps::ShortcutSource::kUnknown);
+  ASSERT_EQ(ConvertWebAppManagementTypeToShortcutSource(
+                WebAppManagement::Type::kSubApp),
+            apps::ShortcutSource::kUnknown);
+}
+
+}  // namespace web_app
diff --git a/chrome/browser/web_applications/features.cc b/chrome/browser/web_applications/features.cc
index b47e65c..a6063222 100644
--- a/chrome/browser/web_applications/features.cc
+++ b/chrome/browser/web_applications/features.cc
@@ -14,4 +14,14 @@
              "SyncOnlySeparateUserDisplayModeForCrOS",
              base::FEATURE_ENABLED_BY_DEFAULT);
 
+#if BUILDFLAG(IS_CHROMEOS)
+BASE_FEATURE(kUserDisplayModeSyncBrowserMitigation,
+             "UserDisplayModeSyncBrowserMitigation",
+             base::FEATURE_ENABLED_BY_DEFAULT);
+
+BASE_FEATURE(kUserDisplayModeSyncStandaloneMitigation,
+             "UserDisplayModeSyncStandaloneMitigation",
+             base::FEATURE_ENABLED_BY_DEFAULT);
+#endif  // BUILDFLAG(IS_CHROMEOS)
+
 }  // namespace web_app
diff --git a/chrome/browser/web_applications/features.h b/chrome/browser/web_applications/features.h
index 23a7ed3d..21aabe8 100644
--- a/chrome/browser/web_applications/features.h
+++ b/chrome/browser/web_applications/features.h
@@ -6,6 +6,8 @@
 #define CHROME_BROWSER_WEB_APPLICATIONS_FEATURES_H_
 
 #include "base/feature_list.h"
+#include "build/build_config.h"
+#include "build/chromeos_buildflags.h"
 
 namespace web_app {
 
@@ -21,6 +23,12 @@
 // prevents the field from being inadvertently cleared by the client.
 BASE_DECLARE_FEATURE(kSyncOnlySeparateUserDisplayModeForCrOS);
 
+#if BUILDFLAG(IS_CHROMEOS)
+BASE_DECLARE_FEATURE(kUserDisplayModeSyncBrowserMitigation);
+
+BASE_DECLARE_FEATURE(kUserDisplayModeSyncStandaloneMitigation);
+#endif  // BUILDFLAG(IS_CHROMEOS)
+
 }  // namespace web_app
 
 #endif  // CHROME_BROWSER_WEB_APPLICATIONS_FEATURES_H_
diff --git a/chrome/browser/web_applications/web_app_install_finalizer.cc b/chrome/browser/web_applications/web_app_install_finalizer.cc
index 955b7fd..5fa77d90 100644
--- a/chrome/browser/web_applications/web_app_install_finalizer.cc
+++ b/chrome/browser/web_applications/web_app_install_finalizer.cc
@@ -38,6 +38,7 @@
 #include "chrome/browser/web_applications/web_app_helpers.h"
 #include "chrome/browser/web_applications/web_app_icon_generator.h"
 #include "chrome/browser/web_applications/web_app_icon_manager.h"
+#include "chrome/browser/web_applications/web_app_id_constants.h"
 #include "chrome/browser/web_applications/web_app_install_info.h"
 #include "chrome/browser/web_applications/web_app_install_manager.h"
 #include "chrome/browser/web_applications/web_app_install_utils.h"
@@ -107,6 +108,96 @@
   }
 }
 
+#if BUILDFLAG(IS_CHROMEOS)
+// When web apps are added to sync on ChromeOS the value of
+// user_display_mode_non_cros should be set in certain cases to avoid poor sync
+// install states on devices with kSeparateUserDisplayModeForCrOS disabled
+// (including all pre-M122 devices) and non-CrOS devices with particular web
+// apps.
+// See switch for specific cases being mitigated against.
+// See go/udm-desync#bookmark=id.cg753kjyrruo for design doc.
+// TODO(b/320771282): Add automated tests.
+void ApplyUserDisplayModeSyncMitigations(
+    const WebAppInstallFinalizer::FinalizeOptions& options,
+    WebApp& web_app) {
+  if (!base::FeatureList::IsEnabled(kSeparateUserDisplayModeForCrOS)) {
+    return;
+  }
+
+  // Guaranteed by EnsureAppsHaveUserDisplayModeForCurrentPlatform().
+  CHECK(web_app.user_display_mode_cros().has_value(),
+        base::NotFatalUntil::M125);
+
+  // Don't mitigate installations from sync, this is only for installs that will
+  // be newly uploaded to sync.
+  if (options.install_surface == webapps::WebappInstallSource::SYNC) {
+    return;
+  }
+
+  // Only mitigate if web app is being added to sync.
+  if (options.source != WebAppManagement::Type::kSync) {
+    return;
+  }
+
+  // Don't override existing non CrOS value.
+  if (web_app.user_display_mode_non_cros().has_value()) {
+    return;
+  }
+
+  switch (web_app.user_display_mode_cros().value()) {
+    case mojom::UserDisplayMode::kBrowser:
+      if (!base::FeatureList::IsEnabled(
+              kUserDisplayModeSyncBrowserMitigation)) {
+        break;
+      }
+
+      // CrOS devices with kSeparateUserDisplayModeForCrOS disabled (including
+      // pre-M122 devices) use the user_display_mode_non_cros sync field instead
+      // of user_display_mode_cros. If user_display_mode_non_cros is ever unset
+      // they will fallback to using kStandalone even if user_display_mode_cros
+      // is set to kBrowser. This mitigation esures user_display_mode_non_cros
+      // is set to kBrowser for these devices.
+      // Example user journey:
+      // - Install web app as browser shortcut on post-M122 CrOS device.
+      // - Sync installation to pre-M122 CrOS device.
+      // - Check that it is synced as a browser shortcut.
+      // TODO(b/321617981): Remove when there are sufficiently few pre-M122 CrOS
+      // devices in circulation.
+      web_app.SetUserDisplayModeNonCrOS(mojom::UserDisplayMode::kBrowser);
+      break;
+
+    case mojom::UserDisplayMode::kStandalone: {
+      if (!base::FeatureList::IsEnabled(
+              kUserDisplayModeSyncStandaloneMitigation)) {
+        break;
+      }
+
+      // Ensure standalone averse apps don't get defaulted to kStandalone on
+      // non-CrOS devices via sync.
+      // Example user journey:
+      // - Install Google Docs as a standalone web app.
+      // - Sync installation to non-CrOS device.
+      // - Check that it is synced as a browser shortcut.
+      // TODO(b/321617972): Remove when Windows/Mac/Linux support for tabbed web
+      // apps is in sufficient circulation.
+      bool is_standalone_averse_app = web_app.app_id() == kGoogleDocsAppId ||
+                                      web_app.app_id() == kGoogleSheetsAppId ||
+                                      web_app.app_id() == kGoogleSlidesAppId;
+      if (!is_standalone_averse_app) {
+        break;
+      }
+      web_app.SetUserDisplayModeNonCrOS(mojom::UserDisplayMode::kBrowser);
+      break;
+    }
+
+    case mojom::UserDisplayMode::kTabbed:
+      // This can only be reached when kDesktopPWAsTabStripSettings is enabled,
+      // this is only for testing and is planned to be removed.
+      break;
+  }
+}
+#endif  // BUILDFLAG(IS_CHROMEOS)
+
 }  // namespace
 
 WebAppInstallFinalizer::FinalizeOptions::FinalizeOptions(
@@ -233,6 +324,9 @@
     DCHECK(web_app_info.user_display_mode.has_value());
     web_app->SetUserDisplayMode(*web_app_info.user_display_mode);
   }
+#if BUILDFLAG(IS_CHROMEOS)
+  ApplyUserDisplayModeSyncMitigations(options, *web_app);
+#endif  // BUILDFLAG(IS_CHROMEOS)
 
   // `WebApp::chromeos_data` has a default value already. Only override if the
   // caller provided a new value.
diff --git a/chrome/browser/web_applications/web_app_sync_bridge_unittest.cc b/chrome/browser/web_applications/web_app_sync_bridge_unittest.cc
index 5666cdf..86b8bdbd 100644
--- a/chrome/browser/web_applications/web_app_sync_bridge_unittest.cc
+++ b/chrome/browser/web_applications/web_app_sync_bridge_unittest.cc
@@ -1430,13 +1430,15 @@
   }
 
   WebAppSyncBridgeTest_UserDisplayModeSplit() {
-    if (flag_enabled()) {
-      scoped_feature_list_.InitAndEnableFeature(
-          kSeparateUserDisplayModeForCrOS);
-    } else {
-      scoped_feature_list_.InitAndDisableFeature(
-          kSeparateUserDisplayModeForCrOS);
-    }
+    scoped_feature_list_.InitWithFeatureStates({
+        {kSeparateUserDisplayModeForCrOS, flag_enabled()},
+#if BUILDFLAG(IS_CHROMEOS)
+        // UDM mitigations mess with the installed local state, disable them so
+        // the state matches the intention of the test.
+        {kUserDisplayModeSyncBrowserMitigation, false},
+        {kUserDisplayModeSyncStandaloneMitigation, false},
+#endif  // BUILDFLAG(IS_CHROMEOS)
+    });
   }
 
   ~WebAppSyncBridgeTest_UserDisplayModeSplit() override = default;
diff --git a/chrome/build/android-arm32.pgo.txt b/chrome/build/android-arm32.pgo.txt
index 21fc828e2..24c536a 100644
--- a/chrome/build/android-arm32.pgo.txt
+++ b/chrome/build/android-arm32.pgo.txt
@@ -1 +1 @@
-chrome-android32-main-1705880330-07c9b56617d6eabfeaa80ab4598a7bc9dce7f1d6.profdata
+chrome-android32-main-1705902798-70a0532316a2a1e949e7d48e76ae0e925cc4e01e.profdata
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index efa9ba6c..ff8bf8c 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-main-1705880330-9d32ec36e9aeebdd53f41d936c441fed0436305c.profdata
+chrome-linux-main-1705902798-452cf184158ebb0777596ceac1fdc3a56902f2f7.profdata
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt
index 44c5139..583e659 100644
--- a/chrome/build/mac-arm.pgo.txt
+++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@
-chrome-mac-arm-main-1705888783-ede8c927dea97285e9e12c01564ff936edb5c63c.profdata
+chrome-mac-arm-main-1705917537-8bd8ff351e7fbbdcd154b43301249ad11dda46c0.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index c4a0896f..fb6216c 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-main-1705838290-dc7cc050f83dfdc198983988f895f6baa65ca64e.profdata
+chrome-mac-main-1705902798-84630cbed3f0c08be1acb85908b207d880e072ce.profdata
diff --git a/chrome/build/win-arm64.pgo.txt b/chrome/build/win-arm64.pgo.txt
index 44b07d7..5270f40 100644
--- a/chrome/build/win-arm64.pgo.txt
+++ b/chrome/build/win-arm64.pgo.txt
@@ -1 +1 @@
-chrome-win-arm64-main-1705708798-64d78504306b151215f6d465ebd4f75859d8f32f.profdata
+chrome-win-arm64-main-1705902798-9be9eec142df6bc8cb04a7dd3965d31f488ff6d1.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index 176c6d6b6..825b0e4f 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1705880330-964689a8b11420072bfdc1cfdc6733db178ad5a1.profdata
+chrome-win64-main-1705913940-f588d173e5955b82015c08d8492c3e836dacb74f.profdata
diff --git a/chrome/common/accessibility/read_anything.mojom b/chrome/common/accessibility/read_anything.mojom
index ad37bbc..76742b5 100644
--- a/chrome/common/accessibility/read_anything.mojom
+++ b/chrome/common/accessibility/read_anything.mojom
@@ -106,10 +106,6 @@
   // the webui toolbar.
   OnFontSizeChange(double font_size);
 
-  // Informs the browser controller that the user has toggled links
-  // via the webui toolbar.
-  OnLinksEnabledChanged(bool enabled);
-
   // Informs the browser controller that the user updated the color theme via
   // the webui toolbar.
   OnColorChange(read_anything.mojom.Colors color);
diff --git a/chrome/renderer/accessibility/read_anything_app_controller.cc b/chrome/renderer/accessibility/read_anything_app_controller.cc
index 76e6121..3479d90 100644
--- a/chrome/renderer/accessibility/read_anything_app_controller.cc
+++ b/chrome/renderer/accessibility/read_anything_app_controller.cc
@@ -727,8 +727,6 @@
       .SetMethod("onFontSizeChanged",
                  &ReadAnythingAppController::OnFontSizeChanged)
       .SetMethod("onFontSizeReset", &ReadAnythingAppController::OnFontSizeReset)
-      .SetMethod("onLinksEnabledToggled",
-                 &ReadAnythingAppController::OnLinksEnabledToggled)
       .SetMethod("onScroll", &ReadAnythingAppController::OnScroll)
       .SetMethod("onLinkClicked", &ReadAnythingAppController::OnLinkClicked)
       .SetMethod("onStandardLineSpacing",
@@ -1203,11 +1201,6 @@
   page_handler_->OnFontSizeChange(model_.font_size());
 }
 
-void ReadAnythingAppController::OnLinksEnabledToggled() {
-  model_.ToggleLinksEnabled();
-  page_handler_->OnLinksEnabledChanged(model_.links_enabled());
-}
-
 void ReadAnythingAppController::OnScroll(bool on_selection) const {
   model_.OnScroll(on_selection, /* from_reading_mode= */ true);
 }
diff --git a/chrome/renderer/accessibility/read_anything_app_controller.h b/chrome/renderer/accessibility/read_anything_app_controller.h
index 02174d7..ec3c8969 100644
--- a/chrome/renderer/accessibility/read_anything_app_controller.h
+++ b/chrome/renderer/accessibility/read_anything_app_controller.h
@@ -191,7 +191,6 @@
   float SpeechRate() const;
   void OnFontSizeChanged(bool increase);
   void OnFontSizeReset();
-  void OnLinksEnabledToggled();
   SkColor ForegroundColor() const;
   float LetterSpacing() const;
   float LineSpacing() const;
diff --git a/chrome/renderer/accessibility/read_anything_app_controller_browsertest.cc b/chrome/renderer/accessibility/read_anything_app_controller_browsertest.cc
index dabdf1c5..88de0c96 100644
--- a/chrome/renderer/accessibility/read_anything_app_controller_browsertest.cc
+++ b/chrome/renderer/accessibility/read_anything_app_controller_browsertest.cc
@@ -63,7 +63,6 @@
               (override));
   MOCK_METHOD(void, OnFontChange, (const std::string& font), (override));
   MOCK_METHOD(void, OnFontSizeChange, (double font_size), (override));
-  MOCK_METHOD(void, OnLinksEnabledChanged, (bool enabled), (override));
   MOCK_METHOD(void, OnSpeechRateChange, (double rate), (override));
   MOCK_METHOD(void,
               OnVoiceChange,
@@ -318,8 +317,6 @@
 
   void OnFontSizeReset() { controller_->OnFontSizeReset(); }
 
-  void OnLinksEnabledToggled() { controller_->OnLinksEnabledToggled(); }
-
   void TurnedHighlightOn() { controller_->TurnedHighlightOn(); }
 
   void TurnedHighlightOff() { controller_->TurnedHighlightOff(); }
@@ -1950,14 +1947,6 @@
   OnFontSizeReset();
 }
 
-TEST_F(ReadAnythingAppControllerTest,
-       OnLinksEnabledChanged_SetsEnabledToFalse) {
-  EXPECT_CALL(page_handler_,
-              OnLinksEnabledChanged(!kReadAnythingDefaultLinksEnabled))
-      .Times(1);
-  OnLinksEnabledToggled();
-}
-
 TEST_F(ReadAnythingAppControllerTest, TurnedHighlightOn_SavesHighlightState) {
   EXPECT_CALL(page_handler_,
               OnHighlightGranularityChanged(
diff --git a/chrome/renderer/accessibility/read_anything_app_model.cc b/chrome/renderer/accessibility/read_anything_app_model.cc
index 078722d..09d1260 100644
--- a/chrome/renderer/accessibility/read_anything_app_model.cc
+++ b/chrome/renderer/accessibility/read_anything_app_model.cc
@@ -868,10 +868,6 @@
   font_size_ = kReadAnythingDefaultFontScale;
 }
 
-void ReadAnythingAppModel::ToggleLinksEnabled() {
-  links_enabled_ = !links_enabled_;
-}
-
 void ReadAnythingAppModel::SetIsPdf(const GURL& url) {
   is_pdf_ = url.spec().ends_with(kPDFExtension);
 }
diff --git a/chrome/renderer/accessibility/read_anything_app_model.h b/chrome/renderer/accessibility/read_anything_app_model.h
index 4b92f48..06a418d 100644
--- a/chrome/renderer/accessibility/read_anything_app_model.h
+++ b/chrome/renderer/accessibility/read_anything_app_model.h
@@ -178,7 +178,6 @@
   void IncreaseTextSize();
   void DecreaseTextSize();
   void ResetTextSize();
-  void ToggleLinksEnabled();
 
   // PDF handling.
   void SetIsPdf(const GURL& url);
diff --git a/chrome/test/data/extensions/api_test/merge_session/background.js b/chrome/test/data/extensions/api_test/merge_session/background.js
index 50bed19..266afe3bc 100644
--- a/chrome/test/data/extensions/api_test/merge_session/background.js
+++ b/chrome/test/data/extensions/api_test/merge_session/background.js
@@ -15,9 +15,9 @@
 }
 
 // Starts XHR requests - one for google.com and one later for non-google.
-function startXHRRequests(googlePageUrl, googlePageCheckCallback,
-                          nonGooglePageUrl, nonGooglePageCheckCallback,
-                          async, should_throttle) {
+function startXHRRequests(
+    googlePageUrl, googlePageCheckCallback, nonGooglePageUrl,
+    nonGooglePageCheckCallback, is_async) {
   // Kick off google XHR first.
   var xhr = new XMLHttpRequest();
 
@@ -34,8 +34,8 @@
     console.warn("xhr.onreadystatechange: " + xhr.readyState);
     switch (xhr.readyState) {
       case XMLHttpRequest.OPENED:
-        startNonGoogleXHRRequests(nonGooglePageUrl, nonGooglePageCheckCallback,
-                                  async, should_throttle);
+        startNonGoogleXHRRequests(
+            nonGooglePageUrl, nonGooglePageCheckCallback, is_async);
         break;
       case XMLHttpRequest.DONE:
         validateResponse();
@@ -43,17 +43,16 @@
     }
   };
   chrome.test.sendMessage("opening " + googlePageUrl);
-  xhr.open("GET", googlePageUrl, async);
+  xhr.open('GET', googlePageUrl, is_async);
   xhr.send();
   googleRequestSent = true;
-  if (!async) {
+  if (!is_async) {
     validateResponse();
   }
 }
 
-function startNonGoogleXHRRequests(nonGooglePageUrl,
-                                   nonGooglePageCheckCallback,
-                                   async, should_throttle) {
+function startNonGoogleXHRRequests(
+    nonGooglePageUrl, nonGooglePageCheckCallback, is_async) {
   // Kick off non-google XHR next.
   var xhr = new XMLHttpRequest();
 
@@ -78,10 +77,10 @@
         break;
     }
   };
-  xhr.open("GET", nonGooglePageUrl, async);
+  xhr.open('GET', nonGooglePageUrl, is_async);
   xhr.send();
   nonGoogleRequestSent = true;
-  if (!async) {
+  if (!is_async) {
     validateResponse();
   }
 }
@@ -103,13 +102,12 @@
 }
 
 // Performs test that will verify if XHR request had completed prematurely.
-function startThrottledTests(googlePageUrl, nonGooglePageUrl, async,
-                             should_throttle) {
+function startThrottledTests(googlePageUrl, nonGooglePageUrl, is_async) {
   chrome.test.runTests([function testXHRThrottle() {
     initGlobals();
-    startXHRRequests(googlePageUrl, googlePageCheck,
-                     nonGooglePageUrl, nonGooglePageCheck, async,
-                     should_throttle);
+    startXHRRequests(
+        googlePageUrl, googlePageCheck, nonGooglePageUrl, nonGooglePageCheck,
+        is_async);
   }]);
   return true;
 }
diff --git a/chrome/test/data/webui/settings/chromeos/os_privacy_page/privacy_hub_camera_subpage_test.ts b/chrome/test/data/webui/settings/chromeos/os_privacy_page/privacy_hub_camera_subpage_test.ts
index 7a0cbad..56bd458 100644
--- a/chrome/test/data/webui/settings/chromeos/os_privacy_page/privacy_hub_camera_subpage_test.ts
+++ b/chrome/test/data/webui/settings/chromeos/os_privacy_page/privacy_hub_camera_subpage_test.ts
@@ -123,7 +123,8 @@
     assertFalse(cameraToggle.checked);
     assertEquals(privacyHubCameraSubpage.i18n('deviceOff'), getOnOffText());
     assertEquals(
-        privacyHubCameraSubpage.i18n('blockedForAllText'), getOnOffSubtext());
+        privacyHubCameraSubpage.i18n('privacyHubCameraAccessBlockedText'),
+        getOnOffSubtext());
     assertTrue(isCameraListSectionVisible());
     assertTrue(isBlockedSuffixDisplayedAfterCameraName());
     assertEquals(
diff --git a/chrome/test/data/webui/settings/chromeos/os_privacy_page/privacy_hub_microphone_subpage_test.ts b/chrome/test/data/webui/settings/chromeos/os_privacy_page/privacy_hub_microphone_subpage_test.ts
index 403f6296..ade80d0 100644
--- a/chrome/test/data/webui/settings/chromeos/os_privacy_page/privacy_hub_microphone_subpage_test.ts
+++ b/chrome/test/data/webui/settings/chromeos/os_privacy_page/privacy_hub_microphone_subpage_test.ts
@@ -146,7 +146,8 @@
         assertEquals(
             privacyHubMicrophoneSubpage.i18n('deviceOff'), getOnOffText());
         assertEquals(
-            privacyHubMicrophoneSubpage.i18n('blockedForAllText'),
+            privacyHubMicrophoneSubpage.i18n(
+                'privacyHubMicrophoneAccessBlockedText'),
             getOnOffSubtext());
         assertTrue(isMicrophoneListSectionVisible());
         assertTrue(isBlockedSuffixDisplayedAfterMicrophoneName());
diff --git a/chrome/test/data/webui/side_panel/read_anything/fake_reading_mode.ts b/chrome/test/data/webui/side_panel/read_anything/fake_reading_mode.ts
index 314f4c5..264df685 100644
--- a/chrome/test/data/webui/side_panel/read_anything/fake_reading_mode.ts
+++ b/chrome/test/data/webui/side_panel/read_anything/fake_reading_mode.ts
@@ -130,9 +130,6 @@
   onFontSizeChanged(_increase: boolean) {}
   onFontSizeReset() {}
 
-  // Called when a user toggles links via the webui toolbar.
-  onLinksEnabledToggled() {}
-
   // Called when the letter spacing is changed via the webui toolbar.
   onStandardLetterSpacing() {}
   onWideLetterSpacing() {}
diff --git a/chrome/test/data/webui/side_panel/read_anything/links_toggle_button.js b/chrome/test/data/webui/side_panel/read_anything/links_toggle_button.js
deleted file mode 100644
index eac6f48..0000000
--- a/chrome/test/data/webui/side_panel/read_anything/links_toggle_button.js
+++ /dev/null
@@ -1,101 +0,0 @@
-// Copyright 2024 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// out/Debug/browser_tests \
-//    --gtest_filter=ReadAnythingAppToolbarTest.LinksToggleButtonOnToolbar
-
-// Do not call the real `onConnected()`. As defined in
-// ReadAnythingAppController, onConnected creates mojo pipes to connect to the
-// rest of the Read Anything feature, which we are not testing here.
-(() => {
-  chrome.readingMode.onConnected = () => {};
-
-  const readAnythingApp =
-      document.querySelector('read-anything-app').shadowRoot;
-  const container = readAnythingApp.getElementById('container');
-  const toolbar =
-      readAnythingApp.querySelector('read-anything-toolbar').shadowRoot;
-
-  let result = true;
-  const assertEquals = (actual, expected) => {
-    const isEqual = actual === expected;
-    if (!isEqual) {
-      console.error(
-          'Expected: ' + JSON.stringify(expected) + ', ' +
-          'Actual: ' + JSON.stringify(actual));
-    }
-    result = result && isEqual;
-    return isEqual;
-  };
-
-  const assertContainerInnerHTML = (expected) => {
-    const actual = container.innerHTML;
-    assertEquals(actual, expected);
-  };
-
-  // root htmlTag='#document' id=1
-  // ++link htmlTag='a' url='http://www.google.com' id=2
-  // ++++staticText name='This is a link.' id=3
-  // ++link htmlTag='a' url='http://www.youtube.com' id=4
-  // ++++staticText name='This is another link.' id=5
-  const axTree = {
-    rootId: 1,
-    nodes: [
-      {
-        id: 1,
-        role: 'rootWebArea',
-        htmlTag: '#document',
-        childIds: [2, 4],
-      },
-      {
-        id: 2,
-        role: 'link',
-        htmlTag: 'a',
-        url: 'http://www.google.com',
-        childIds: [3],
-      },
-      {
-        id: 3,
-        role: 'staticText',
-        name: 'This is a link.',
-      },
-      {
-        id: 4,
-        role: 'link',
-        htmlTag: 'a',
-        url: 'http://www.youtube.com',
-        childIds: [5],
-      },
-      {
-        id: 5,
-        role: 'staticText',
-        name: 'This is another link.',
-      },
-    ],
-  };
-
-  chrome.readingMode.setContentForTesting(axTree, [2, 4]);
-  // HTML Before disabling links.
-  const beforeHtml = '<div><a href="http://www.google.com">This is a link.' +
-      '</a><a href="http://www.youtube.com">This is another link.</a></div>';
-  const buttonIconBefore = 'read-anything:links-enabled';
-
-  // HTML After disabling links.
-  const afterHtml = '<div><span>This is a link.' +
-      '</span><span>This is another link.</span></div>';
-  const buttonIconAfter = 'read-anything:links-disabled';
-
-  // Get button.
-  const link_toggle_button = toolbar.getElementById('link-toggle-button');
-  // Assert before state.
-  assertContainerInnerHTML(beforeHtml);
-  assertEquals(link_toggle_button.ironIcon, buttonIconBefore);
-  // Toggle State
-  link_toggle_button.click();
-  // Assert after state.
-  assertContainerInnerHTML(afterHtml);
-  assertEquals(link_toggle_button.ironIcon, buttonIconAfter);
-
-  return result;
-})();
diff --git a/clank b/clank
index 95dba2c..361cdfa 160000
--- a/clank
+++ b/clank
@@ -1 +1 @@
-Subproject commit 95dba2c15cf581680a54dd78d2bed0fc2acc1558
+Subproject commit 361cdfa3f0ddcd5ed6ed4283e32b62ee619e7f2d
diff --git a/components/BUILD.gn b/components/BUILD.gn
index c34fcb4..62639cf 100644
--- a/components/BUILD.gn
+++ b/components/BUILD.gn
@@ -630,6 +630,8 @@
       "//components/permissions/android:java",
       "//components/policy/android:native_test_support_java",
       "//components/policy/android:policy_java",
+      "//components/search_engines/android:java",
+      "//components/search_engines/android:test_utils_java",
       "//components/signin/core/browser",
       "//components/signin/public/android:java",
       "//components/signin/public/android:signin_java_test_support",
diff --git a/components/android_autofill/browser/android_autofill_features.cc b/components/android_autofill/browser/android_autofill_features.cc
index f1c55633..d3c9b454 100644
--- a/components/android_autofill/browser/android_autofill_features.cc
+++ b/components/android_autofill/browser/android_autofill_features.cc
@@ -17,8 +17,8 @@
     &kAndroidAutofillBottomSheetWorkaround,
     &kAndroidAutofillFormSubmissionCheckById,
     &kAndroidAutofillPrefillRequestsForLoginForms,
-    &kAndroidAutofillSignatureForPrefillRequestSimilarityCheck,
     &kAndroidAutofillSupportVisibilityChanges,
+    &kAndroidAutofillUsePwmPredictionsForOverrides,
 };
 
 }  // namespace
@@ -50,15 +50,6 @@
              "AndroidAutofillPrefillRequestsForLoginForms",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
-// If enabled, similarity checks between cached forms and focused forms are
-// replaced by comparing the form signatures of the cached and the focused form.
-// The motivation behind this experiment is that the decision to cache a form is
-// made based on server predictions and the server predictions of two forms
-// match iff their form signatures match.
-BASE_FEATURE(kAndroidAutofillSignatureForPrefillRequestSimilarityCheck,
-             "AndroidAutofillSignatureForPrefillRequestSimilarityCheck",
-             base::FEATURE_DISABLED_BY_DEFAULT);
-
 // If enabled, visibility changes of form fields of the form of an ongoing
 // Autofill session are communicated to Android's `AutofillManager` by calling
 // `AutofillManager.notifyViewVisibilityChanged()`.
@@ -69,6 +60,16 @@
              "AndroidAutofillSupportVisibilityChanges",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
+// If enabled, username and password field predictions are taken from
+// `password_manager::FormDataParser` and overwrite Autofill's native
+// predictions. Furthermore, similarity checks between cached forms and focused
+// forms that serve to decide whether to show a bottomsheet are performed using
+// these predictions: Two forms are considered similar iff they have the same
+// `FormDataParser` predictions.
+BASE_FEATURE(kAndroidAutofillUsePwmPredictionsForOverrides,
+             "AndroidAutofillUsePwmPredictionsForOverrides",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
 static jlong JNI_AndroidAutofillFeatures_GetFeature(JNIEnv* env, jint ordinal) {
   return reinterpret_cast<jlong>(kFeaturesExposedToJava[ordinal]);
 }
diff --git a/components/android_autofill/browser/android_autofill_features.h b/components/android_autofill/browser/android_autofill_features.h
index 10c5fa3..b05a318 100644
--- a/components/android_autofill/browser/android_autofill_features.h
+++ b/components/android_autofill/browser/android_autofill_features.h
@@ -15,7 +15,7 @@
 
 BASE_DECLARE_FEATURE(kAndroidAutofillPrefillRequestsForLoginForms);
 
-BASE_DECLARE_FEATURE(kAndroidAutofillSignatureForPrefillRequestSimilarityCheck);
+BASE_DECLARE_FEATURE(kAndroidAutofillUsePwmPredictionsForOverrides);
 
 BASE_DECLARE_FEATURE(kAndroidAutofillSupportVisibilityChanges);
 
diff --git a/components/android_autofill/browser/autofill_provider_android.cc b/components/android_autofill/browser/autofill_provider_android.cc
index 4cc6fbb..a43dc5c 100644
--- a/components/android_autofill/browser/autofill_provider_android.cc
+++ b/components/android_autofill/browser/autofill_provider_android.cc
@@ -205,8 +205,7 @@
   }
   if (form_structure &&
       base::FeatureList::IsEnabled(
-          features::
-              kAndroidAutofillSignatureForPrefillRequestSimilarityCheck)) {
+          features::kAndroidAutofillUsePwmPredictionsForOverrides)) {
     CHECK_EQ(form.global_id(), form_structure->global_id());
     std::unique_ptr<PasswordForm> pw_form =
         ParseToPasswordForm(*form_structure);
@@ -280,8 +279,7 @@
           kPrefillRequestStateUma,
           PrefillRequestState::kRequestSentFormChanged);
       if (!base::FeatureList::IsEnabled(
-              features::
-                  kAndroidAutofillSignatureForPrefillRequestSimilarityCheck)) {
+              features::kAndroidAutofillUsePwmPredictionsForOverrides)) {
         base::UmaHistogramExactLinear(
             kSimilarityCheckCacheRequestUma,
             ProjectSimilarityCheckResultToMetricsValue(
@@ -718,8 +716,7 @@
           PasswordParserOverrides::FromLoginForm(*pw_form, *form_structure);
       overrides &&
       base::FeatureList::IsEnabled(
-          features::
-              kAndroidAutofillSignatureForPrefillRequestSimilarityCheck)) {
+          features::kAndroidAutofillUsePwmPredictionsForOverrides)) {
     // If we manage to match the fields that the password form parser identified
     // as username and password fields, override their types.
     cached_data_->password_parser_overrides = *std::move(overrides);
diff --git a/components/android_autofill/browser/autofill_provider_android_unittest.cc b/components/android_autofill/browser/autofill_provider_android_unittest.cc
index 1878ef3b..95f793c1d2 100644
--- a/components/android_autofill/browser/autofill_provider_android_unittest.cc
+++ b/components/android_autofill/browser/autofill_provider_android_unittest.cc
@@ -685,7 +685,7 @@
   scoped_feature_list.InitWithFeatures(
       /*enabled_features=*/
       {features::kAndroidAutofillPrefillRequestsForLoginForms,
-       features::kAndroidAutofillSignatureForPrefillRequestSimilarityCheck},
+       features::kAndroidAutofillUsePwmPredictionsForOverrides},
       /*disabled_features=*/{});
 
   FormData form =
@@ -719,7 +719,7 @@
   scoped_feature_list.InitWithFeatures(
       /*enabled_features=*/
       {features::kAndroidAutofillPrefillRequestsForLoginForms,
-       features::kAndroidAutofillSignatureForPrefillRequestSimilarityCheck},
+       features::kAndroidAutofillUsePwmPredictionsForOverrides},
       /*disabled_features=*/{});
 
   FormData form =
@@ -768,10 +768,10 @@
   AutofillProviderAndroidPrefillRequestTest() {
     if (GetParam()) {
       param_feature_list_.InitAndEnableFeature(
-          features::kAndroidAutofillSignatureForPrefillRequestSimilarityCheck);
+          features::kAndroidAutofillUsePwmPredictionsForOverrides);
     } else {
       param_feature_list_.InitAndDisableFeature(
-          features::kAndroidAutofillSignatureForPrefillRequestSimilarityCheck);
+          features::kAndroidAutofillUsePwmPredictionsForOverrides);
     }
   }
 
diff --git a/components/android_autofill/browser/java/src/org/chromium/components/autofill/AndroidAutofillFeatures.java b/components/android_autofill/browser/java/src/org/chromium/components/autofill/AndroidAutofillFeatures.java
index edcc102..643d153 100644
--- a/components/android_autofill/browser/java/src/org/chromium/components/autofill/AndroidAutofillFeatures.java
+++ b/components/android_autofill/browser/java/src/org/chromium/components/autofill/AndroidAutofillFeatures.java
@@ -24,11 +24,10 @@
             "AndroidAutofillFormSubmissionCheckById";
     public static final String ANDROID_AUTOFILL_PREFILL_REQUESTS_FOR_LOGIN_FORMS_NAME =
             "AndroidAutofillPrefillRequestsForLoginForms";
-    public static final String
-            ANDROID_AUTOFILL_SIGNATURE_FOR_PREFILL_REQUEST_SIMILARITY_CHECK_NAME =
-                    "AndroidAutofillSignatureForPrefillRequestSimilarityCheck";
     public static final String ANDROID_AUTOFILL_SUPPORT_VISIBILITY_CHANGES_NAME =
             "AndroidAutofillSupportVisibilityChanges";
+    public static final String ANDROID_AUTOFILL_USE_PWM_PREDICTIONS_FOR_OVERRIDES_NAME =
+            "AndroidAutofillUsePwmPredictionsForOverrides";
 
     public static final AndroidAutofillFeatures ANDROID_AUTOFILL_BOTTOM_SHEET_WORKAROUND =
             new AndroidAutofillFeatures(0, ANDROID_AUTOFILL_BOTTOM_SHEET_WORKAROUND_NAME);
@@ -36,13 +35,10 @@
             new AndroidAutofillFeatures(1, ANDROID_AUTOFILL_FORM_SUBMISSION_CHECK_BY_ID_NAME);
     public static final AndroidAutofillFeatures ANDROID_AUTOFILL_PREFILL_REQUESTS_FOR_LOGIN_FORMS =
             new AndroidAutofillFeatures(2, ANDROID_AUTOFILL_PREFILL_REQUESTS_FOR_LOGIN_FORMS_NAME);
-    public static final AndroidAutofillFeatures
-            ANDROID_AUTOFILL_SIGNATURE_FOR_PREFILL_REQUEST_SIMILARITY_CHECK =
-                    new AndroidAutofillFeatures(
-                            3,
-                            ANDROID_AUTOFILL_SIGNATURE_FOR_PREFILL_REQUEST_SIMILARITY_CHECK_NAME);
     public static final AndroidAutofillFeatures ANDROID_AUTOFILL_SUPPORT_VISIBILITY_CHANGES =
-            new AndroidAutofillFeatures(4, ANDROID_AUTOFILL_SUPPORT_VISIBILITY_CHANGES_NAME);
+            new AndroidAutofillFeatures(3, ANDROID_AUTOFILL_SUPPORT_VISIBILITY_CHANGES_NAME);
+    public static final AndroidAutofillFeatures ANDROID_AUTOFILL_USE_PWM_PREDICTIONS_FOR_OVERRIDES =
+            new AndroidAutofillFeatures(4, ANDROID_AUTOFILL_USE_PWM_PREDICTIONS_FOR_OVERRIDES_NAME);
 
     private final int mOrdinal;
 
diff --git a/components/autofill/content/renderer/autofill_agent.cc b/components/autofill/content/renderer/autofill_agent.cc
index d8f2daf..2acd0fb 100644
--- a/components/autofill/content/renderer/autofill_agent.cc
+++ b/components/autofill/content/renderer/autofill_agent.cc
@@ -778,8 +778,10 @@
       previewed_elements;
   for (const auto& [previewed_element, prior_autofill_state] :
        previewed_elements_) {
-    previewed_elements.emplace_back(previewed_element.GetField(),
-                                    prior_autofill_state);
+    if (WebFormControlElement field = previewed_element.GetField();
+        !field.IsNull()) {
+      previewed_elements.emplace_back(field, prior_autofill_state);
+    }
   }
   form_util::ClearPreviewedElements(last_action_type_, previewed_elements,
                                     last_queried_element);
diff --git a/components/autofill/core/browser/BUILD.gn b/components/autofill/core/browser/BUILD.gn
index 81d7b08..0b3306f 100644
--- a/components/autofill/core/browser/BUILD.gn
+++ b/components/autofill/core/browser/BUILD.gn
@@ -117,8 +117,6 @@
     "crowdsourcing/autofill_crowdsourcing_manager.h",
     "data_model/address.cc",
     "data_model/address.h",
-    "data_model/autofill_address_component_store.cc",
-    "data_model/autofill_address_component_store.h",
     "data_model/autofill_data_model.cc",
     "data_model/autofill_data_model.h",
     "data_model/autofill_feature_guarded_address_component.cc",
@@ -143,6 +141,8 @@
     "data_model/autofill_structured_address.h",
     "data_model/autofill_structured_address_component.cc",
     "data_model/autofill_structured_address_component.h",
+    "data_model/autofill_structured_address_component_store.cc",
+    "data_model/autofill_structured_address_component_store.h",
     "data_model/autofill_structured_address_constants.cc",
     "data_model/autofill_structured_address_constants.h",
     "data_model/autofill_structured_address_format_provider.cc",
diff --git a/components/autofill/core/browser/autofill_suggestion_generator.cc b/components/autofill/core/browser/autofill_suggestion_generator.cc
index 17722273..30c30b07 100644
--- a/components/autofill/core/browser/autofill_suggestion_generator.cc
+++ b/components/autofill/core/browser/autofill_suggestion_generator.cc
@@ -237,6 +237,7 @@
 Suggestion GetFillFullAddressSuggestion(Suggestion::BackendId backend_id) {
   Suggestion suggestion(l10n_util::GetStringUTF16(
       IDS_AUTOFILL_FILL_ADDRESS_GROUP_POPUP_OPTION_SELECTED));
+  suggestion.main_text.is_primary = Suggestion::Text::IsPrimary(false);
   suggestion.popup_item_id = PopupItemId::kFillFullAddress;
   suggestion.payload = backend_id;
   suggestion.acceptance_a11y_announcement = l10n_util::GetStringUTF16(
@@ -249,6 +250,7 @@
   Suggestion suggestion(l10n_util::GetStringUTF16(
       IDS_AUTOFILL_FILL_NAME_GROUP_POPUP_OPTION_SELECTED));
   suggestion.popup_item_id = PopupItemId::kFillFullName;
+  suggestion.main_text.is_primary = Suggestion::Text::IsPrimary(false);
   suggestion.payload = backend_id;
   suggestion.acceptance_a11y_announcement = l10n_util::GetStringUTF16(
       IDS_AUTOFILL_A11Y_ANNOUNCE_FILL_NAME_GROUP_POPUP_OPTION_SELECTED);
@@ -412,9 +414,11 @@
 
   bool added_any_address_line =
       AddAddressLineChildSuggestions(profile, app_locale, suggestion.children);
+  bool added_city = AddAddressFieldByFieldSuggestions(
+      {ADDRESS_HOME_CITY}, profile, app_locale, suggestion.children);
   bool added_zip = AddAddressFieldByFieldSuggestions(
       {ADDRESS_HOME_ZIP}, profile, app_locale, suggestion.children);
-  if (added_any_address_line || added_zip) {
+  if (added_any_address_line || added_zip || added_city) {
     suggestion.children.push_back(
         AutofillSuggestionGenerator::CreateSeparator());
   }
@@ -1232,9 +1236,13 @@
   FieldTypeGroup trigger_field_type_group =
       GroupTypeOfFieldType(trigger_field_type);
   for (const AutofillProfile* profile : profiles) {
-    // Name fields should have `NAME_FULL` as main text.
+    // Name fields should have `NAME_FULL` as main text, unless in field by
+    // field filling mode.
+    const PopupItemId popup_item_id = GetProfileSuggestionPopupItemId(
+        last_targeted_fields, trigger_field_type);
     FieldType main_text_field_type =
         GroupTypeOfFieldType(trigger_field_type) == FieldTypeGroup::kName &&
+                popup_item_id != PopupItemId::kAddressFieldByFieldFilling &&
                 base::FeatureList::IsEnabled(
                     features::kAutofillGranularFillingAvailable)
             ? NAME_FULL
@@ -1252,8 +1260,7 @@
     suggestions.back().payload = Suggestion::Guid(profile->guid());
     suggestions.back().acceptance_a11y_announcement =
         l10n_util::GetStringUTF16(IDS_AUTOFILL_A11Y_ANNOUNCE_FILLED_FORM);
-    suggestions.back().popup_item_id = GetProfileSuggestionPopupItemId(
-        last_targeted_fields, trigger_field_type);
+    suggestions.back().popup_item_id = popup_item_id;
     suggestions.back().is_acceptable = IsAddressType(trigger_field_type);
     suggestions.back().hidden_prior_to_address_rewriter_usage =
         previously_hidden_profiles_guid.contains(profile->guid());
diff --git a/components/autofill/core/browser/autofill_suggestion_generator_unittest.cc b/components/autofill/core/browser/autofill_suggestion_generator_unittest.cc
index fc835db..a7143e9a 100644
--- a/components/autofill/core/browser/autofill_suggestion_generator_unittest.cc
+++ b/components/autofill/core/browser/autofill_suggestion_generator_unittest.cc
@@ -949,7 +949,7 @@
   const AutofillProfile profile_ = test::GetFullProfile();
 };
 
-// Test that only "Fill full address" is added when the target field is
+// Test that only "Fill address" is added when the target field is
 // `ADDRESS_HOME_LINE1` and no other suggestion exist with the same
 // `Suggestion::main_text` and `ADDRESS_HOME_LINE1`.
 TEST_F(AutofillChildrenSuggestionGeneratorTest,
@@ -962,9 +962,8 @@
       /*field_types=*/{ADDRESS_HOME_LINE1, ADDRESS_HOME_LINE2});
 
   ASSERT_EQ(suggestions.size(), 1u);
-  EXPECT_EQ(suggestions[0].labels,
-            std::vector<std::vector<Suggestion::Text>>(
-                {{Suggestion::Text(u"Fill full address")}}));
+  EXPECT_EQ(suggestions[0].labels, std::vector<std::vector<Suggestion::Text>>(
+                                       {{Suggestion::Text(u"Fill address")}}));
 }
 
 // Test that a differentiating label is added when the `Suggestion::main_text`
@@ -989,7 +988,7 @@
   ASSERT_EQ(suggestions.size(), 2u);
   EXPECT_EQ(suggestions[0].labels,
             std::vector<std::vector<Suggestion::Text>>(
-                {{Suggestion::Text(u"Fill full address - John Doe")}}));
+                {{Suggestion::Text(u"Fill address - John Doe")}}));
 }
 
 // Test similar to the one above. However also makes sure that
@@ -1019,13 +1018,13 @@
       suggestions[0].labels,
       std::vector<std::vector<Suggestion::Text>>(
           {{Suggestion::Text(
-                u"Fill full address - " +
+                u"Fill address - " +
                 profile_1.GetInfo(ADDRESS_HOME_LINE1, app_locale()) + u", "),
             Suggestion::Text(u"John H. Doe, a@gmail.com")}}));
 }
 
 // When there is no need to detailing or differentiating label, we add only the
-// granular filling label, either "Fill full name" or "Fill full address".
+// granular filling label, either "Fill full name" or "Fill address".
 TEST_F(AutofillChildrenSuggestionGeneratorTest,
        CreateSuggestionsFromProfiles_GroupFillingLabels_AddOnlyFillName) {
   std::vector<Suggestion> suggestions = CreateSuggestionWithChildrenFromProfile(
@@ -1107,14 +1106,15 @@
   // 5. line separator
   // 6. address line 1
   // 7. address line 2
-  // 8. Zip
-  // 9. line separator
-  // 10. phone number
-  // 11. email
-  // 12. line separator
-  // 13. edit profile
-  // 14. delete address
-  ASSERT_EQ(14U, suggestions[0].children.size());
+  // 8. City
+  // 9. Zip
+  // 10. line separator
+  // 11. phone number
+  // 12. email
+  // 13. line separator
+  // 14. edit profile
+  // 15. delete address
+  ASSERT_EQ(15U, suggestions[0].children.size());
   EXPECT_THAT(
       suggestions[0].children,
       ElementsAre(
@@ -1142,6 +1142,10 @@
               ADDRESS_HOME_LINE2, Suggestion::Guid(profile().guid())),
           EqualsFieldByFieldFillingSuggestion(
               PopupItemId::kAddressFieldByFieldFilling,
+              profile().GetInfo(ADDRESS_HOME_CITY, app_locale()),
+              ADDRESS_HOME_CITY, Suggestion::Guid(profile().guid())),
+          EqualsFieldByFieldFillingSuggestion(
+              PopupItemId::kAddressFieldByFieldFilling,
               profile().GetInfo(ADDRESS_HOME_ZIP, app_locale()),
               ADDRESS_HOME_ZIP, Suggestion::Guid(profile().guid())),
           EqualsSuggestion(PopupItemId::kSeparator),
@@ -1193,10 +1197,14 @@
       profile(), std::optional<FieldTypeSet>({NAME_LAST}), NAME_FIRST);
 
   ASSERT_EQ(suggestions.size(), 1u);
+  // Differently from other filling modes, where when focusing on a name field
+  // the NAME_FULL is rendered in the main text, field-by-field filling always
+  // displays the value that will actually be used to fill the field as main
+  // text.
   EXPECT_THAT(suggestions[0],
               EqualsFieldByFieldFillingSuggestion(
                   PopupItemId::kAddressFieldByFieldFilling,
-                  profile().GetInfo(NAME_FULL, app_locale()), NAME_FIRST,
+                  profile().GetInfo(NAME_FIRST, app_locale()), NAME_FIRST,
                   Suggestion::Guid(profile().guid()), {{}}));
 }
 
@@ -1243,21 +1251,22 @@
   // 4. line separator
   // 5. address line 1
   // 6. address line 2
-  // 7. Zip
-  // 8. line separator
-  // 9. phone number
-  // 10. email
-  // 11. line separator
-  // 12. edit profile
-  // 13. delete address
-  ASSERT_EQ(13U, suggestions[0].children.size());
+  // 7. City
+  // 8. Zip
+  // 9. line separator
+  // 10. phone number
+  // 12. email
+  // 13. line separator
+  // 13. edit profile
+  // 14. delete address
+  ASSERT_EQ(14U, suggestions[0].children.size());
 
   // Triggering field is international phone number type, international phone
   // number should be shown to the user.
-  EXPECT_THAT(suggestions[0].children[8],
+  EXPECT_THAT(suggestions[0].children[9],
               EqualsSuggestion(PopupItemId::kFillFullPhoneNumber,
                                GetFormattedInternationalNumber()));
-  EXPECT_THAT(suggestions[0].children[8].children, IsEmpty());
+  EXPECT_THAT(suggestions[0].children[9].children, IsEmpty());
 }
 
 // Asserts that when the triggering field is a phone field, the phone number
@@ -1279,21 +1288,22 @@
   // 4. line separator
   // 5. address line 1
   // 6. address line 2
-  // 7. Zip
-  // 8. line separator
-  // 9. phone number
-  // 10. email
-  // 11. line separator
-  // 12. edit profile
-  // 13. delete address
-  ASSERT_EQ(13U, suggestions[0].children.size());
+  // 7. City
+  // 8. Zip
+  // 9. line separator
+  // 10. phone number
+  // 11. email
+  // 12. line separator
+  // 13. edit profile
+  // 14. delete address
+  ASSERT_EQ(14U, suggestions[0].children.size());
 
   // Triggering field is phone number country code, international phone number
   // should be shown to the user.
-  EXPECT_THAT(suggestions[0].children[8],
+  EXPECT_THAT(suggestions[0].children[9],
               EqualsSuggestion(PopupItemId::kFillFullPhoneNumber,
                                GetFormattedInternationalNumber()));
-  EXPECT_THAT(suggestions[0].children[8].children, IsEmpty());
+  EXPECT_THAT(suggestions[0].children[9].children, IsEmpty());
 }
 
 // Asserts that when the triggering field is a phone field, the phone number
@@ -1315,20 +1325,21 @@
   // 4. line separator
   // 5. address line 1
   // 6. address line 2
-  // 7. Zip
-  // 8. line separator
-  // 9. phone number
-  // 10. email
-  // 11. line separator
-  // 12. edit profile
-  // 13. delete address
-  ASSERT_EQ(13U, suggestions[0].children.size());
+  // 7. City
+  // 8. Zip
+  // 9. line separator
+  // 10. phone number
+  // 11. email
+  // 12. line separator
+  // 13. edit profile
+  // 14. delete address
+  ASSERT_EQ(14U, suggestions[0].children.size());
   // Triggering field is local phone number type, local phone number should
   // be shown to the user.
-  EXPECT_THAT(suggestions[0].children[8],
+  EXPECT_THAT(suggestions[0].children[9],
               EqualsSuggestion(PopupItemId::kFillFullPhoneNumber,
                                GetFormattedNationalNumber()));
-  EXPECT_THAT(suggestions[0].children[8].children, IsEmpty());
+  EXPECT_THAT(suggestions[0].children[9].children, IsEmpty());
 }
 
 // Same as above but for email fields.
@@ -1345,15 +1356,16 @@
   // 4. line separator
   // 5. address line 1
   // 6. address line 2
-  // 7. Zip
-  // 8. line separator
-  // 9. phone number
-  // 10. email
-  // 11. line separator
-  // 12. edit profile
-  // 13. delete address
-  ASSERT_EQ(13U, suggestions[0].children.size());
-  EXPECT_THAT(suggestions[0].children[9],
+  // 7. City
+  // 8. Zip
+  // 9. line separator
+  // 10. phone number
+  // 11. email
+  // 12. line separator
+  // 13. edit profile
+  // 14. delete address
+  ASSERT_EQ(14U, suggestions[0].children.size());
+  EXPECT_THAT(suggestions[0].children[10],
               Field(&Suggestion::popup_item_id, PopupItemId::kFillFullEmail));
 }
 
@@ -1371,15 +1383,16 @@
   // 5. fill full address
   // 6. address line 1
   // 7. address line 2
-  // 8. Zip
-  // 9. line separator
-  // 10. phone number
-  // 11. email
-  // 12. line separator
-  // 13. edit address
-  // 14. delete address
+  // 8. City
+  // 9. Zip
+  // 10. line separator
+  // 11. phone number
+  // 12. email
+  // 13. line separator
+  // 14. edit address
+  // 15. delete address
   ASSERT_EQ(suggestions.size(), 1u);
-  ASSERT_EQ(14U, suggestions[0].children.size());
+  ASSERT_EQ(15U, suggestions[0].children.size());
   EXPECT_THAT(suggestions[0].children[4],
               Field(&Suggestion::popup_item_id, PopupItemId::kFillFullAddress));
 }
@@ -1535,7 +1548,7 @@
 }
 
 // Tests that a non-address field suggestion has all the profile fields as
-// children, and doesn't have children like "Fill full address" or "Fill full
+// children, and doesn't have children like "Fill address" or "Fill full
 // name".
 TEST_F(AutofillNonAddressFieldsSuggestionGeneratorTest,
        SuggestionHasCorrectChildren) {
@@ -1550,15 +1563,16 @@
   // 4. line separator
   // 5. address line 1
   // 6. address line 2
-  // 7. Zip
-  // 8. line separator
-  // 9. phone number
-  // 10. email
-  // 11. line separator
-  // 12. edit address
-  // 13. delete address
+  // 7. City
+  // 8. Zip
+  // 9. line separator
+  // 10. phone number
+  // 11. email
+  // 12. line separator
+  // 13. edit address
+  // 14. delete address
   ASSERT_EQ(suggestions.size(), 1u);
-  ASSERT_EQ(13u, suggestions[0].children.size());
+  ASSERT_EQ(14u, suggestions[0].children.size());
 
   EXPECT_THAT(
       suggestions[0].children,
@@ -1586,6 +1600,10 @@
               ADDRESS_HOME_LINE2, Suggestion::Guid(profile().guid())),
           EqualsFieldByFieldFillingSuggestion(
               PopupItemId::kAddressFieldByFieldFilling,
+              profile().GetInfo(ADDRESS_HOME_CITY, app_locale()),
+              ADDRESS_HOME_CITY, Suggestion::Guid(profile().guid())),
+          EqualsFieldByFieldFillingSuggestion(
+              PopupItemId::kAddressFieldByFieldFilling,
               profile().GetInfo(ADDRESS_HOME_ZIP, app_locale()),
               ADDRESS_HOME_ZIP, Suggestion::Guid(profile().guid())),
           EqualsSuggestion(PopupItemId::kSeparator),
diff --git a/components/autofill/core/browser/data_model/address.h b/components/autofill/core/browser/data_model/address.h
index f204a13c..5f98bfb 100644
--- a/components/autofill/core/browser/data_model/address.h
+++ b/components/autofill/core/browser/data_model/address.h
@@ -10,9 +10,9 @@
 
 #include "base/compiler_specific.h"
 #include "components/autofill/core/browser/country_type.h"
-#include "components/autofill/core/browser/data_model/autofill_address_component_store.h"
 #include "components/autofill/core/browser/data_model/autofill_i18n_api.h"
 #include "components/autofill/core/browser/data_model/autofill_structured_address.h"
+#include "components/autofill/core/browser/data_model/autofill_structured_address_component_store.h"
 #include "components/autofill/core/browser/data_model/form_group.h"
 #include "components/autofill/core/browser/geo/alternative_state_name_map.h"
 
diff --git a/components/autofill/core/browser/data_model/autofill_i18n_api.h b/components/autofill/core/browser/data_model/autofill_i18n_api.h
index b4ac943..fa57762 100644
--- a/components/autofill/core/browser/data_model/autofill_i18n_api.h
+++ b/components/autofill/core/browser/data_model/autofill_i18n_api.h
@@ -6,10 +6,10 @@
 #define COMPONENTS_AUTOFILL_CORE_BROWSER_DATA_MODEL_AUTOFILL_I18N_API_H_
 
 #include "components/autofill/core/browser/country_type.h"
-#include "components/autofill/core/browser/data_model/autofill_address_component_store.h"
 #include "components/autofill/core/browser/data_model/autofill_i18n_hierarchies.h"
 #include "components/autofill/core/browser/data_model/autofill_i18n_parsing_expression_components.h"
 #include "components/autofill/core/browser/data_model/autofill_structured_address_component.h"
+#include "components/autofill/core/browser/data_model/autofill_structured_address_component_store.h"
 
 namespace autofill::i18n_model_definition {
 
diff --git a/components/autofill/core/browser/data_model/autofill_address_component_store.cc b/components/autofill/core/browser/data_model/autofill_structured_address_component_store.cc
similarity index 92%
rename from components/autofill/core/browser/data_model/autofill_address_component_store.cc
rename to components/autofill/core/browser/data_model/autofill_structured_address_component_store.cc
index 5dc463aa..abd0cba 100644
--- a/components/autofill/core/browser/data_model/autofill_address_component_store.cc
+++ b/components/autofill/core/browser/data_model/autofill_structured_address_component_store.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/autofill/core/browser/data_model/autofill_address_component_store.h"
+#include "components/autofill/core/browser/data_model/autofill_structured_address_component_store.h"
 
 namespace autofill {
 
diff --git a/components/autofill/core/browser/data_model/autofill_address_component_store.h b/components/autofill/core/browser/data_model/autofill_structured_address_component_store.h
similarity index 85%
rename from components/autofill/core/browser/data_model/autofill_address_component_store.h
rename to components/autofill/core/browser/data_model/autofill_structured_address_component_store.h
index f6cc7e0..4960f4a 100644
--- a/components/autofill/core/browser/data_model/autofill_address_component_store.h
+++ b/components/autofill/core/browser/data_model/autofill_structured_address_component_store.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_DATA_MODEL_AUTOFILL_ADDRESS_COMPONENT_STORE_H_
-#define COMPONENTS_AUTOFILL_CORE_BROWSER_DATA_MODEL_AUTOFILL_ADDRESS_COMPONENT_STORE_H_
+#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_DATA_MODEL_AUTOFILL_STRUCTURED_ADDRESS_COMPONENT_STORE_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_DATA_MODEL_AUTOFILL_STRUCTURED_ADDRESS_COMPONENT_STORE_H_
 
 #include "base/containers/flat_map.h"
 #include "components/autofill/core/browser/data_model/autofill_structured_address_component.h"
@@ -43,4 +43,4 @@
 
 }  // namespace autofill
 
-#endif  // COMPONENTS_AUTOFILL_CORE_BROWSER_DATA_MODEL_AUTOFILL_ADDRESS_COMPONENT_STORE_H_
+#endif  // COMPONENTS_AUTOFILL_CORE_BROWSER_DATA_MODEL_AUTOFILL_STRUCTURED_ADDRESS_COMPONENT_STORE_H_
diff --git a/components/autofill/core/browser/metrics/granular_filling_metrics.cc b/components/autofill/core/browser/metrics/granular_filling_metrics.cc
index 45f23e8f..6c2b2fa5 100644
--- a/components/autofill/core/browser/metrics/granular_filling_metrics.cc
+++ b/components/autofill/core/browser/metrics/granular_filling_metrics.cc
@@ -45,6 +45,8 @@
       return AutofillFieldByFieldFillingTypes::kCreditCardExpiryYear;
     case CREDIT_CARD_EXP_MONTH:
       return AutofillFieldByFieldFillingTypes::kCreditCardExpiryMonth;
+    case ADDRESS_HOME_CITY:
+      return AutofillFieldByFieldFillingTypes::kCity;
     default:
       NOTREACHED_NORETURN();
   }
@@ -86,8 +88,7 @@
                     triggering_field_type_matches_filling_product
                         ? ".TriggeringFieldMatchesFillingProduct"
                         : ".TriggeringFieldDoesNotMatchFillingProduct"}),
-      GetFieldByFieldFillingType(field_type_used),
-      AutofillFieldByFieldFillingTypes::kMaxValue);
+      GetFieldByFieldFillingType(field_type_used));
 }
 
 }  // namespace autofill::autofill_metrics
diff --git a/components/autofill/core/browser/metrics/granular_filling_metrics.h b/components/autofill/core/browser/metrics/granular_filling_metrics.h
index fb0d78f5..40de18c 100644
--- a/components/autofill/core/browser/metrics/granular_filling_metrics.h
+++ b/components/autofill/core/browser/metrics/granular_filling_metrics.h
@@ -50,7 +50,8 @@
   kCreditCardExpiryDate = 12,
   kCreditCardExpiryYear = 13,
   kCreditCardExpiryMonth = 14,
-  kMaxValue = kCreditCardExpiryMonth
+  kCity = 15,
+  kMaxValue = kCity
 };
 
 // This metric is only relevant for granular filling, i.e. when the edit dialog
diff --git a/components/autofill_strings.grdp b/components/autofill_strings.grdp
index 0232cbe..881609f 100644
--- a/components/autofill_strings.grdp
+++ b/components/autofill_strings.grdp
@@ -142,11 +142,11 @@
   </message>
 
   <message name="IDS_AUTOFILL_A11Y_ANNOUNCE_FILL_ADDRESS_GROUP_POPUP_OPTION_SELECTED" desc="Screen reader announcement indicating that filling address fields option was selected.">
-    The fill full address option was selected
+    The fill address option was selected
   </message>
 
   <message name="IDS_AUTOFILL_FILL_ADDRESS_GROUP_POPUP_OPTION_SELECTED" desc="The text displayed at one of the autofill submenus options allowing the users to fill all address related fields at once." meaning="Fills all address related fields">
-    Fill full address
+    Fill address
   </message>
 
   <message name="IDS_AUTOFILL_A11Y_ANNOUNCE_FILL_NAME_GROUP_POPUP_OPTION_SELECTED" desc="Screen reader announcement indicating that filling name fields option was selected.">
diff --git a/components/autofill_strings_grdp/IDS_AUTOFILL_A11Y_ANNOUNCE_FILL_ADDRESS_GROUP_POPUP_OPTION_SELECTED.png.sha1 b/components/autofill_strings_grdp/IDS_AUTOFILL_A11Y_ANNOUNCE_FILL_ADDRESS_GROUP_POPUP_OPTION_SELECTED.png.sha1
index 8714bae..b431a4f 100644
--- a/components/autofill_strings_grdp/IDS_AUTOFILL_A11Y_ANNOUNCE_FILL_ADDRESS_GROUP_POPUP_OPTION_SELECTED.png.sha1
+++ b/components/autofill_strings_grdp/IDS_AUTOFILL_A11Y_ANNOUNCE_FILL_ADDRESS_GROUP_POPUP_OPTION_SELECTED.png.sha1
@@ -1 +1 @@
-06e6741e46476cd7c299c203b78cd79963747cdd
\ No newline at end of file
+2d5c8ae96b204ed16502102c94d2f90b504edb95
\ No newline at end of file
diff --git a/components/autofill_strings_grdp/IDS_AUTOFILL_FILL_ADDRESS_GROUP_POPUP_OPTION_SELECTED.png.sha1 b/components/autofill_strings_grdp/IDS_AUTOFILL_FILL_ADDRESS_GROUP_POPUP_OPTION_SELECTED.png.sha1
index 8714bae..b431a4f 100644
--- a/components/autofill_strings_grdp/IDS_AUTOFILL_FILL_ADDRESS_GROUP_POPUP_OPTION_SELECTED.png.sha1
+++ b/components/autofill_strings_grdp/IDS_AUTOFILL_FILL_ADDRESS_GROUP_POPUP_OPTION_SELECTED.png.sha1
@@ -1 +1 @@
-06e6741e46476cd7c299c203b78cd79963747cdd
\ No newline at end of file
+2d5c8ae96b204ed16502102c94d2f90b504edb95
\ No newline at end of file
diff --git a/components/content_settings/core/browser/content_settings_default_provider.cc b/components/content_settings/core/browser/content_settings_default_provider.cc
index 570ea16b..9545f229 100644
--- a/components/content_settings/core/browser/content_settings_default_provider.cc
+++ b/components/content_settings/core/browser/content_settings_default_provider.cc
@@ -169,7 +169,8 @@
 
 DefaultProvider::~DefaultProvider() = default;
 
-// TODO(b/307193732): handle the PartitionKey in all relevant methods.
+// TODO(b/307193732): handle the PartitionKey in all relevant methods, including
+// when we call NotifyObservers().
 bool DefaultProvider::SetWebsiteSetting(
     const ContentSettingsPattern& primary_pattern,
     const ContentSettingsPattern& secondary_pattern,
@@ -209,7 +210,8 @@
   }
 
   NotifyObservers(ContentSettingsPattern::Wildcard(),
-                  ContentSettingsPattern::Wildcard(), content_type);
+                  ContentSettingsPattern::Wildcard(), content_type,
+                  /*partition_key=*/nullptr);
 
   return true;
 }
@@ -347,7 +349,8 @@
   }
 
   NotifyObservers(ContentSettingsPattern::Wildcard(),
-                  ContentSettingsPattern::Wildcard(), content_type);
+                  ContentSettingsPattern::Wildcard(), content_type,
+                  /*partition_key=*/nullptr);
 }
 
 base::Value DefaultProvider::ReadFromPref(ContentSettingsType content_type) {
diff --git a/components/content_settings/core/browser/content_settings_observable_provider.cc b/components/content_settings/core/browser/content_settings_observable_provider.cc
index 06c03d13..d483180 100644
--- a/components/content_settings/core/browser/content_settings_observable_provider.cc
+++ b/components/content_settings/core/browser/content_settings_observable_provider.cc
@@ -26,13 +26,17 @@
 void ObservableProvider::NotifyObservers(
     const ContentSettingsPattern& primary_pattern,
     const ContentSettingsPattern& secondary_pattern,
-    ContentSettingsType content_type) {
+    ContentSettingsType content_type,
+    const PartitionKey* partition_key) {
   DCHECK(primary_pattern.IsValid())
       << "pattern: " << primary_pattern.ToString();
   DCHECK(secondary_pattern.IsValid())
       << "pattern: " << secondary_pattern.ToString();
   for (Observer& observer : observer_list_) {
     observer.OnContentSettingChanged(primary_pattern, secondary_pattern,
+                                     ContentSettingsTypeSet(content_type),
+                                     partition_key);
+    observer.OnContentSettingChanged(primary_pattern, secondary_pattern,
                                      ContentSettingsTypeSet(content_type));
     observer.OnContentSettingChanged(primary_pattern, secondary_pattern,
                                      content_type);
diff --git a/components/content_settings/core/browser/content_settings_observable_provider.h b/components/content_settings/core/browser/content_settings_observable_provider.h
index b4505f01..39dbb40b 100644
--- a/components/content_settings/core/browser/content_settings_observable_provider.h
+++ b/components/content_settings/core/browser/content_settings_observable_provider.h
@@ -13,6 +13,8 @@
 
 namespace content_settings {
 
+class PartitionKey;
+
 class ObservableProvider : public ProviderInterface {
  public:
   ObservableProvider();
@@ -22,9 +24,11 @@
   void RemoveObserver(Observer* observer);
 
  protected:
+  // See `content_settings::Observer` for details.
   void NotifyObservers(const ContentSettingsPattern& primary_pattern,
                        const ContentSettingsPattern& secondary_pattern,
-                       ContentSettingsType content_type);
+                       ContentSettingsType content_type,
+                       const PartitionKey* partition_key);
   void RemoveAllObservers();
   bool CalledOnValidThread();
 
diff --git a/components/content_settings/core/browser/content_settings_observer.h b/components/content_settings/core/browser/content_settings_observer.h
index 4e5ba03..371a060 100644
--- a/components/content_settings/core/browser/content_settings_observer.h
+++ b/components/content_settings/core/browser/content_settings_observer.h
@@ -6,13 +6,27 @@
 #define COMPONENTS_CONTENT_SETTINGS_CORE_BROWSER_CONTENT_SETTINGS_OBSERVER_H_
 
 #include "components/content_settings/core/browser/content_settings_type_set.h"
+#include "components/content_settings/core/common/content_settings_partition_key.h"
 #include "components/content_settings/core/common/content_settings_pattern.h"
 #include "components/content_settings/core/common/content_settings_types.h"
 
 namespace content_settings {
 
+class PartitionKey;
+
 class Observer {
  public:
+  // partition_key is nullptr if the change is not specific to a particular
+  // PartitionKey.
+  //
+  // TODO(b/307193732): migrate the implementors of the other methods and remove
+  // them.
+  virtual void OnContentSettingChanged(
+      const ContentSettingsPattern& primary_pattern,
+      const ContentSettingsPattern& secondary_pattern,
+      ContentSettingsTypeSet content_type_set,
+      const PartitionKey* partition_key) {}
+
   virtual void OnContentSettingChanged(
       const ContentSettingsPattern& primary_pattern,
       const ContentSettingsPattern& secondary_pattern,
diff --git a/components/content_settings/core/browser/content_settings_policy_provider.cc b/components/content_settings/core/browser/content_settings_policy_provider.cc
index b22ce5c..86590cf 100644
--- a/components/content_settings/core/browser/content_settings_policy_provider.cc
+++ b/components/content_settings/core/browser/content_settings_policy_provider.cc
@@ -603,7 +603,7 @@
 
   NotifyObservers(ContentSettingsPattern::Wildcard(),
                   ContentSettingsPattern::Wildcard(),
-                  ContentSettingsType::DEFAULT);
+                  ContentSettingsType::DEFAULT, /*partition_key=*/nullptr);
 }
 
 }  // namespace content_settings
diff --git a/components/content_settings/core/browser/content_settings_pref.cc b/components/content_settings/core/browser/content_settings_pref.cc
index cb569a24..bbecf63 100644
--- a/components/content_settings/core/browser/content_settings_pref.cc
+++ b/components/content_settings/core/browser/content_settings_pref.cc
@@ -251,7 +251,8 @@
                partition_key);
   }
 
-  notify_callback_.Run(primary_pattern, secondary_pattern, content_type_);
+  notify_callback_.Run(primary_pattern, secondary_pattern, content_type_,
+                       &partition_key);
 }
 
 void ContentSettingsPref::ClearAllContentSettingsRules(
@@ -281,7 +282,8 @@
   }
 
   notify_callback_.Run(ContentSettingsPattern::Wildcard(),
-                       ContentSettingsPattern::Wildcard(), content_type_);
+                       ContentSettingsPattern::Wildcard(), content_type_,
+                       &partition_key);
 }
 
 void ContentSettingsPref::OnShutdown() {
@@ -513,7 +515,8 @@
   ReadContentSettingsFromPref();
 
   notify_callback_.Run(ContentSettingsPattern::Wildcard(),
-                       ContentSettingsPattern::Wildcard(), content_type_);
+                       ContentSettingsPattern::Wildcard(), content_type_,
+                       nullptr);
 }
 
 void ContentSettingsPref::UpdatePref(
diff --git a/components/content_settings/core/browser/content_settings_pref.h b/components/content_settings/core/browser/content_settings_pref.h
index 310550e..79999d2 100644
--- a/components/content_settings/core/browser/content_settings_pref.h
+++ b/components/content_settings/core/browser/content_settings_pref.h
@@ -38,7 +38,8 @@
  public:
   typedef base::RepeatingCallback<void(const ContentSettingsPattern&,
                                        const ContentSettingsPattern&,
-                                       ContentSettingsType)>
+                                       ContentSettingsType,
+                                       const PartitionKey*)>
       NotifyObserversCallback;
 
   ContentSettingsPref(ContentSettingsType content_type,
diff --git a/components/content_settings/core/browser/content_settings_pref_provider.cc b/components/content_settings/core/browser/content_settings_pref_provider.cc
index 6778a333..8827c4f6 100644
--- a/components/content_settings/core/browser/content_settings_pref_provider.cc
+++ b/components/content_settings/core/browser/content_settings_pref_provider.cc
@@ -409,8 +409,10 @@
 
 void PrefProvider::Notify(const ContentSettingsPattern& primary_pattern,
                           const ContentSettingsPattern& secondary_pattern,
-                          ContentSettingsType content_type) {
-  NotifyObservers(primary_pattern, secondary_pattern, content_type);
+                          ContentSettingsType content_type,
+                          const PartitionKey* partition_key) {
+  NotifyObservers(primary_pattern, secondary_pattern, content_type,
+                  partition_key);
 }
 
 void PrefProvider::DiscardOrMigrateObsoletePreferences() {
diff --git a/components/content_settings/core/browser/content_settings_pref_provider.h b/components/content_settings/core/browser/content_settings_pref_provider.h
index b97e8a7..3f45115b 100644
--- a/components/content_settings/core/browser/content_settings_pref_provider.h
+++ b/components/content_settings/core/browser/content_settings_pref_provider.h
@@ -96,7 +96,8 @@
 
   void Notify(const ContentSettingsPattern& primary_pattern,
               const ContentSettingsPattern& secondary_pattern,
-              ContentSettingsType content_type);
+              ContentSettingsType content_type,
+              const PartitionKey* partition_key);
 
   bool SetLastVisitTime(const ContentSettingsPattern& primary_pattern,
                         const ContentSettingsPattern& secondary_pattern,
diff --git a/components/messages/android/java/src/org/chromium/components/messages/MessagesMetrics.java b/components/messages/android/java/src/org/chromium/components/messages/MessagesMetrics.java
index cc64f3c..1fc8c8d 100644
--- a/components/messages/android/java/src/org/chromium/components/messages/MessagesMetrics.java
+++ b/components/messages/android/java/src/org/chromium/components/messages/MessagesMetrics.java
@@ -396,6 +396,18 @@
                 return "TrackingProtectionNotice";
             case MessageIdentifier.DESKTOP_SITE_WINDOW_SETTING:
                 return "DesktopSiteWindowSetting";
+            case MessageIdentifier.PROMPT_HATS_LOCATION_CUSTOM_INVITATION:
+                return "PromptHatsLocationCustomInvitation";
+            case MessageIdentifier.PROMPT_HATS_LOCATION_GENERIC_INVITATION:
+                return "PromptHatsLocationGenericInvitation";
+            case MessageIdentifier.PROMPT_HATS_CAMERA_CUSTOM_INVITATION:
+                return "PromptHatsCameraCustomInvitation";
+            case MessageIdentifier.PROMPT_HATS_CAMERA_GENERIC_INVITATION:
+                return "PromptHatsCameraGenericInvitation";
+            case MessageIdentifier.PROMPT_HATS_MICROPHONE_CUSTOM_INVITATION:
+                return "PromptHatsMicrophoneCustomInvitation";
+            case MessageIdentifier.PROMPT_HATS_MICROPHONE_GENERIC_INVITATION:
+                return "PromptHatsMicrophoneGenericInvitation";
             default:
                 return "Unknown";
         }
diff --git a/components/messages/android/message_enums.h b/components/messages/android/message_enums.h
index 4b4a852d..7a36254 100644
--- a/components/messages/android/message_enums.h
+++ b/components/messages/android/message_enums.h
@@ -69,7 +69,8 @@
 //
 // When adding a new message identifier, make corresponding changes in the
 // following locations:
-// - tools/metrics/histograms/enums.xml: <enum name="MessageIdentifier">
+// - tools/metrics/histograms/metadata/android/enums.xml:
+//       <enum name="MessageIdentifier">
 // - tools/metrics/histograms/metadata/android/histograms.xml:
 //       <variants name="MessageIdentifiers">
 // - MessagesMetrics.java: #messageIdentifierToHistogramSuffix()
@@ -119,6 +120,12 @@
   CVC_SAVE = 39,
   TRACKING_PROTECTION_NOTICE = 40,
   DESKTOP_SITE_WINDOW_SETTING = 41,
+  PROMPT_HATS_LOCATION_CUSTOM_INVITATION = 42,
+  PROMPT_HATS_LOCATION_GENERIC_INVITATION = 43,
+  PROMPT_HATS_CAMERA_CUSTOM_INVITATION = 44,
+  PROMPT_HATS_CAMERA_GENERIC_INVITATION = 45,
+  PROMPT_HATS_MICROPHONE_CUSTOM_INVITATION = 46,
+  PROMPT_HATS_MICROPHONE_GENERIC_INVITATION = 47,
   // Insert new values before this line.
   COUNT
 };
diff --git a/components/payments/core/payment_prefs.cc b/components/payments/core/payment_prefs.cc
index 3196bef..d42858b 100644
--- a/components/payments/core/payment_prefs.cc
+++ b/components/payments/core/payment_prefs.cc
@@ -8,11 +8,6 @@
 
 namespace payments {
 
-const char kPaymentsFirstTransactionCompleted[] =
-    "payments.first_transaction_completed";
-
-const char kCanMakePaymentEnabled[] = "payments.can_make_payment_enabled";
-
 void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry) {
   registry->RegisterBooleanPref(kPaymentsFirstTransactionCompleted, false);
   registry->RegisterBooleanPref(
diff --git a/components/payments/core/payment_prefs.h b/components/payments/core/payment_prefs.h
index 0ffaf19..6469dc5 100644
--- a/components/payments/core/payment_prefs.h
+++ b/components/payments/core/payment_prefs.h
@@ -13,11 +13,13 @@
 
 // True if the profile has already successfully completed at least one payment
 // request transaction.
-extern const char kPaymentsFirstTransactionCompleted[];
+inline constexpr char kPaymentsFirstTransactionCompleted[] =
+    "payments.first_transaction_completed";
 
 // True if the user has allowed canMakePayment to return a truthful value, false
 // if canMakePayment should always return false regardless.
-extern const char kCanMakePaymentEnabled[];
+inline constexpr char kCanMakePaymentEnabled[] =
+    "payments.can_make_payment_enabled";
 
 void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
 
diff --git a/components/permissions/features.cc b/components/permissions/features.cc
index af1f0178..38381a3a 100644
--- a/components/permissions/features.cc
+++ b/components/permissions/features.cc
@@ -189,6 +189,23 @@
 const base::FeatureParam<std::string> kPermissionsPromptSurveyTriggerId{
     &permissions::features::kPermissionsPromptSurvey, "trigger_id", ""};
 
+// WARNING: This parameter is intended only for a one-off A/B experiment on
+// Clank (see crbug.com/1502780) and will be removed thereafter.
+// The experiment is active iff |experimental_custom_invitation_arm_trigger_id|
+// is configured. If it is active, a coin flip determines whether the generic or
+// a custom invitation is shown. These two cases will use distinct trigger IDs
+// in order to properly convey the survey context to the user in both cases. The
+// parameter specifies the alternate set of trigger IDs for the HaTS surveys
+// that should be shown after a customized invitation was shown. The triggerIds
+// configured in |trigger_id| are used if a generic invitation was shown. The
+// configuration of |experimental_custom_invitation_arm_trigger_id| is analogous
+// to that of |trigger_id|. Custom invitations are hardcoded and only supported
+// for the request types geolocation, camera, and microphone.
+const base::FeatureParam<std::string>
+    kPermissionsPromptSurveyCustomInvitationTriggerId{
+        &permissions::features::kPermissionsPromptSurvey,
+        "experimental_custom_invitation_arm_trigger_id", ""};
+
 // If multiple trigger ids are configured, the trigger id at position p only
 // triggers for the request type at position p of the request type filter,
 // and calls the HaTS service with the probability at position p in the
diff --git a/components/permissions/features.h b/components/permissions/features.h
index 7df1363..62d5ea3 100644
--- a/components/permissions/features.h
+++ b/components/permissions/features.h
@@ -111,6 +111,10 @@
 
 COMPONENT_EXPORT(PERMISSIONS_COMMON)
 extern const base::FeatureParam<std::string>
+    kPermissionsPromptSurveyCustomInvitationTriggerId;
+
+COMPONENT_EXPORT(PERMISSIONS_COMMON)
+extern const base::FeatureParam<std::string>
     kPermissionsPromptSurveyDisplayTime;
 
 COMPONENT_EXPORT(PERMISSIONS_COMMON)
diff --git a/components/permissions/permission_hats_trigger_helper.cc b/components/permissions/permission_hats_trigger_helper.cc
index e035a98..49aabdc 100644
--- a/components/permissions/permission_hats_trigger_helper.cc
+++ b/components/permissions/permission_hats_trigger_helper.cc
@@ -4,6 +4,7 @@
 
 #include "components/permissions/permission_hats_trigger_helper.h"
 
+#include <optional>
 #include <utility>
 
 #include "base/check_is_test.h"
@@ -13,13 +14,18 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
 #include "base/time/time.h"
+#include "components/messages/android/message_enums.h"
 #include "components/permissions/constants.h"
 #include "components/permissions/features.h"
 #include "components/permissions/permission_uma_util.h"
 #include "components/permissions/pref_names.h"
+#include "components/permissions/request_type.h"
 #include "components/pref_registry/pref_registry_syncable.h"
 #include "components/prefs/pref_service.h"
+#include "components/strings/grit/components_strings.h"
+#include "ui/base/l10n/l10n_util.h"
 
 namespace permissions {
 
@@ -43,7 +49,7 @@
 
 std::map<std::string, std::pair<std::string, std::string>>
 GetKeyToValueFilterPairMap(
-    PermissionHatsTriggerHelper::PromptParametersForHaTS prompt_parameters) {
+    PermissionHatsTriggerHelper::PromptParametersForHats prompt_parameters) {
   // configuration key -> {current value for key, configured filter for key}
   return {
       {kPermissionsPromptSurveyPromptDispositionKey,
@@ -89,7 +95,7 @@
 // of misconfiguration (which would lead to very high HaTS QPS), we enforce
 // that at least one valid filter must be configured.
 bool IsValidConfiguration(
-    PermissionHatsTriggerHelper::PromptParametersForHaTS prompt_parameters) {
+    PermissionHatsTriggerHelper::PromptParametersForHats prompt_parameters) {
   auto filter_pair_map = GetKeyToValueFilterPairMap(prompt_parameters);
 
   if (filter_pair_map[kPermissionsPromptSurveyDisplayTimeKey].second.empty()) {
@@ -153,35 +159,19 @@
   }
   return *request_filter_vector;
 }
-
-std::vector<std::pair<std::string, std::string>>
-ComputePermissionPromptTriggerIdPairs(const std::string& trigger_name_base) {
-  std::vector<std::string> permission_trigger_id_vector(
-      base::SplitString(feature_params::kPermissionsPromptSurveyTriggerId.Get(),
-                        ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY));
-  int trigger_index = 0;
-  std::vector<std::pair<std::string, std::string>> pairs;
-  pairs.clear();
-  for (const auto& trigger_id : permission_trigger_id_vector) {
-    pairs.emplace_back(
-        trigger_name_base + base::NumberToString(trigger_index++), trigger_id);
-  }
-  return pairs;
-}
-
 }  // namespace
 
-PermissionHatsTriggerHelper::PromptParametersForHaTS::PromptParametersForHaTS(
+PermissionHatsTriggerHelper::PromptParametersForHats::PromptParametersForHats(
     RequestType request_type,
-    absl::optional<PermissionAction> action,
+    std::optional<PermissionAction> action,
     PermissionPromptDisposition prompt_disposition,
     PermissionPromptDispositionReason prompt_disposition_reason,
     PermissionRequestGestureType gesture_type,
     const std::string& channel,
     const std::string& survey_display_time,
-    absl::optional<base::TimeDelta> prompt_display_duration,
+    std::optional<base::TimeDelta> prompt_display_duration,
     OneTimePermissionPromptsDecidedBucket one_time_prompts_decided_bucket,
-    absl::optional<GURL> gurl)
+    std::optional<GURL> gurl)
     : request_type(request_type),
       action(action),
       prompt_disposition(prompt_disposition),
@@ -193,10 +183,26 @@
       one_time_prompts_decided_bucket(one_time_prompts_decided_bucket),
       url(gurl.has_value() ? gurl->spec() : "") {}
 
-PermissionHatsTriggerHelper::PromptParametersForHaTS::PromptParametersForHaTS(
-    const PromptParametersForHaTS& other) = default;
-PermissionHatsTriggerHelper::PromptParametersForHaTS::
-    ~PromptParametersForHaTS() = default;
+PermissionHatsTriggerHelper::SurveyParametersForHats::SurveyParametersForHats(
+    double trigger_probability,
+    std::optional<std::string> supplied_trigger_id,
+    std::optional<std::u16string> custom_survey_invitation,
+    std::optional<messages::MessageIdentifier> message_identifier)
+    : trigger_probability(trigger_probability),
+      supplied_trigger_id(supplied_trigger_id),
+      custom_survey_invitation(custom_survey_invitation),
+      message_identifier(message_identifier) {}
+
+PermissionHatsTriggerHelper::SurveyParametersForHats::
+    ~SurveyParametersForHats() = default;
+
+PermissionHatsTriggerHelper::SurveyParametersForHats::SurveyParametersForHats(
+    const SurveyParametersForHats& other) = default;
+
+PermissionHatsTriggerHelper::PromptParametersForHats::PromptParametersForHats(
+    const PromptParametersForHats& other) = default;
+PermissionHatsTriggerHelper::PromptParametersForHats::
+    ~PromptParametersForHats() = default;
 
 PermissionHatsTriggerHelper::SurveyProductSpecificData::
     SurveyProductSpecificData(SurveyBitsData survey_bits_data,
@@ -209,7 +215,7 @@
 
 PermissionHatsTriggerHelper::SurveyProductSpecificData
 PermissionHatsTriggerHelper::SurveyProductSpecificData::PopulateFrom(
-    PromptParametersForHaTS prompt_parameters) {
+    PromptParametersForHats prompt_parameters) {
   static const char* const kProductSpecificBitsFields[] = {
       kPermissionsPromptSurveyHadGestureKey};
   static const char* const kProductSpecificStringFields[] = {
@@ -251,15 +257,13 @@
 }
 
 bool PermissionHatsTriggerHelper::ArePromptTriggerCriteriaSatisfied(
-    PromptParametersForHaTS prompt_parameters,
-    const std::string& trigger_name_base) {
-  auto trigger_and_probability = PermissionHatsTriggerHelper::
-      GetPermissionPromptTriggerNameAndProbabilityForRequestType(
-          trigger_name_base, PermissionUmaUtil::GetRequestTypeString(
-                                 prompt_parameters.request_type));
+    PromptParametersForHats prompt_parameters) {
+  std::optional<SurveyParametersForHats> survey_parameters =
+      PermissionHatsTriggerHelper::GetSurveyParametersForRequestType(
+          prompt_parameters.request_type);
 
-  if (!trigger_and_probability.has_value() ||
-      base::RandDouble() >= trigger_and_probability->second) {
+  if (!survey_parameters.has_value() ||
+      base::RandDouble() >= survey_parameters->trigger_probability) {
     return false;
   }
 
@@ -344,57 +348,112 @@
 }
 
 // static
-std::vector<std::pair<std::string, std::string>>&
-PermissionHatsTriggerHelper::GetPermissionPromptTriggerIdPairs(
-    const std::string& trigger_name_base) {
-  static base::NoDestructor<std::vector<std::pair<std::string, std::string>>>
-      trigger_id_pairs([trigger_name_base] {
-        return ComputePermissionPromptTriggerIdPairs(trigger_name_base);
-      }());
-  if (is_test) {
-    CHECK_IS_TEST();
-    *trigger_id_pairs =
-        ComputePermissionPromptTriggerIdPairs(trigger_name_base);
-  }
-  return *trigger_id_pairs;
-}
-
-// static
-absl::optional<std::pair<std::string, double>> PermissionHatsTriggerHelper::
-    GetPermissionPromptTriggerNameAndProbabilityForRequestType(
-        const std::string& trigger_name_base,
-        const std::string& request_type) {
-  auto& trigger_id_pairs = GetPermissionPromptTriggerIdPairs(trigger_name_base);
+std::optional<PermissionHatsTriggerHelper::SurveyParametersForHats>
+PermissionHatsTriggerHelper::GetSurveyParametersForRequestType(
+    permissions::RequestType request_type) {
   auto& probability_vector =
       GetProbabilityVector(feature_params::kProbabilityVector.Get());
 
-  if (trigger_id_pairs.size() == 1 && probability_vector.size() <= 1) {
+  std::vector<std::string> permission_trigger_id_vector(
+      base::SplitString(feature_params::kPermissionsPromptSurveyTriggerId.Get(),
+                        ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY));
+
+  std::vector<std::string> custom_invitation_trigger_id_vector(
+      base::SplitString(
+          feature_params::kPermissionsPromptSurveyCustomInvitationTriggerId
+              .Get(),
+          ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY));
+
+  CHECK(custom_invitation_trigger_id_vector.empty() ||
+        custom_invitation_trigger_id_vector.size() ==
+            permission_trigger_id_vector.size());
+
+  // If custom_invitation_trigger_id_vector is not empty, the custom invitation
+  // experiment is active. In that case, we show custom invitations with the
+  // corresponding separate triggerId with probability 50%, and the generic
+  // invitation with the corresponding separate triggerId in the other 50% of
+  // cases.
+  bool is_custom_invitation_experiment =
+      custom_invitation_trigger_id_vector.size() != 0;
+  bool is_custom_invitation_arm =
+      is_custom_invitation_experiment && base::RandDouble() < 0.5;
+
+  std::optional<messages::MessageIdentifier> message_identifier;
+  std::optional<std::u16string> custom_invitation;
+  if (is_custom_invitation_experiment) {
+    int request_type_message_id = -1;
+    if (request_type == RequestType::kCameraStream) {
+      request_type_message_id = IDS_CAMERA_PERMISSION_NAME_FRAGMENT;
+      message_identifier = is_custom_invitation_arm
+                               ? messages::MessageIdentifier::
+                                     PROMPT_HATS_CAMERA_CUSTOM_INVITATION
+                               : messages::MessageIdentifier::
+                                     PROMPT_HATS_CAMERA_GENERIC_INVITATION;
+    } else if (request_type == RequestType::kGeolocation) {
+      request_type_message_id = IDS_GEOLOCATION_NAME_FRAGMENT;
+      message_identifier = is_custom_invitation_arm
+                               ? messages::MessageIdentifier::
+                                     PROMPT_HATS_LOCATION_CUSTOM_INVITATION
+                               : messages::MessageIdentifier::
+                                     PROMPT_HATS_LOCATION_GENERIC_INVITATION;
+    } else if (request_type == RequestType::kMicStream) {
+      request_type_message_id = IDS_MICROPHONE_PERMISSION_NAME_FRAGMENT;
+      message_identifier = is_custom_invitation_arm
+                               ? messages::MessageIdentifier::
+                                     PROMPT_HATS_MICROPHONE_CUSTOM_INVITATION
+                               : messages::MessageIdentifier::
+                                     PROMPT_HATS_MICROPHONE_GENERIC_INVITATION;
+    }
+    CHECK_NE(request_type_message_id, -1)
+        << "Request type not supported by the custom invitation experiment.";
+    if (is_custom_invitation_arm) {
+      custom_invitation =
+          std::optional<std::u16string>(l10n_util::GetStringFUTF16(
+              IDS_PERMISSION_PROMPT_SURVEY_CUSTOM_INVITATION,
+              l10n_util::GetStringUTF16(request_type_message_id)));
+    }
+  }
+
+  if (permission_trigger_id_vector.size() == 1 &&
+      probability_vector.size() <= 1) {
     // If a value is configured, use it, otherwise set it to 1.
-    return std::make_pair(
-        trigger_id_pairs[0].first,
-        probability_vector.size() == 1 ? probability_vector[0] : 1.0);
-  } else if (trigger_id_pairs.size() != probability_vector.size()) {
+    double probability =
+        probability_vector.size() == 1 ? probability_vector[0] : 1.0;
+    const std::string& supplied_trigger_id =
+        is_custom_invitation_experiment ? custom_invitation_trigger_id_vector[0]
+                                        : permission_trigger_id_vector[0];
+    return PermissionHatsTriggerHelper::SurveyParametersForHats(
+        probability, supplied_trigger_id, custom_invitation);
+  } else if (permission_trigger_id_vector.size() != probability_vector.size()) {
     // Configuration error
-    return absl::nullopt;
+    return std::nullopt;
   } else {
     auto& request_filter_vector = GetRequestFilterVector(
         feature_params::kPermissionsPromptSurveyRequestTypeFilter.Get());
 
-    if (request_filter_vector.size() != trigger_id_pairs.size()) {
+    if (request_filter_vector.size() != permission_trigger_id_vector.size()) {
       // Configuration error
-      return absl::nullopt;
+      return std::nullopt;
     }
 
-    for (unsigned long i = 0; i < trigger_id_pairs.size(); i++) {
-      if (base::EqualsCaseInsensitiveASCII(request_type,
-                                           request_filter_vector[i])) {
-        return std::make_pair(trigger_id_pairs.at(i).first,
-                              probability_vector[i]);
+    for (unsigned long i = 0; i < permission_trigger_id_vector.size(); i++) {
+      if (base::EqualsCaseInsensitiveASCII(
+              permissions::PermissionUmaUtil::GetRequestTypeString(
+                  request_type),
+              request_filter_vector[i])) {
+        double probability = probability_vector[i];
+        const std::string& supplied_trigger_id =
+            is_custom_invitation_experiment
+                ? custom_invitation_trigger_id_vector[i]
+                : permission_trigger_id_vector[i];
+        return PermissionHatsTriggerHelper::SurveyParametersForHats(
+            probability, supplied_trigger_id, custom_invitation,
+            message_identifier);
       }
     }
 
     // No matching filter
-    return absl::nullopt;
+    return std::nullopt;
   }
 }
 
diff --git a/components/permissions/permission_hats_trigger_helper.h b/components/permissions/permission_hats_trigger_helper.h
index 5a40a61..8c3dd4a 100644
--- a/components/permissions/permission_hats_trigger_helper.h
+++ b/components/permissions/permission_hats_trigger_helper.h
@@ -6,9 +6,11 @@
 #define COMPONENTS_PERMISSIONS_PERMISSION_HATS_TRIGGER_HELPER_H_
 
 #include <map>
+#include <optional>
 #include <utility>
 
 #include "components/keyed_service/core/keyed_service.h"
+#include "components/messages/android/message_enums.h"
 #include "components/permissions/permission_util.h"
 #include "constants.h"
 
@@ -44,40 +46,56 @@
     BUCKET_GT20    // >20
   };
 
-  struct PromptParametersForHaTS {
-    PromptParametersForHaTS(
+  struct PromptParametersForHats {
+    PromptParametersForHats(
         permissions::RequestType request_type,
-        absl::optional<permissions::PermissionAction> action,
+        std::optional<permissions::PermissionAction> action,
         permissions::PermissionPromptDisposition prompt_disposition,
         permissions::PermissionPromptDispositionReason
             prompt_disposition_reason,
         permissions::PermissionRequestGestureType gesture_type,
         const std::string& channel,
         const std::string& survey_display_time,
-        absl::optional<base::TimeDelta> prompt_display_duration,
+        std::optional<base::TimeDelta> prompt_display_duration,
         OneTimePermissionPromptsDecidedBucket one_time_prompts_decided_bucket,
-        absl::optional<GURL> gurl);
-    PromptParametersForHaTS(const PromptParametersForHaTS& other);
-    ~PromptParametersForHaTS();
+        std::optional<GURL> gurl);
+    PromptParametersForHats(const PromptParametersForHats& other);
+    ~PromptParametersForHats();
 
     permissions::RequestType request_type;
-    absl::optional<permissions::PermissionAction> action;
+    std::optional<permissions::PermissionAction> action;
     permissions::PermissionPromptDisposition prompt_disposition;
     permissions::PermissionPromptDispositionReason prompt_disposition_reason;
     permissions::PermissionRequestGestureType gesture_type;
     std::string channel;
     std::string survey_display_time;
-    absl::optional<base::TimeDelta> prompt_display_duration;
+    std::optional<base::TimeDelta> prompt_display_duration;
     OneTimePermissionPromptsDecidedBucket one_time_prompts_decided_bucket;
     std::string url;
   };
 
+  struct SurveyParametersForHats {
+    explicit SurveyParametersForHats(
+        double trigger_probability,
+        std::optional<std::string> supplied_trigger_id = std::nullopt,
+        std::optional<std::u16string> custom_survey_invitation = std::nullopt,
+        std::optional<messages::MessageIdentifier> message_identifier =
+            std::nullopt);
+    SurveyParametersForHats(const SurveyParametersForHats& other);
+    ~SurveyParametersForHats();
+
+    double trigger_probability;
+    std::optional<std::string> supplied_trigger_id;
+    std::optional<std::u16string> custom_survey_invitation;
+    std::optional<messages::MessageIdentifier> message_identifier;
+  };
+
   struct SurveyProductSpecificData {
    public:
     ~SurveyProductSpecificData();
 
     static SurveyProductSpecificData PopulateFrom(
-        PromptParametersForHaTS prompt_parameters);
+        PromptParametersForHats prompt_parameters);
 
     const SurveyBitsData survey_bits_data;
     const SurveyStringData survey_string_data;
@@ -95,8 +113,7 @@
   static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
 
   static bool ArePromptTriggerCriteriaSatisfied(
-      PromptParametersForHaTS prompt_parameters,
-      const std::string& trigger_name_base);
+      PromptParametersForHats prompt_parameters);
 
   static OneTimePermissionPromptsDecidedBucket GetOneTimePromptsDecidedBucket(
       PrefService* pref_service);
@@ -112,19 +129,11 @@
   static std::string GetOneTimePromptsDecidedBucketString(
       OneTimePermissionPromptsDecidedBucket bucket);
 
-  // Returns a vector containing pairs of <trigger_name, trigger_id>
-  // The trigger_name is a unique name used by the HaTS service integration, and
-  // the trigger_id is an ID that specifies a survey in the Listnr backend.
-  static std::vector<std::pair<std::string, std::string>>&
-  GetPermissionPromptTriggerIdPairs(const std::string& trigger_name_base);
-
-  // Returns the trigger name and probability corresponding to a specific
+  // Returns the survey parameters corresponding to a specific
   // request type. Returns empty value if there is a configuration error or the
   // passed request type is not configured.
-  static absl::optional<std::pair<std::string, double>>
-  GetPermissionPromptTriggerNameAndProbabilityForRequestType(
-      const std::string& trigger_name_base,
-      const std::string& request_type);
+  static std::optional<SurveyParametersForHats>
+  GetSurveyParametersForRequestType(RequestType request_type);
 
   static void SetIsTest();
 };
diff --git a/components/permissions_strings.grdp b/components/permissions_strings.grdp
index c1384ae..188713d 100644
--- a/components/permissions_strings.grdp
+++ b/components/permissions_strings.grdp
@@ -395,6 +395,9 @@
   <message name ="IDS_PERMISSION_CHROME_NEEDS_PERMISSION" desc="Text informing the user in the background that Chrome needs a permission in order to use it on the site the user is visiting">
       To use your <ph name="permission">$1<ex>camera</ex></ph> on this site, you need to give Chrome access
   </message>
+    <message name="IDS_GEOLOCATION_NAME_FRAGMENT" desc="Text fragment identifying the geolocation permission">
+    location
+  </message>
   <message name="IDS_CAMERA_PERMISSION_NAME_FRAGMENT" desc="Text fragment identifying the camera permission">
     camera
   </message>
@@ -423,4 +426,7 @@
   <message name="IDS_EMBEDDED_PROMPT_CONTINUE_NOT_ALLOWING" desc="Label used on a button that to continue not allowing a permission prompt that the user has previously also not allowed for the site.">
     Continue not allowing
   </message>
+  <message name ="IDS_PERMISSION_PROMPT_SURVEY_CUSTOM_INVITATION" desc="The invitation shown to a user, which allows the user to accept taking a survey.">
+      A website just asked for access to your <ph name="request_type">$1<ex>camera</ex></ph>. Help us improve how websites ask for access by taking this 1-minute survey.
+  </message>
 </grit-part>
diff --git a/components/permissions_strings_grdp/IDS_GEOLOCATION_NAME_FRAGMENT.png.sha1 b/components/permissions_strings_grdp/IDS_GEOLOCATION_NAME_FRAGMENT.png.sha1
new file mode 100644
index 0000000..aef3688c
--- /dev/null
+++ b/components/permissions_strings_grdp/IDS_GEOLOCATION_NAME_FRAGMENT.png.sha1
@@ -0,0 +1 @@
+9b1280341c78d211a21edefe38db23c4ab0e8a6a
\ No newline at end of file
diff --git a/components/permissions_strings_grdp/IDS_PERMISSION_PROMPT_SURVEY_CUSTOM_INVITATION.png.sha1 b/components/permissions_strings_grdp/IDS_PERMISSION_PROMPT_SURVEY_CUSTOM_INVITATION.png.sha1
new file mode 100644
index 0000000..2f619250
--- /dev/null
+++ b/components/permissions_strings_grdp/IDS_PERMISSION_PROMPT_SURVEY_CUSTOM_INVITATION.png.sha1
@@ -0,0 +1 @@
+ef54bfed7e8ebe4ac43ca4d734f07cf540eab1a9
\ No newline at end of file
diff --git a/components/renderer_context_menu/context_menu_delegate.cc b/components/renderer_context_menu/context_menu_delegate.cc
index f59bf8c..03a1d50e 100644
--- a/components/renderer_context_menu/context_menu_delegate.cc
+++ b/components/renderer_context_menu/context_menu_delegate.cc
@@ -17,23 +17,39 @@
  public:
   explicit ContextMenuDelegateUserData(ContextMenuDelegate* menu_delegate)
       : menu_delegate_(menu_delegate) {}
-  ~ContextMenuDelegateUserData() override {}
+  ~ContextMenuDelegateUserData() override {
+    if (menu_delegate_) {
+      menu_delegate_->ClearWebContents();
+    }
+  }
   ContextMenuDelegate* menu_delegate() { return menu_delegate_; }
 
+  void ClearDelegate() { menu_delegate_ = nullptr; }
+
  private:
-  raw_ptr<ContextMenuDelegate, AcrossTasksDanglingUntriaged>
-      menu_delegate_;  // not owned by us.
+  raw_ptr<ContextMenuDelegate> menu_delegate_;  // not owned by us.
 };
 
 }  // namespace
 
 ContextMenuDelegate::ContextMenuDelegate(content::WebContents* web_contents) {
+  web_contents_ = web_contents;
   web_contents->SetUserData(
       &kMenuDelegateUserDataKey,
       std::make_unique<ContextMenuDelegateUserData>(this));
 }
 
 ContextMenuDelegate::~ContextMenuDelegate() {
+  if (web_contents_) {
+    ContextMenuDelegateUserData* user_data =
+        static_cast<ContextMenuDelegateUserData*>(
+            web_contents_->GetUserData(&kMenuDelegateUserDataKey));
+    user_data->ClearDelegate();
+  }
+}
+
+void ContextMenuDelegate::ClearWebContents() {
+  web_contents_ = nullptr;
 }
 
 // static
diff --git a/components/renderer_context_menu/context_menu_delegate.h b/components/renderer_context_menu/context_menu_delegate.h
index 64b14264..042428f 100644
--- a/components/renderer_context_menu/context_menu_delegate.h
+++ b/components/renderer_context_menu/context_menu_delegate.h
@@ -6,6 +6,7 @@
 #define COMPONENTS_RENDERER_CONTEXT_MENU_CONTEXT_MENU_DELEGATE_H_
 
 #include <memory>
+#include "base/memory/raw_ptr.h"
 
 class RenderViewContextMenuBase;
 
@@ -23,6 +24,8 @@
   ContextMenuDelegate(const ContextMenuDelegate&) = delete;
   ContextMenuDelegate& operator=(const ContextMenuDelegate&) = delete;
 
+  void ClearWebContents();
+
   virtual ~ContextMenuDelegate();
 
   static ContextMenuDelegate* FromWebContents(
@@ -41,6 +44,9 @@
 
   // Displays the context menu.
   virtual void ShowMenu(std::unique_ptr<RenderViewContextMenuBase> menu) = 0;
+
+ private:
+  raw_ptr<content::WebContents> web_contents_ = nullptr;
 };
 
 #endif  // COMPONENTS_RENDERER_CONTEXT_MENU_CONTEXT_MENU_DELEGATE_H_
diff --git a/components/safe_browsing/core/common/safe_browsing_prefs.cc b/components/safe_browsing/core/common/safe_browsing_prefs.cc
index 8464192..10e255a1 100644
--- a/components/safe_browsing/core/common/safe_browsing_prefs.cc
+++ b/components/safe_browsing/core/common/safe_browsing_prefs.cc
@@ -67,82 +67,6 @@
 
 }  // namespace
 
-namespace prefs {
-const char kSafeBrowsingCsdPingTimestamps[] =
-    "safebrowsing.csd_ping_timestamps";
-const char kSafeBrowsingDeepScanningEnabled[] =
-    "safebrowsing.deep_scanning_enabled";
-const char kSafeBrowsingEnabled[] = "safebrowsing.enabled";
-const char kSafeBrowsingEnhanced[] = "safebrowsing.enhanced";
-const char kSafeBrowsingEnterpriseRealTimeUrlCheckMode[] =
-    "safebrowsing.enterprise_real_time_url_check_mode";
-const char kSafeBrowsingEnterpriseRealTimeUrlCheckScope[] =
-    "safebrowsing.enterprise_real_time_url_check_scope";
-const char kSafeBrowsingEsbProtegoPingWithTokenLastLogTime[] =
-    "safebrowsing.esb_protego_ping_with_token_last_log_time";
-const char kSafeBrowsingEsbProtegoPingWithoutTokenLastLogTime[] =
-    "safebrowsing.esb_protego_ping_without_token_last_log_time";
-const char kSafeBrowsingExtendedReportingOptInAllowed[] =
-    "safebrowsing.extended_reporting_opt_in_allowed";
-const char kSafeBrowsingIncidentsSent[] = "safebrowsing.incidents_sent";
-const char kSafeBrowsingProceedAnywayDisabled[] =
-    "safebrowsing.proceed_anyway_disabled";
-const char kSafeBrowsingSawInterstitialScoutReporting[] =
-    "safebrowsing.saw_interstitial_sber2";
-const char kSafeBrowsingScoutReportingEnabled[] =
-    "safebrowsing.scout_reporting_enabled";
-const char kSafeBrowsingSurveysEnabled[] = "safebrowsing.surveys_enabled";
-const char kSafeBrowsingTriggerEventTimestamps[] =
-    "safebrowsing.trigger_event_timestamps";
-const char kSafeBrowsingUnhandledGaiaPasswordReuses[] =
-    "safebrowsing.unhandled_sync_password_reuses";
-const char kSafeBrowsingNextPasswordCaptureEventLogTime[] =
-    "safebrowsing.next_password_capture_event_log_time";
-const char kSafeBrowsingAllowlistDomains[] =
-    "safebrowsing.safe_browsing_whitelist_domains";
-const char kPasswordProtectionChangePasswordURL[] =
-    "safebrowsing.password_protection_change_password_url";
-const char kPasswordProtectionLoginURLs[] =
-    "safebrowsing.password_protection_login_urls";
-const char kPasswordProtectionWarningTrigger[] =
-    "safebrowsing.password_protection_warning_trigger";
-const char kAdvancedProtectionLastRefreshInUs[] =
-    "safebrowsing.advanced_protection_last_refresh";
-const char kAdvancedProtectionAllowed[] =
-    "safebrowsing.advanced_protection_allowed";
-const char kSafeBrowsingMetricsLastLogTime[] =
-    "safebrowsing.metrics_last_log_time";
-const char kSafeBrowsingEventTimestamps[] = "safebrowsing.event_timestamps";
-const char kSafeBrowsingHashRealTimeOhttpExpirationTime[] =
-    "safebrowsing.hash_real_time_ohttp_expiration_time";
-const char kSafeBrowsingHashRealTimeOhttpKey[] =
-    "safebrowsing.hash_real_time_ohttp_key";
-const char kAccountTailoredSecurityUpdateTimestamp[] =
-    "safebrowsing.aesb_update_time_windows_epoch_micros";
-const char kTailoredSecurityNextSyncFlowTimestamp[] =
-    "safebrowsing.aesb_next_sync_flow_timestamp";
-const char kAccountTailoredSecurityShownNotification[] =
-    "safebrowsing.aesb_shown_notification";
-const char kTailoredSecuritySyncFlowLastRunTime[] =
-    "safebrowsing.aesb_sync_flow_start_timestamp";
-const char kTailoredSecuritySyncFlowLastUserInteractionState[] =
-    "safebrowsing.aesb_sync_flow_last_user_interaction_state";
-const char kTailoredSecuritySyncFlowRetryState[] =
-    "safebrowsing.aesb_sync_flow_retry_state";
-const char kTailoredSecuritySyncFlowObservedOutcomeUnsetTimestamp[] =
-    "safebrowsing.aesb_sync_flow_observed_outcome_unset_timestamp";
-const char kEnhancedProtectionEnabledViaTailoredSecurity[] =
-    "safebrowsing.esb_enabled_via_tailored_security";
-const char kExtensionTelemetryLastUploadTime[] =
-    "safebrowsing.extension_telemetry_last_upload_time";
-const char kExtensionTelemetryConfig[] =
-    "safebrowsing.extension_telemetry_configuration";
-const char kExtensionTelemetryFileData[] =
-    "safebrowsing.extension_telemetry_file_data";
-const char kHashPrefixRealTimeChecksAllowedByPolicy[] =
-    "safebrowsing.hash_prefix_real_time_checks_allowed_by_policy";
-}  // namespace prefs
-
 namespace safe_browsing {
 
 SafeBrowsingState GetSafeBrowsingState(const PrefService& prefs) {
diff --git a/components/safe_browsing/core/common/safe_browsing_prefs.h b/components/safe_browsing/core/common/safe_browsing_prefs.h
index f5237463..e2c97c8 100644
--- a/components/safe_browsing/core/common/safe_browsing_prefs.h
+++ b/components/safe_browsing/core/common/safe_browsing_prefs.h
@@ -24,174 +24,211 @@
 
 namespace prefs {
 // A list of times at which CSD pings were sent.
-extern const char kSafeBrowsingCsdPingTimestamps[];
+inline constexpr char kSafeBrowsingCsdPingTimestamps[] =
+    "safebrowsing.csd_ping_timestamps";
 
 // Boolean that is true when deep scanning is allowed.
-extern const char kSafeBrowsingDeepScanningEnabled[];
+inline constexpr char kSafeBrowsingDeepScanningEnabled[] =
+    "safebrowsing.deep_scanning_enabled";
 
 // Boolean that is true when SafeBrowsing is enabled.
-extern const char kSafeBrowsingEnabled[];
+inline constexpr char kSafeBrowsingEnabled[] = "safebrowsing.enabled";
 
 // Boolean that is true when Safe Browsing Enhanced Protection is enabled.
-extern const char kSafeBrowsingEnhanced[];
+inline constexpr char kSafeBrowsingEnhanced[] = "safebrowsing.enhanced";
 
 // Integer indicating the state of real time URL check. This is managed
 // by enterprise policy and has no effect on users who are not managed by
 // enterprise policy.
-extern const char kSafeBrowsingEnterpriseRealTimeUrlCheckMode[];
+inline constexpr char kSafeBrowsingEnterpriseRealTimeUrlCheckMode[] =
+    "safebrowsing.enterprise_real_time_url_check_mode";
 
 // Integer indicating the scope at which the
 // kSafeBrowsingEnterpriseRealTimeUrlCheckMode pref is set.
-extern const char kSafeBrowsingEnterpriseRealTimeUrlCheckScope[];
+inline constexpr char kSafeBrowsingEnterpriseRealTimeUrlCheckScope[] =
+    "safebrowsing.enterprise_real_time_url_check_scope";
 
 // Timestamp indicating the last time a protego ping with a token was sent.
 // This is only set if the user has enhanced protection enabled and is signed
 // in with their account.
-extern const char kSafeBrowsingEsbProtegoPingWithTokenLastLogTime[];
+inline constexpr char kSafeBrowsingEsbProtegoPingWithTokenLastLogTime[] =
+    "safebrowsing.esb_protego_ping_with_token_last_log_time";
 
 // Timestamp indicating the last time a protego ping without a token was sent.
 // This is only set if the user has enhanced protection enabled and is not
 // signed in with their account.
-extern const char kSafeBrowsingEsbProtegoPingWithoutTokenLastLogTime[];
+inline constexpr char kSafeBrowsingEsbProtegoPingWithoutTokenLastLogTime[] =
+    "safebrowsing.esb_protego_ping_without_token_last_log_time";
 
 // Boolean that tells us whether users are given the option to opt in to
 // Safe Browsing extended reporting. This is exposed as a preference that
 // can be overridden by enterprise policy.
-extern const char kSafeBrowsingExtendedReportingOptInAllowed[];
+inline constexpr char kSafeBrowsingExtendedReportingOptInAllowed[] =
+    "safebrowsing.extended_reporting_opt_in_allowed";
 
 // A dictionary mapping incident types to a dict of incident key:digest pairs.
 // The key is a string: a filename or pref name. Digests are 4 bytes. This pref
 // is only set/updated if Chrome (Windows only) notices certain security
 // incidents, e.g. the user downloaded binaries with invalid signatures.
-extern const char kSafeBrowsingIncidentsSent[];
+inline constexpr char kSafeBrowsingIncidentsSent[] =
+    "safebrowsing.incidents_sent";
 
 // Boolean that is true when the SafeBrowsing interstitial should not allow
 // users to proceed anyway.
-extern const char kSafeBrowsingProceedAnywayDisabled[];
+inline constexpr char kSafeBrowsingProceedAnywayDisabled[] =
+    "safebrowsing.proceed_anyway_disabled";
 
 // Boolean indicating whether the user has ever seen a security interstitial.
-extern const char kSafeBrowsingSawInterstitialScoutReporting[];
+inline constexpr char kSafeBrowsingSawInterstitialScoutReporting[] =
+    "safebrowsing.saw_interstitial_sber2";
 
 // Boolean indicating whether Safe Browsing Scout reporting is enabled, which
 // collects data for malware detection.
-extern const char kSafeBrowsingScoutReportingEnabled[];
+inline constexpr char kSafeBrowsingScoutReportingEnabled[] =
+    "safebrowsing.scout_reporting_enabled";
 
 // Dictionary containing safe browsing triggers and the list of times they have
 // fired recently. The keys are TriggerTypes (4-byte ints) and the values are
 // lists of doubles.
-extern const char kSafeBrowsingTriggerEventTimestamps[];
+inline constexpr char kSafeBrowsingTriggerEventTimestamps[] =
+    "safebrowsing.trigger_event_timestamps";
 
 // Dictionary that records the origin and navigation ID pairs of unhandled gaia
 // password reuses. The keys are origin strings and the ID values are 8-byte
 // ints. Only set/update if a Chrome user reuses their Gaia password on a
 // phishing site.
-extern const char kSafeBrowsingUnhandledGaiaPasswordReuses[];
+inline constexpr char kSafeBrowsingUnhandledGaiaPasswordReuses[] =
+    "safebrowsing.unhandled_sync_password_reuses";
 
 // Integer timestamp of next time the PasswordCaptured event should be logged.
-extern const char kSafeBrowsingNextPasswordCaptureEventLogTime[];
+inline constexpr char kSafeBrowsingNextPasswordCaptureEventLogTime[] =
+    "safebrowsing.next_password_capture_event_log_time";
 
 // List of domains where Safe Browsing should trust. That means Safe Browsing
 // won't check for malware/phishing/Uws on resources on these domains, or
 // trigger warnings. Used for enterprise only.
-extern const char kSafeBrowsingAllowlistDomains[];
+inline constexpr char kSafeBrowsingAllowlistDomains[] =
+    "safebrowsing.safe_browsing_whitelist_domains";
 
 // String indicating the URL where password protection service should send user
 // to change their password if they've been phished. Password protection service
 // also captures new password on this page in a change password event. Used for
 // enterprise only.
-extern const char kPasswordProtectionChangePasswordURL[];
+inline constexpr char kPasswordProtectionChangePasswordURL[] =
+    "safebrowsing.password_protection_change_password_url";
 
 // List of string indicating the URL(s) users use to log in. Password protection
 // service will capture passwords on these URLs.
 // This is managed by enterprise policy and has no effect on users who are not
 // managed by enterprise policy.
-extern const char kPasswordProtectionLoginURLs[];
+inline constexpr char kPasswordProtectionLoginURLs[] =
+    "safebrowsing.password_protection_login_urls";
 
 // Integer indicating the password protection warning trigger. This is managed
 // by enterprise policy and has no effect on users who are not managed by
 // enterprise policy.
-extern const char kPasswordProtectionWarningTrigger[];
+inline constexpr char kPasswordProtectionWarningTrigger[] =
+    "safebrowsing.password_protection_warning_trigger";
 
 // Last time Chrome refreshes advanced protection status for sign-in users (in
 // microseconds);
-extern const char kAdvancedProtectionLastRefreshInUs[];
+inline constexpr char kAdvancedProtectionLastRefreshInUs[] =
+    "safebrowsing.advanced_protection_last_refresh";
 
 // Boolean that indicates if Chrome is allowed to provide extra
 // features to users enrolled in the Advanced Protection Program.
-extern const char kAdvancedProtectionAllowed[];
+inline constexpr char kAdvancedProtectionAllowed[] =
+    "safebrowsing.advanced_protection_allowed";
 
 // Integer epoch timestamp in seconds. Indicates the last logging time of Safe
 // Browsing metrics.
-extern const char kSafeBrowsingMetricsLastLogTime[];
+inline constexpr char kSafeBrowsingMetricsLastLogTime[] =
+    "safebrowsing.metrics_last_log_time";
 
 // A dictionary of Safe Browsing events and their corresponding timestamps.
 // Used for logging metrics. Structure: go/sb-event-ts-pref-struct.
-extern const char kSafeBrowsingEventTimestamps[];
+inline constexpr char kSafeBrowsingEventTimestamps[] =
+    "safebrowsing.event_timestamps";
 
 // A timestamp indicating the expiration time of the Oblivious HTTP key used by
 // hash prefix real time URL check.
-extern const char kSafeBrowsingHashRealTimeOhttpExpirationTime[];
+inline constexpr char kSafeBrowsingHashRealTimeOhttpExpirationTime[] =
+    "safebrowsing.hash_real_time_ohttp_expiration_time";
 
 // The Oblivious HTTP key used by hash prefix real time URL check.
-extern const char kSafeBrowsingHashRealTimeOhttpKey[];
+inline constexpr char kSafeBrowsingHashRealTimeOhttpKey[] =
+    "safebrowsing.hash_real_time_ohttp_key";
 
 // Boolean indicating whether users can receive surveys.
-extern const char kSafeBrowsingSurveysEnabled[];
+inline constexpr char kSafeBrowsingSurveysEnabled[] =
+    "safebrowsing.surveys_enabled";
 
 // A timestamp indicating the last time the account tailored security boolean
 // was updated.
-extern const char kAccountTailoredSecurityUpdateTimestamp[];
+inline constexpr char kAccountTailoredSecurityUpdateTimestamp[] =
+    "safebrowsing.aesb_update_time_windows_epoch_micros";
 
 // Timestamp indicating when the next time the sync flow retry can happen is.
 // This value is managed by the ChromeTailoredSecurityService.
-extern const char kTailoredSecurityNextSyncFlowTimestamp[];
+inline constexpr char kTailoredSecurityNextSyncFlowTimestamp[] =
+    "safebrowsing.aesb_next_sync_flow_timestamp";
 
 // Timestamp indicating the last time the tailored security sync flow ran.
-extern const char kTailoredSecuritySyncFlowLastRunTime[];
+inline constexpr char kTailoredSecuritySyncFlowLastRunTime[] =
+    "safebrowsing.aesb_sync_flow_start_timestamp";
 
 // Integer that maps to TailoredSecurityUserInteractionState. Indicates the
 // last known state of the tailored security sync flow.
 // TODO(crbug.com/1469133): remove this preference value.
-extern const char kTailoredSecuritySyncFlowLastUserInteractionState[];
+inline constexpr char kTailoredSecuritySyncFlowLastUserInteractionState[] =
+    "safebrowsing.aesb_sync_flow_last_user_interaction_state";
 
 // Integer that maps to TailoredSecurityRetryState. Indicates the last
 // known state of the tailored security sync flow retry mechanism.
-extern const char kTailoredSecuritySyncFlowRetryState[];
+inline constexpr char kTailoredSecuritySyncFlowRetryState[] =
+    "safebrowsing.aesb_sync_flow_retry_state";
 
 // Timestamp indicating when the last user interaction state was observed as
 // having the value of `UNSET`. It is possible that this value will never be
 // set. This will only be set for syncing users where the retry detection logic
 // ran and no outcome was set -- indicating that tailored security with retry
 // capabilities had never run.
-extern const char kTailoredSecuritySyncFlowObservedOutcomeUnsetTimestamp[];
+inline constexpr char kTailoredSecuritySyncFlowObservedOutcomeUnsetTimestamp[] =
+    "safebrowsing.aesb_sync_flow_observed_outcome_unset_timestamp";
 
 // Whether the user was shown the notification that they may want to enable
 // Enhanced Safe Browsing due to their account tailored security state.
 // This value is only relevant to the tailored security flow for non-syncing
 // users.
-extern const char kAccountTailoredSecurityShownNotification[];
+inline constexpr char kAccountTailoredSecurityShownNotification[] =
+    "safebrowsing.aesb_shown_notification";
 
 // A boolean indicating if Enhanced Protection was enabled in sync with
 // account tailored security.
-extern const char kEnhancedProtectionEnabledViaTailoredSecurity[];
+inline constexpr char kEnhancedProtectionEnabledViaTailoredSecurity[] =
+    "safebrowsing.esb_enabled_via_tailored_security";
 
 // The last time the Extension Telemetry Service successfully
 // uploaded its data.
-extern const char kExtensionTelemetryLastUploadTime[];
+inline constexpr char kExtensionTelemetryLastUploadTime[] =
+    "safebrowsing.extension_telemetry_last_upload_time";
 
 // The saved copy of the current configuration that will be used by
 // the Extension Telemetry Service.
-extern const char kExtensionTelemetryConfig[];
+inline constexpr char kExtensionTelemetryConfig[] =
+    "safebrowsing.extension_telemetry_configuration";
 
 // A dictionary of extension ids and their file data from the
 // Telemetry Service's file processor.
-extern const char kExtensionTelemetryFileData[];
+inline constexpr char kExtensionTelemetryFileData[] =
+    "safebrowsing.extension_telemetry_file_data";
 
 // A boolean indicating if hash-prefix real-time lookups are allowed by policy.
 // If false, the lookups will instead be hash-prefix database lookups. If true,
 // there is no such override; the hash-prefix real-time lookups might still not
 // occur for unrelated reasons.
-extern const char kHashPrefixRealTimeChecksAllowedByPolicy[];
+inline constexpr char kHashPrefixRealTimeChecksAllowedByPolicy[] =
+    "safebrowsing.hash_prefix_real_time_checks_allowed_by_policy";
 
 }  // namespace prefs
 
diff --git a/components/search_engines/BUILD.gn b/components/search_engines/BUILD.gn
index 7f6ff754..9bf2aac 100644
--- a/components/search_engines/BUILD.gn
+++ b/components/search_engines/BUILD.gn
@@ -235,7 +235,10 @@
 
   if (is_android) {
     sources += [ "android/template_url_service_android_unittest.cc" ]
-    # deps += [ "//components/search_engines/android:jni_headers" ]
+    deps += [
+      "//components/search_engines/android:jni_headers",
+      "//components/search_engines/android:test_utils_jni_headers",
+    ]
   }
 }
 
diff --git a/components/search_engines/android/BUILD.gn b/components/search_engines/android/BUILD.gn
index 1850cad..98f7fa50 100644
--- a/components/search_engines/android/BUILD.gn
+++ b/components/search_engines/android/BUILD.gn
@@ -47,6 +47,7 @@
     "//components/search_engines:search_engine_enums_java",
   ]
   sources = [
+    "java/src/org/chromium/components/search_engines/SearchEngineChoiceService.java",
     "java/src/org/chromium/components/search_engines/TemplateUrl.java",
     "java/src/org/chromium/components/search_engines/TemplateUrlService.java",
   ]
@@ -54,19 +55,30 @@
 
 generate_jni("jni_headers") {
   sources = [
+    "java/src/org/chromium/components/search_engines/SearchEngineChoiceService.java",
     "java/src/org/chromium/components/search_engines/TemplateUrl.java",
     "java/src/org/chromium/components/search_engines/TemplateUrlService.java",
   ]
 }
 
+generate_jni("test_utils_jni_headers") {
+  testonly = true
+  sources = [ "java/src/org/chromium/components/search_engines/test/util/SearchEngineChoiceServiceTestUtil.java" ]
+}
+
 android_library("test_utils_java") {
   testonly = true
   sources = [
     "java/src/org/chromium/components/search_engines/FakeTemplateUrl.java",
     "java/src/org/chromium/components/search_engines/TemplateUrlTestHelpers.java",
+    "java/src/org/chromium/components/search_engines/test/util/SearchEngineChoiceServiceTestUtil.java",
   ]
   deps = [
+    ":delegate_java",
     ":java",
+    "//base:base_java",
+    "//base:base_java_test_support",
+    "//third_party/jni_zero:jni_zero_java",
     "//third_party/mockito:mockito_java",
   ]
 }
diff --git a/components/search_engines/android/java/src/org/chromium/components/search_engines/SearchEngineChoiceService.java b/components/search_engines/android/java/src/org/chromium/components/search_engines/SearchEngineChoiceService.java
new file mode 100644
index 0000000..63c55027
--- /dev/null
+++ b/components/search_engines/android/java/src/org/chromium/components/search_engines/SearchEngineChoiceService.java
@@ -0,0 +1,98 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+package org.chromium.components.search_engines;
+
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+
+import org.jni_zero.CalledByNative;
+import org.jni_zero.NativeMethods;
+
+import org.chromium.base.ContextUtils;
+import org.chromium.base.ThreadUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * Java counterpart of the native `SearchEngineChoiceService`. This singleton is responsible for
+ * getting the device country string from {@link SearchEngineCountryDelegate} and propagating it to
+ * C++ instances of `SearchEngineChoiceService`. The object is a singleton rather than being
+ * profile-scoped as the device country obtained from `SearchEngineCountryDelegate` is global (it
+ * also allows `SearchEngineChoiceService` instance to be created before the native is initialized).
+ */
+public class SearchEngineChoiceService {
+    private static SearchEngineChoiceService sInstance;
+
+    private final SearchEngineCountryDelegate mDelegate;
+    private final List<Long> mPtrToNativeCallbacks = new ArrayList<>();
+    // To understand whether we already got a reply from `SearchEngineCountryDelegate`, we need to
+    // differentiate between "null result" and "no result yet", thus the optional.
+    private @Nullable Optional<String> mPlayCountryRequestResult;
+
+    /** Returns the instance of the singleton. Creates the instance if needed. */
+    public static SearchEngineChoiceService getInstance() {
+        ThreadUtils.checkUiThread();
+        if (sInstance == null) {
+            sInstance =
+                    new SearchEngineChoiceService(
+                            new SearchEngineCountryDelegateImpl(
+                                    ContextUtils.getApplicationContext()));
+        }
+        return sInstance;
+    }
+
+    /** Overrides the instance of the singleton for tests. */
+    public static void setInstanceForTests(SearchEngineChoiceService instance) {
+        ThreadUtils.checkUiThread();
+        sInstance = instance;
+    }
+
+    @VisibleForTesting
+    public SearchEngineChoiceService(SearchEngineCountryDelegate delegate) {
+        ThreadUtils.checkUiThread();
+        mDelegate = delegate;
+
+        mDelegate
+                .getDeviceCountry()
+                .then(
+                        (deviceCountry) -> {
+                            ThreadUtils.checkUiThread();
+                            mPlayCountryRequestResult = Optional.ofNullable(deviceCountry);
+                            for (long ptrToNativeCallback : mPtrToNativeCallbacks) {
+                                // `mPtrToNativeCallbacks` can be non-empty only after the native is
+                                // loaded, so it is safe to call JNI here.
+                                SearchEngineChoiceServiceJni.get()
+                                        .processCountryFromPlayApi(
+                                                ptrToNativeCallback, deviceCountry);
+                            }
+                            mPtrToNativeCallbacks.clear();
+                        });
+    }
+
+    @CalledByNative
+    private static void requestCountryFromPlayApi(long ptrToNativeCallback) {
+        ThreadUtils.checkUiThread();
+        getInstance().requestCountryFromPlayApiInternal(ptrToNativeCallback);
+    }
+
+    private void requestCountryFromPlayApiInternal(long ptrToNativeCallback) {
+        if (mPlayCountryRequestResult != null) {
+            // The result is ready - call native so it can save the result in prefs.
+            SearchEngineChoiceServiceJni.get()
+                    .processCountryFromPlayApi(
+                            ptrToNativeCallback, mPlayCountryRequestResult.orElse(null));
+            return;
+        }
+        // When `SearchEngineCountryDelegate` replies with the result - the result will be reported
+        // to native using the saved callback.
+        mPtrToNativeCallbacks.add(ptrToNativeCallback);
+    }
+
+    @NativeMethods
+    public interface Natives {
+        void processCountryFromPlayApi(long ptrToNativeCallback, @Nullable String deviceCountry);
+    }
+}
diff --git a/components/search_engines/android/java/src/org/chromium/components/search_engines/test/util/SearchEngineChoiceServiceTestUtil.java b/components/search_engines/android/java/src/org/chromium/components/search_engines/test/util/SearchEngineChoiceServiceTestUtil.java
new file mode 100644
index 0000000..72493ef
--- /dev/null
+++ b/components/search_engines/android/java/src/org/chromium/components/search_engines/test/util/SearchEngineChoiceServiceTestUtil.java
@@ -0,0 +1,79 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.components.search_engines.test.util;
+
+import static org.mockito.Mockito.doReturn;
+
+import android.os.Handler;
+import android.os.Looper;
+
+import org.jni_zero.CalledByNative;
+import org.mockito.Mockito;
+
+import org.chromium.base.Promise;
+import org.chromium.base.test.util.LooperUtils;
+import org.chromium.components.search_engines.SearchEngineChoiceService;
+import org.chromium.components.search_engines.SearchEngineCountryDelegate;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+final class SearchEngineChoiceServiceTestUtil {
+    private final SearchEngineCountryDelegate mMockDelegate;
+    private final Promise<String> mDeviceCountry = new Promise<>();
+
+    /** Stubs {@link SearchEngineChoiceService} for native tests. */
+    @CalledByNative
+    public SearchEngineChoiceServiceTestUtil() {
+        mMockDelegate = Mockito.mock(SearchEngineCountryDelegate.class);
+        doReturn(mDeviceCountry).when(mMockDelegate).getDeviceCountry();
+        SearchEngineChoiceService.setInstanceForTests(new SearchEngineChoiceService(mMockDelegate));
+    }
+
+    /** Restores the global state after the test completes. */
+    @CalledByNative
+    public void destroy() {
+        SearchEngineChoiceService.setInstanceForTests(null);
+    }
+
+    /**
+     * Fulfills the promise returned by `SearchEngineCountryDelegate` to simulate a response from
+     * the Play API.
+     *
+     * @param deviceCountry the result of the device country request.
+     */
+    @CalledByNative
+    public void returnDeviceCountry(String deviceCountry) {
+        mDeviceCountry.fulfill(deviceCountry);
+        // `Promise` posts callback tasks on Android Looper which is not integrated with native
+        // RunLoop in NativeTest. Run these tasks synchronously now.
+        // TODO(crbug.com/1135593): remove this hack once Promise uses PostTask.
+        runLooperTasks();
+    }
+
+    /**
+     * Runs all tasks that are currently posted on the {@link Looper}'s message queue on the current
+     * thread.
+     */
+    private static void runLooperTasks() {
+        AtomicBoolean called = new AtomicBoolean(false);
+        new Handler(Looper.myLooper())
+                .post(
+                        () -> {
+                            called.set(true);
+                        });
+
+        do {
+            try {
+                LooperUtils.runSingleNestedLooperTask();
+            } catch (IllegalArgumentException
+                    | IllegalAccessException
+                    | SecurityException
+                    | InvocationTargetException e) {
+                throw new RuntimeException(e);
+            }
+        } while (!called.get());
+    }
+}
diff --git a/components/search_engines/default_search_manager_unittest.cc b/components/search_engines/default_search_manager_unittest.cc
index eeb4fdd..43cea00b54 100644
--- a/components/search_engines/default_search_manager_unittest.cc
+++ b/components/search_engines/default_search_manager_unittest.cc
@@ -18,6 +18,7 @@
 #include "components/pref_registry/pref_registry_syncable.h"
 #include "components/search_engines/search_engine_choice/search_engine_choice_service.h"
 #include "components/search_engines/search_engines_pref_names.h"
+#include "components/search_engines/search_engines_switches.h"
 #include "components/search_engines/search_engines_test_util.h"
 #include "components/search_engines/template_url_data.h"
 #include "components/search_engines/template_url_data_util.h"
@@ -102,6 +103,10 @@
             *pref_service_);
     DefaultSearchManager::RegisterProfilePrefs(pref_service_->registry());
     TemplateURLPrepopulateData::RegisterProfilePrefs(pref_service_->registry());
+
+    // Override the country checks to simulate being in the US.
+    base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
+        switches::kSearchEngineChoiceCountry, "US");
   }
 
   sync_preferences::TestingPrefServiceSyncable* pref_service() {
diff --git a/components/search_engines/keyword_table.cc b/components/search_engines/keyword_table.cc
index 5e616ec..a33ad02 100644
--- a/components/search_engines/keyword_table.cc
+++ b/components/search_engines/keyword_table.cc
@@ -37,7 +37,9 @@
 namespace {
 
 // Keys used in the meta table.
-const char kBuiltinKeywordVersion[] = "Builtin Keyword Version";
+const char kBuiltinKeywordDataVersion[] = "Builtin Keyword Version";
+const char kBuiltinKeywordMilestone[] = "Builtin Keyword Milestone";
+const char kBuiltinKeywordCountry[] = "Builtin Keyword Country";
 const char kStarterPackKeywordVersion[] = "Starter Pack Keyword Version";
 
 const std::string ColumnsForVersion(int version, bool concatenated) {
@@ -326,13 +328,34 @@
   return value;
 }
 
-bool KeywordTable::SetBuiltinKeywordVersion(int version) {
-  return meta_table_->SetValue(kBuiltinKeywordVersion, version);
+bool KeywordTable::SetBuiltinKeywordDataVersion(int version) {
+  return meta_table_->SetValue(kBuiltinKeywordDataVersion, version);
 }
 
-int KeywordTable::GetBuiltinKeywordVersion() {
+int KeywordTable::GetBuiltinKeywordDataVersion() {
   int version = 0;
-  return meta_table_->GetValue(kBuiltinKeywordVersion, &version) ? version : 0;
+  return meta_table_->GetValue(kBuiltinKeywordDataVersion, &version) ? version
+                                                                     : 0;
+}
+
+bool KeywordTable::SetBuiltinKeywordMilestone(int milestone) {
+  return meta_table_->SetValue(kBuiltinKeywordMilestone, milestone);
+}
+
+int KeywordTable::GetBuiltinKeywordMilestone() {
+  int milestone = 0;
+  return meta_table_->GetValue(kBuiltinKeywordMilestone, &milestone) ? milestone
+                                                                     : 0;
+}
+
+bool KeywordTable::SetBuiltinKeywordCountry(int country_id) {
+  return meta_table_->SetValue(kBuiltinKeywordCountry, country_id);
+}
+
+int KeywordTable::GetBuiltinKeywordCountry() {
+  int country_id = 0;
+  return meta_table_->GetValue(kBuiltinKeywordCountry, &country_id) ? country_id
+                                                                    : 0;
 }
 
 bool KeywordTable::SetStarterPackKeywordVersion(int version) {
diff --git a/components/search_engines/keyword_table.h b/components/search_engines/keyword_table.h
index 76549d7..d231fb9e 100644
--- a/components/search_engines/keyword_table.h
+++ b/components/search_engines/keyword_table.h
@@ -128,9 +128,20 @@
   bool SetDefaultSearchProviderID(int64_t id);
   int64_t GetDefaultSearchProviderID();
 
-  // Version of the built-in keywords.
-  bool SetBuiltinKeywordVersion(int version);
-  int GetBuiltinKeywordVersion();
+  // Version of the built-in keyword data. It gets set from the
+  // `TemplateURLPrepopulateData::kCurrentDataVersion` when the data was
+  // last updated.
+  bool SetBuiltinKeywordDataVersion(int version);
+  int GetBuiltinKeywordDataVersion();
+
+  // Chrome milestone when the built-in keywords were last updated.
+  bool SetBuiltinKeywordMilestone(int milestone);
+  int GetBuiltinKeywordMilestone();
+
+  // Country associated with the built-in keywords, stored as a country ID,
+  // see `country_codes::CountryStringToCountryID()`.
+  bool SetBuiltinKeywordCountry(int country_id);
+  int GetBuiltinKeywordCountry();
 
   // Version of built-in starter pack keywords (@bookmarks, @settings, etc.).
   bool SetStarterPackKeywordVersion(int version);
diff --git a/components/search_engines/keyword_table_unittest.cc b/components/search_engines/keyword_table_unittest.cc
index 47d3b94..2b6fb6b 100644
--- a/components/search_engines/keyword_table_unittest.cc
+++ b/components/search_engines/keyword_table_unittest.cc
@@ -92,13 +92,19 @@
 
   void KeywordMiscTest() const {
     EXPECT_EQ(kInvalidTemplateURLID, table_->GetDefaultSearchProviderID());
-    EXPECT_EQ(0, table_->GetBuiltinKeywordVersion());
+    EXPECT_EQ(0, table_->GetBuiltinKeywordDataVersion());
+    EXPECT_EQ(0, table_->GetBuiltinKeywordCountry());
+    EXPECT_EQ(0, table_->GetBuiltinKeywordMilestone());
 
     EXPECT_TRUE(table_->SetDefaultSearchProviderID(10));
-    EXPECT_TRUE(table_->SetBuiltinKeywordVersion(11));
+    EXPECT_TRUE(table_->SetBuiltinKeywordDataVersion(11));
+    EXPECT_TRUE(table_->SetBuiltinKeywordCountry(12));
+    EXPECT_TRUE(table_->SetBuiltinKeywordMilestone(13));
 
     EXPECT_EQ(10, table_->GetDefaultSearchProviderID());
-    EXPECT_EQ(11, table_->GetBuiltinKeywordVersion());
+    EXPECT_EQ(11, table_->GetBuiltinKeywordDataVersion());
+    EXPECT_EQ(12, table_->GetBuiltinKeywordCountry());
+    EXPECT_EQ(13, table_->GetBuiltinKeywordMilestone());
   }
 
   void GetStatement(const char* sql, sql::Statement* statement) const {
diff --git a/components/search_engines/keyword_web_data_service.cc b/components/search_engines/keyword_web_data_service.cc
index 410d423..74d4de1 100644
--- a/components/search_engines/keyword_web_data_service.cc
+++ b/components/search_engines/keyword_web_data_service.cc
@@ -33,7 +33,11 @@
   result.default_search_provider_id =
       keyword_table->GetDefaultSearchProviderID();
   result.metadata = {
-      .builtin_keyword_version = keyword_table->GetBuiltinKeywordVersion(),
+      .builtin_keyword_data_version =
+          keyword_table->GetBuiltinKeywordDataVersion(),
+      .builtin_keyword_milestone = keyword_table->GetBuiltinKeywordMilestone(),
+      .builtin_keyword_country = keyword_table->GetBuiltinKeywordCountry(),
+
       .starter_pack_version = keyword_table->GetStarterPackKeywordVersion(),
   };
   return std::make_unique<WDResult<WDKeywordsResult>>(KEYWORDS_RESULT, result);
@@ -46,8 +50,25 @@
              : WebDatabase::COMMIT_NOT_NEEDED;
 }
 
-WebDatabase::State SetBuiltinKeywordVersionImpl(int version, WebDatabase* db) {
-  return KeywordTable::FromWebDatabase(db)->SetBuiltinKeywordVersion(version)
+WebDatabase::State SetBuiltinKeywordDataVersionImpl(int version,
+                                                    WebDatabase* db) {
+  return KeywordTable::FromWebDatabase(db)->SetBuiltinKeywordDataVersion(
+             version)
+             ? WebDatabase::COMMIT_NEEDED
+             : WebDatabase::COMMIT_NOT_NEEDED;
+}
+
+WebDatabase::State SetBuiltinKeywordMilestoneImpl(int milestone_version,
+                                                  WebDatabase* db) {
+  return KeywordTable::FromWebDatabase(db)->SetBuiltinKeywordMilestone(
+             milestone_version)
+             ? WebDatabase::COMMIT_NEEDED
+             : WebDatabase::COMMIT_NOT_NEEDED;
+}
+
+WebDatabase::State SetBuiltinKeywordCountryImpl(int country_id,
+                                                WebDatabase* db) {
+  return KeywordTable::FromWebDatabase(db)->SetBuiltinKeywordCountry(country_id)
              ? WebDatabase::COMMIT_NEEDED
              : WebDatabase::COMMIT_NOT_NEEDED;
 }
@@ -142,9 +163,19 @@
                         base::BindOnce(&SetDefaultSearchProviderIDImpl, id));
 }
 
-void KeywordWebDataService::SetBuiltinKeywordVersion(int version) {
+void KeywordWebDataService::SetBuiltinKeywordDataVersion(int version) {
+  wdbs_->ScheduleDBTask(
+      FROM_HERE, base::BindOnce(&SetBuiltinKeywordDataVersionImpl, version));
+}
+
+void KeywordWebDataService::SetBuiltinKeywordMilestone(int version) {
+  wdbs_->ScheduleDBTask(
+      FROM_HERE, base::BindOnce(&SetBuiltinKeywordMilestoneImpl, version));
+}
+
+void KeywordWebDataService::SetBuiltinKeywordCountry(int version) {
   wdbs_->ScheduleDBTask(FROM_HERE,
-                        base::BindOnce(&SetBuiltinKeywordVersionImpl, version));
+                        base::BindOnce(&SetBuiltinKeywordCountryImpl, version));
 }
 
 void KeywordWebDataService::SetStarterPackKeywordVersion(int version) {
diff --git a/components/search_engines/keyword_web_data_service.h b/components/search_engines/keyword_web_data_service.h
index 37d3ce5..69ec79f 100644
--- a/components/search_engines/keyword_web_data_service.h
+++ b/components/search_engines/keyword_web_data_service.h
@@ -37,18 +37,27 @@
   struct Metadata {
     // Version number of the most recent prepopulate data that has been merged
     // into the current keyword data.
-    int builtin_keyword_version = 0;
+    int builtin_keyword_data_version = 0;
+
+    // Version number of Chrome milestone when the keyword data has been last
+    // merged into the database.
+    int builtin_keyword_milestone = 0;
+
+    // Country associated with the keywords data, stored as a country ID,
+    // see `country_codes::CountryStringToCountryID()`.
+    int builtin_keyword_country = 0;
 
     // Version number of the most recent starter pack data that has been merged
     // into the current keyword data.
     int starter_pack_version = 0;
 
-    // When the metadata is read from the database, will be `false` during the
-    // first run.
-    bool HasBuiltinKeywordData() const { return builtin_keyword_version != 0; }
+    // Whether any metadata associated with the keywords bundle is set.
+    bool HasBuiltinKeywordData() const {
+      return builtin_keyword_data_version != 0 ||
+             builtin_keyword_milestone != 0 || builtin_keyword_country != 0;
+    }
 
-    // When the metadata is read from the database, will be `false` during the
-    // first run.
+    // Whether any metadata associated with the starter pack bundle is set.
     bool HasStarterPackData() const { return starter_pack_version != 0; }
   };
   Metadata metadata;
@@ -106,8 +115,14 @@
   // Sets the ID of the default search provider.
   void SetDefaultSearchProviderID(TemplateURLID id);
 
-  // Sets the version of the builtin keywords.
-  void SetBuiltinKeywordVersion(int version);
+  // Sets the version of the builtin keyword data.
+  void SetBuiltinKeywordDataVersion(int version);
+
+  // Sets the Chrome milestone associated with the builtin keyword data.
+  void SetBuiltinKeywordMilestone(int milestone_version);
+
+  // Sets the country ID associated with the builtin keyword data.
+  void SetBuiltinKeywordCountry(int country_id);
 
   // Sets the version of the starter pack keywords.
   void SetStarterPackKeywordVersion(int version);
diff --git a/components/search_engines/search_engine_choice/search_engine_choice_service.cc b/components/search_engines/search_engine_choice/search_engine_choice_service.cc
index 51214e44..dd1e845 100644
--- a/components/search_engines/search_engine_choice/search_engine_choice_service.cc
+++ b/components/search_engines/search_engine_choice/search_engine_choice_service.cc
@@ -4,22 +4,36 @@
 
 #include "components/search_engines/search_engine_choice/search_engine_choice_service.h"
 
+#include <memory>
+
+#include "base/callback_list.h"
 #include "base/check_deref.h"
 #include "base/command_line.h"
 #include "base/json/json_reader.h"
+#include "base/memory/ptr_util.h"
 #include "base/metrics/histogram_functions.h"
+#include "base/no_destructor.h"
+#include "base/threading/thread_restrictions.h"
 #include "base/version.h"
 #include "components/country_codes/country_codes.h"
 #include "components/policy/core/common/policy_service.h"
 #include "components/policy/policy_constants.h"
+#include "components/pref_registry/pref_registry_syncable.h"
 #include "components/prefs/pref_service.h"
 #include "components/search_engines/eea_countries_ids.h"
 #include "components/search_engines/search_engine_choice_utils.h"
 #include "components/search_engines/search_engines_pref_names.h"
 #include "components/search_engines/search_engines_switches.h"
 #include "components/search_engines/template_url_service.h"
+#include "components/signin/public/base/signin_switches.h"
 #include "components/version_info/version_info.h"
 
+#if BUILDFLAG(IS_ANDROID)
+#include "base/android/jni_android.h"
+#include "base/android/jni_string.h"
+#include "components/search_engines/android/jni_headers/SearchEngineChoiceService_jni.h"
+#endif
+
 namespace search_engines {
 namespace {
 
@@ -102,6 +116,8 @@
   }
 }
 
+using NativeCallbackType = base::OnceCallback<void(int)>;
+
 }  // namespace
 
 SearchEngineChoiceService::SearchEngineChoiceService(PrefService& profile_prefs)
@@ -243,11 +259,11 @@
 }
 
 int SearchEngineChoiceService::GetCountryId() {
-  int command_line_country = country_codes::CountryStringToCountryID(
-      base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
-          switches::kSearchEngineChoiceCountry));
-  if (command_line_country != country_codes::kCountryIDUnknown) {
-    return command_line_country;
+  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kSearchEngineChoiceCountry)) {
+    return country_codes::CountryStringToCountryID(
+        base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+            switches::kSearchEngineChoiceCountry));
   }
 
   bool force_eea_country =
@@ -258,7 +274,10 @@
     return country_codes::CountryStringToCountryID("BE");
   }
 
-  return country_codes::GetCountryIDFromPrefs(&profile_prefs_.get());
+  if (!country_id_cache_.has_value()) {
+    country_id_cache_ = GetCountryIdInternal();
+  }
+  return *country_id_cache_;
 }
 
 void SearchEngineChoiceService::RecordChoiceMade(
@@ -380,4 +399,67 @@
   }
 }
 
+int SearchEngineChoiceService::GetCountryIdInternal() {
+#if BUILDFLAG(IS_ANDROID)
+  if (!IsChoiceScreenFlagEnabled(ChoicePromo::kAny)) {
+    return country_codes::GetCountryIDFromPrefs(&profile_prefs_.get());
+  }
+
+  if (profile_prefs_->HasPrefPath(country_codes::kCountryIDAtInstall)) {
+    return profile_prefs_->GetInteger(country_codes::kCountryIDAtInstall);
+  }
+
+  // Usage of `WeakPtr` is crucial here, as `SearchEngineChoiceService` is not
+  // guaranteed to be alive when the response from Java arrives.
+  auto heap_callback = std::make_unique<NativeCallbackType>(base::BindOnce(
+      &SearchEngineChoiceService::ProcessGetCountryResponseFromPlayApi,
+      weak_ptr_factory_.GetWeakPtr()));
+  // The ownership of the callback on the heap is passed to Java. It will be
+  // deleted by JNI_SearchEngineChoiceService_ProcessCountryFromPlayApi.
+  Java_SearchEngineChoiceService_requestCountryFromPlayApi(
+      base::android::AttachCurrentThread(),
+      reinterpret_cast<intptr_t>(heap_callback.release()));
+  // Java call above might save the preference, so we need to re-check.
+  if (!profile_prefs_->HasPrefPath(country_codes::kCountryIDAtInstall)) {
+    // Couldn't get the value from the Play API, fallback to locale.
+    return country_codes::GetCurrentCountryID();
+  }
+  return profile_prefs_->GetInteger(country_codes::kCountryIDAtInstall);
+#else
+  return country_codes::GetCountryIDFromPrefs(&profile_prefs_.get());
+#endif
+}
+
+#if BUILDFLAG(IS_ANDROID)
+void SearchEngineChoiceService::ProcessGetCountryResponseFromPlayApi(
+    int country_id) {
+  profile_prefs_->SetInteger(country_codes::kCountryIDAtInstall, country_id);
+}
+#endif
+
 }  // namespace search_engines
+
+#if BUILDFLAG(IS_ANDROID)
+void JNI_SearchEngineChoiceService_ProcessCountryFromPlayApi(
+    JNIEnv* env,
+    jlong ptr_to_native_callback,
+    const base::android::JavaParamRef<jstring>& j_device_country) {
+  // Using base::WrapUnique ensures that the callback is deleted when this goes
+  // out of scope.
+  std::unique_ptr<search_engines::NativeCallbackType> heap_callback =
+      base::WrapUnique(reinterpret_cast<search_engines::NativeCallbackType*>(
+          ptr_to_native_callback));
+  CHECK(heap_callback);
+  if (!j_device_country) {
+    return;
+  }
+  std::string device_country =
+      base::android::ConvertJavaStringToUTF8(env, j_device_country);
+  int device_country_id =
+      country_codes::CountryStringToCountryID(device_country);
+  if (device_country_id == country_codes::kCountryIDUnknown) {
+    return;
+  }
+  std::move(*heap_callback).Run(device_country_id);
+}
+#endif
diff --git a/components/search_engines/search_engine_choice/search_engine_choice_service.h b/components/search_engines/search_engine_choice/search_engine_choice_service.h
index d8a4e64..792077d 100644
--- a/components/search_engines/search_engine_choice/search_engine_choice_service.h
+++ b/components/search_engines/search_engine_choice/search_engine_choice_service.h
@@ -6,6 +6,7 @@
 #define COMPONENTS_SEARCH_ENGINES_SEARCH_ENGINE_CHOICE_SEARCH_ENGINE_CHOICE_SERVICE_H_
 
 #include "base/memory/raw_ref.h"
+#include "base/memory/weak_ptr.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "components/search_engines/search_engine_choice_utils.h"
 
@@ -77,7 +78,19 @@
   void PreprocessPrefsForReprompt();
 
  private:
+  int GetCountryIdInternal();
+
+#if BUILDFLAG(IS_ANDROID)
+  void ProcessGetCountryResponseFromPlayApi(int country_id);
+#endif
+
   const raw_ref<PrefService> profile_prefs_;
+
+  // Used to ensure that the value returned from `GetCountryId` never changes
+  // in runtime (different runs can still return different values, though).
+  std::optional<int> country_id_cache_;
+
+  base::WeakPtrFactory<SearchEngineChoiceService> weak_ptr_factory_{this};
 };
 
 }  // namespace search_engines
diff --git a/components/search_engines/search_engine_choice/search_engine_choice_service_unittest.cc b/components/search_engines/search_engine_choice/search_engine_choice_service_unittest.cc
index 27556a0f..efb883f 100644
--- a/components/search_engines/search_engine_choice/search_engine_choice_service_unittest.cc
+++ b/components/search_engines/search_engine_choice/search_engine_choice_service_unittest.cc
@@ -35,11 +35,48 @@
 #include "components/version_info/version_info.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+#if BUILDFLAG(IS_ANDROID)
+#include "base/android/jni_android.h"
+#include "base/android/jni_string.h"
+#include "components/search_engines/android/test_utils_jni_headers/SearchEngineChoiceServiceTestUtil_jni.h"
+#endif
+
 using search_engines::RepromptResult;
 using search_engines::WipeSearchEngineChoiceReason;
 using ::testing::NiceMock;
 
 namespace search_engines {
+namespace {
+#if BUILDFLAG(IS_ANDROID)
+class TestSupportAndroid {
+ public:
+  TestSupportAndroid() {
+    JNIEnv* env = base::android::AttachCurrentThread();
+    base::android::ScopedJavaLocalRef<jobject> java_ref =
+        Java_SearchEngineChoiceServiceTestUtil_Constructor(env);
+    java_test_util_ref_.Reset(env, java_ref.obj());
+  }
+
+  ~TestSupportAndroid() {
+    JNIEnv* env = base::android::AttachCurrentThread();
+    Java_SearchEngineChoiceServiceTestUtil_destroy(env, java_test_util_ref_);
+  }
+
+  void ReturnDeviceCountry(const std::string& device_country) {
+    JNIEnv* env = base::android::AttachCurrentThread();
+    Java_SearchEngineChoiceServiceTestUtil_returnDeviceCountry(
+        env, java_test_util_ref_,
+        base::android::ConvertUTF8ToJavaString(env, device_country));
+  }
+
+ private:
+  base::android::ScopedJavaGlobalRef<jobject> java_test_util_ref_;
+};
+#endif
+
+const int kBelgiumCountryId = country_codes::CountryCharsToCountryID('B', 'E');
+
+}  // namespace
 
 class SearchEngineChoiceServiceTest : public ::testing::Test {
  public:
@@ -448,10 +485,7 @@
   );
 }
 
-TEST_F(SearchEngineChoiceServiceTest, GetSearchEngineChoiceCountryId) {
-  const int kBelgiumCountryId =
-      country_codes::CountryCharsToCountryID('B', 'E');
-
+TEST_F(SearchEngineChoiceServiceTest, GetCountryIdCommandLineOverride) {
   // The test is set up to use the command line to simulate the country as being
   // Belgium.
   EXPECT_EQ(search_engine_choice_service().GetCountryId(), kBelgiumCountryId);
@@ -460,14 +494,6 @@
   // device locale.
   base::CommandLine::ForCurrentProcess()->RemoveSwitch(
       switches::kSearchEngineChoiceCountry);
-  EXPECT_EQ(search_engine_choice_service().GetCountryId(),
-            country_codes::GetCurrentCountryID());
-
-  // When the command line value is invalid, it is ignored.
-  base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
-      switches::kSearchEngineChoiceCountry, "USA");
-  EXPECT_EQ(search_engine_choice_service().GetCountryId(),
-            country_codes::GetCurrentCountryID());
 
   // Note that if the format matches (2-character strings), we might get a
   // country ID that is not valid/supported.
@@ -475,12 +501,121 @@
       switches::kSearchEngineChoiceCountry, "??");
   EXPECT_EQ(search_engine_choice_service().GetCountryId(),
             country_codes::CountryCharsToCountryID('?', '?'));
+}
 
-  // The value set from the pref is reflected otherwise.
-  pref_service()->SetInteger(country_codes::kCountryIDAtInstall,
-                             kBelgiumCountryId);
+TEST_F(SearchEngineChoiceServiceTest,
+       GetCountryIdCommandLineOverrideSetsToUnknownOnFormatMismatch) {
   base::CommandLine::ForCurrentProcess()->RemoveSwitch(
       switches::kSearchEngineChoiceCountry);
+
+  // When the command line value is invalid, the country code should be unknown.
+  base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
+      switches::kSearchEngineChoiceCountry, "USA");
+  EXPECT_EQ(search_engine_choice_service().GetCountryId(),
+            country_codes::kCountryIDUnknown);
+}
+
+#if BUILDFLAG(IS_ANDROID)
+TEST_F(SearchEngineChoiceServiceTest, PlayResponseBeforeGetCountryId) {
+  base::CommandLine::ForCurrentProcess()->RemoveSwitch(
+      switches::kSearchEngineChoiceCountry);
+  TestSupportAndroid test_support;
+  test_support.ReturnDeviceCountry(
+      country_codes::CountryIDToCountryString(kBelgiumCountryId));
+
+  // We got response from Play API before `GetCountryId` was invoked for the
+  // first time this run, so the new value should be used right away.
+  EXPECT_EQ(search_engine_choice_service().GetCountryId(), kBelgiumCountryId);
+  // The pref should be updated as well.
+  EXPECT_EQ(pref_service()->GetInteger(country_codes::kCountryIDAtInstall),
+            kBelgiumCountryId);
+}
+
+TEST_F(SearchEngineChoiceServiceTest, GetCountryIdBeforePlayResponse) {
+  base::CommandLine::ForCurrentProcess()->RemoveSwitch(
+      switches::kSearchEngineChoiceCountry);
+
+  TestSupportAndroid test_support;
+  // We didn't get a response from Play API before `GetCountryId` was invoked,
+  // so the last known country from prefs should be used.
+  EXPECT_EQ(search_engine_choice_service().GetCountryId(),
+            country_codes::GetCurrentCountryID());
+
+  // Simulate a response arriving after the first `GetCountryId` call.
+  test_support.ReturnDeviceCountry(
+      country_codes::CountryIDToCountryString(kBelgiumCountryId));
+
+  // The pref should be updated so the new country can be used the next run.
+  EXPECT_EQ(pref_service()->GetInteger(country_codes::kCountryIDAtInstall),
+            kBelgiumCountryId);
+  // However, `GetCountryId` result shouldn't change until the next run.
+  EXPECT_EQ(search_engine_choice_service().GetCountryId(),
+            country_codes::GetCurrentCountryID());
+}
+
+TEST_F(SearchEngineChoiceServiceTest, GetCountryIdPrefAlreadyWritten) {
+  // The value set from the pref should be used.
+  pref_service()->SetInteger(country_codes::kCountryIDAtInstall,
+                             kBelgiumCountryId);
+  // Don't create `TestSupportAndroid` - since the pref isn't set we should not
+  // reach out to Java.
+  EXPECT_EQ(search_engine_choice_service().GetCountryId(), kBelgiumCountryId);
+}
+#endif  // BUILDFLAG(IS_ANDROID)
+
+TEST_F(SearchEngineChoiceServiceTest, GetCountryIdDefault) {
+#if BUILDFLAG(IS_ANDROID)
+  // On Android, Play API is used when kSearchEngineChoice is enabled.
+  feature_list()->Reset();
+  feature_list()->InitAndDisableFeature(switches::kSearchEngineChoiceTrigger);
+#endif  // BUILDFLAG(IS_ANDROID)
+
+  // Remove the command line flag set by the test.
+  base::CommandLine::ForCurrentProcess()->RemoveSwitch(
+      switches::kSearchEngineChoiceCountry);
+
+  // The default value should be based on the device locale.
+  EXPECT_EQ(search_engine_choice_service().GetCountryId(),
+            country_codes::GetCurrentCountryID());
+}
+
+TEST_F(SearchEngineChoiceServiceTest, GetCountryIdFromPrefs) {
+#if BUILDFLAG(IS_ANDROID)
+  // On Android, Play API is used when kSearchEngineChoice is enabled.
+  feature_list()->Reset();
+  feature_list()->InitAndDisableFeature(switches::kSearchEngineChoiceTrigger);
+#endif  // BUILDFLAG(IS_ANDROID)
+
+  // Remove the command line flag set by the test.
+  base::CommandLine::ForCurrentProcess()->RemoveSwitch(
+      switches::kSearchEngineChoiceCountry);
+
+  // The value set from the pref should be used.
+  pref_service()->SetInteger(country_codes::kCountryIDAtInstall,
+                             kBelgiumCountryId);
+  EXPECT_EQ(search_engine_choice_service().GetCountryId(), kBelgiumCountryId);
+}
+
+TEST_F(SearchEngineChoiceServiceTest, GetCountryIdChangesAfterReading) {
+#if BUILDFLAG(IS_ANDROID)
+  // On Android, Play API is used when kSearchEngineChoice is enabled.
+  feature_list()->Reset();
+  feature_list()->InitAndDisableFeature(switches::kSearchEngineChoiceTrigger);
+#endif  // BUILDFLAG(IS_ANDROID)
+
+  // Remove the command line flag set by the test.
+  base::CommandLine::ForCurrentProcess()->RemoveSwitch(
+      switches::kSearchEngineChoiceCountry);
+
+  // The value set from the pref should be used.
+  pref_service()->SetInteger(country_codes::kCountryIDAtInstall,
+                             kBelgiumCountryId);
+  EXPECT_EQ(search_engine_choice_service().GetCountryId(), kBelgiumCountryId);
+
+  // Change the value in pref.
+  pref_service()->SetInteger(country_codes::kCountryIDAtInstall,
+                             country_codes::CountryCharsToCountryID('U', 'S'));
+  // The value returned by `GetCountryId` shouldn't change.
   EXPECT_EQ(search_engine_choice_service().GetCountryId(), kBelgiumCountryId);
 }
 
@@ -524,12 +659,9 @@
 TEST_F(SearchEngineChoiceServiceTest, RecordChoiceMade) {
   base::CommandLine::ForCurrentProcess()->RemoveSwitch(
       switches::kSearchEngineChoiceCountry);
-
   // Test that the choice is not recorded for countries outside the EEA region.
-  const int kUnitedStatesCountryId =
-      country_codes::CountryCharsToCountryID('U', 'S');
-  pref_service()->SetInteger(country_codes::kCountryIDAtInstall,
-                             kUnitedStatesCountryId);
+  base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
+      switches::kSearchEngineChoiceCountry, "US");
 
   const TemplateURL* default_search_engine =
       template_url_service().GetDefaultSearchProvider();
@@ -549,10 +681,11 @@
       prefs::kDefaultSearchProviderChoiceScreenCompletionVersion));
 
   // Revert to an EEA region country.
-  const int kBelgiumCountryId =
-      country_codes::CountryCharsToCountryID('B', 'E');
-  pref_service()->SetInteger(country_codes::kCountryIDAtInstall,
-                             kBelgiumCountryId);
+  base::CommandLine::ForCurrentProcess()->RemoveSwitch(
+      switches::kSearchEngineChoiceCountry);
+  base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
+      switches::kSearchEngineChoiceCountry,
+      country_codes::CountryIDToCountryString(kBelgiumCountryId));
 
   // Test that the choice is recorded if it wasn't previously done.
   search_engine_choice_service().RecordChoiceMade(
diff --git a/components/search_engines/template_url_prepopulate_data_unittest.cc b/components/search_engines/template_url_prepopulate_data_unittest.cc
index 5334f05..f9655ac 100644
--- a/components/search_engines/template_url_prepopulate_data_unittest.cc
+++ b/components/search_engines/template_url_prepopulate_data_unittest.cc
@@ -165,7 +165,19 @@
     // Pick any EEA country
     const int kFranceCountryId =
         country_codes::CountryCharsToCountryID('F', 'R');
-    prefs_.SetInteger(country_codes::kCountryIDAtInstall, kFranceCountryId);
+    OverrideCountryId(kFranceCountryId);
+  }
+
+  void OverrideCountryId(int country_id) {
+    if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+            switches::kSearchEngineChoiceCountry)) {
+      base::CommandLine::ForCurrentProcess()->RemoveSwitch(
+          switches::kSearchEngineChoiceCountry);
+    }
+
+    base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
+        switches::kSearchEngineChoiceCountry,
+        country_codes::CountryIDToCountryString(country_id));
   }
 
  protected:
@@ -178,7 +190,7 @@
 // ids.
 TEST_F(TemplateURLPrepopulateDataTest, UniqueIDs) {
   for (int country_id : kAllCountryIds) {
-    prefs_.SetInteger(country_codes::kCountryIDAtInstall, country_id);
+    OverrideCountryId(country_id);
     std::vector<std::unique_ptr<TemplateURLData>> urls =
         TemplateURLPrepopulateData::GetPrepopulatedEngines(
             &prefs_, &search_engine_choice_service_, nullptr);
@@ -202,7 +214,7 @@
   const size_t kMaxRow = 5;
 
   for (int country_id : kAllCountryIds) {
-    prefs_.SetInteger(country_codes::kCountryIDAtInstall, country_id);
+    OverrideCountryId(country_id);
 
     const size_t kNumberOfSearchEngines =
         TemplateURLPrepopulateData::GetPrepopulatedEngines(
@@ -370,6 +382,7 @@
 }
 
 TEST_F(TemplateURLPrepopulateDataTest, ClearProvidersFromPrefs) {
+  OverrideCountryId(country_codes::kCountryIDUnknown);
   prefs_.SetUserPref(prefs::kSearchProviderOverridesVersion,
                      std::make_unique<base::Value>(1));
 
@@ -420,7 +433,7 @@
 // Verifies that built-in search providers are processed correctly.
 TEST_F(TemplateURLPrepopulateDataTest, ProvidersFromPrepopulated) {
   // Use United States.
-  prefs_.SetInteger(country_codes::kCountryIDAtInstall, 'U' << 8 | 'S');
+  OverrideCountryId(country_codes::CountryCharsToCountryID('U', 'S'));
   size_t default_index;
   std::vector<std::unique_ptr<TemplateURLData>> t_urls =
       TemplateURLPrepopulateData::GetPrepopulatedEngines(
@@ -460,7 +473,7 @@
 // use https urls.
 TEST_F(TemplateURLPrepopulateDataTest, PrepopulatedAreHttps) {
   for (int country_id : kAllCountryIds) {
-    prefs_.SetInteger(country_codes::kCountryIDAtInstall, country_id);
+    OverrideCountryId(country_id);
 
     std::vector<std::unique_ptr<TemplateURLData>> t_urls =
         TemplateURLPrepopulateData::GetPrepopulatedEngines(
@@ -616,7 +629,7 @@
   std::vector<std::unique_ptr<TemplateURLData>> urls;
 
   // Google is first in US, so confirm index 0.
-  prefs_.SetInteger(country_codes::kCountryIDAtInstall, 'U' << 8 | 'S');
+  OverrideCountryId(country_codes::CountryCharsToCountryID('U', 'S'));
   urls = TemplateURLPrepopulateData::GetPrepopulatedEngines(
       &prefs_, &search_engine_choice_service_, &index);
   EXPECT_EQ(index, size_t{0});
@@ -626,7 +639,7 @@
   // If Google ever does reach top in China, this test will need to be adjusted:
   // check template_url_prepopulate_data.cc reference orders (engines_CN, etc.)
   // to find a suitable country and index.
-  prefs_.SetInteger(country_codes::kCountryIDAtInstall, 'C' << 8 | 'N');
+  OverrideCountryId(country_codes::CountryCharsToCountryID('C', 'N'));
   urls = TemplateURLPrepopulateData::GetPrepopulatedEngines(
       &prefs_, &search_engine_choice_service_, &index);
   EXPECT_GT(index, size_t{0});
@@ -638,8 +651,7 @@
 TEST_F(TemplateURLPrepopulateDataTest, GetPrepopulatedEngineFromFullList) {
   // Ensure that we use the default set of search engines, which is google,
   // bing, yahoo.
-  prefs_.SetInteger(country_codes::kCountryIDAtInstall,
-                    country_codes::kCountryIDUnknown);
+  OverrideCountryId(country_codes::kCountryIDUnknown);
   ASSERT_EQ(TemplateURLPrepopulateData::GetPrepopulatedEngines(
                 &prefs_, &search_engine_choice_service_, nullptr)
                 .size(),
@@ -665,8 +677,8 @@
 #if BUILDFLAG(IS_ANDROID)
 TEST_F(TemplateURLPrepopulateDataTest, GetLocalPrepopulatedEngines) {
   constexpr char sample_country[] = "US";
-  prefs_.SetInteger(country_codes::kCountryIDAtInstall,
-                    sample_country[0] << 8 | sample_country[1]);
+  OverrideCountryId(country_codes::CountryCharsToCountryID(sample_country[0],
+                                                           sample_country[1]));
 
   // For a given country, the output from `GetLocalPrepopulatedEngines`
   // should match the template URLs obtained from `GetPrepopulatedEngines`.
diff --git a/components/search_engines/template_url_service.cc b/components/search_engines/template_url_service.cc
index f3544ba..473095ed 100644
--- a/components/search_engines/template_url_service.cc
+++ b/components/search_engines/template_url_service.cc
@@ -1313,8 +1313,12 @@
     visits_to_add_.clear();
 
     if (updated_keywords_metadata.HasBuiltinKeywordData()) {
-      web_data_service_->SetBuiltinKeywordVersion(
-          updated_keywords_metadata.builtin_keyword_version);
+      web_data_service_->SetBuiltinKeywordDataVersion(
+          updated_keywords_metadata.builtin_keyword_data_version);
+      web_data_service_->SetBuiltinKeywordMilestone(
+          updated_keywords_metadata.builtin_keyword_milestone);
+      web_data_service_->SetBuiltinKeywordCountry(
+          updated_keywords_metadata.builtin_keyword_country);
     }
 
     if (updated_keywords_metadata.HasStarterPackData()) {
diff --git a/components/search_engines/template_url_service_unittest.cc b/components/search_engines/template_url_service_unittest.cc
index 9f82cc2..f6bd598c 100644
--- a/components/search_engines/template_url_service_unittest.cc
+++ b/components/search_engines/template_url_service_unittest.cc
@@ -8,8 +8,11 @@
 
 #include <memory>
 
+#include "base/command_line.h"
 #include "base/threading/platform_thread.h"
+#include "components/country_codes/country_codes.h"
 #include "components/search_engines/search_engine_choice/search_engine_choice_service.h"
+#include "components/search_engines/search_engines_switches.h"
 #include "components/search_engines/template_url_prepopulate_data.h"
 #include "components/search_engines/template_url_service_client.h"
 #include "components/sync_preferences/testing_pref_service_syncable.h"
@@ -25,6 +28,10 @@
     search_engine_choice_service_ =
         std::make_unique<search_engines::SearchEngineChoiceService>(
             pref_service_);
+    // Bypass the country checks.
+    base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
+        switches::kSearchEngineChoiceCountry,
+        country_codes::kCountryCodeUnknown);
 
     template_url_service_ = std::make_unique<TemplateURLService>(
         &pref_service_, search_engine_choice_service_.get(),
diff --git a/components/search_engines/template_url_service_util_unittest.cc b/components/search_engines/template_url_service_util_unittest.cc
index b51bb43..9f1686e 100644
--- a/components/search_engines/template_url_service_util_unittest.cc
+++ b/components/search_engines/template_url_service_util_unittest.cc
@@ -13,6 +13,7 @@
 #include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
 #include "base/values.h"
+#include "base/version_info/version_info.h"
 #include "components/country_codes/country_codes.h"
 #include "components/search_engines/keyword_web_data_service.h"
 #include "components/search_engines/prepopulated_engines.h"
@@ -29,6 +30,7 @@
 #include "components/sync_preferences/testing_pref_service_syncable.h"
 #include "components/webdata/common/web_database_service.h"
 #include "components/webdata/common/webdata_constants.h"
+#include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace {
@@ -246,105 +248,321 @@
   EXPECT_EQ(url_to_update->keyword(), u"new keyword");
 }
 
-TEST(TemplateURLServiceUtilTest, GetSearchProvidersUsingLoadedEngines) {
-  using TemplateURLPrepopulateData::kCurrentDataVersion;
+class TemplateURLServiceUtilLoadTest : public testing::Test {
+ public:
+  // Type used both as input and output of test helpers, to represent the
+  // state of the database from its metadata.
+  struct KeywordTestMetadata {
+    // Version of the built-in keywords data.
+    int data_version = 0;
 
-  sync_preferences::TestingPrefServiceSyncable prefs;
-  search_engines::SearchEngineChoiceService search_engine_choice_service(prefs);
-  size_t starter_pack_engines_count =
-      TemplateURLStarterPackData::GetStarterPackEngines().size();
+    // Chrome milestone.
+    int milestone = 0;
 
-  // Simulates how the search providers are loaded during Chrome init by calling
-  // `GetSearchProvidersUsingLoadedEngines()`. `prefs` carries the profile prefs
-  // between runs.
-  // Returns a struct with fields (`loaded_engines_count` and `loaded_version`)
-  // that can be verified against expectations.
-  auto simulate_run = [&](bool enable_feature, int mocked_current_version) {
-    base::test::ScopedFeatureList feature_list;
-    if (enable_feature) {
-      feature_list.InitAndEnableFeature(switches::kSearchEngineChoiceTrigger);
+    // Country stored in the database. As such, when passed as input, it will
+    // be used to update only the database. To change the profile's country,
+    // write directly to prefs.
+    int country = 0;
+
+    // Number of keywords search engines available. Ignored when passing the
+    // struct as input to set the database's initial state.
+    size_t keyword_engines_count = 0;
+
+    // Whether the database is expected to be configured to show the extended
+    // list with more than 5 keywords search engines. Gets set in prefs, not
+    // in the database metadata.
+    std::optional<bool> use_extended_list = std::nullopt;
+
+    // Formatter method for Google Test.
+    friend std::ostream& operator<<(std::ostream& out,
+                                    const KeywordTestMetadata& m) {
+      return out << "{data_version=" << m.data_version
+                 << ", milestone=" << m.milestone << ", country=" << m.country
+                 << ", keyword_engines_count=" << m.keyword_engines_count
+                 << ", use_extended_list="
+                 << (m.use_extended_list.has_value()
+                         ? (*m.use_extended_list ? "yes" : "no")
+                         : "unset")
+                 << "}";
+    }
+
+    // Needed to be able to use EXPECT_EQ with this struct.
+    bool operator==(const KeywordTestMetadata& rhs) const {
+      return data_version == rhs.data_version && milestone == rhs.milestone &&
+             country == rhs.country &&
+             keyword_engines_count == rhs.keyword_engines_count &&
+             use_extended_list == rhs.use_extended_list;
+    }
+  };
+
+  const int kCurrentDataVersion =
+      TemplateURLPrepopulateData::kCurrentDataVersion;
+  const int kCurrentMilestone = version_info::GetMajorVersionNumberAsInt();
+
+  // For country samples, using Belgium and France for EEA, and the United
+  // States for non-EEA.
+  const int kEeaCountryId = country_codes::CountryStringToCountryID("BE");
+  const int kOtherEeaCountryId = country_codes::CountryStringToCountryID("FR");
+  const int kNonEeaCountryId = country_codes::CountryStringToCountryID("US");
+
+  TemplateURLServiceUtilLoadTest() {
+    TemplateURLPrepopulateData::RegisterProfilePrefs(prefs_.registry());
+  }
+
+  // Simulates how the search providers are loaded during Chrome init by
+  // calling `GetSearchProvidersUsingLoadedEngines()`.
+  // The `initial_state` struct represents the state of the database from its
+  // metadata, before the search providers are loaded. Note:
+  // `keyword_engines_count` is ignored in the input.
+  // The returned struct represents the database state after the search
+  // providers are loaded.
+  KeywordTestMetadata SimulateFromDatabaseState(
+      KeywordTestMetadata initial_state) {
+    if (initial_state.use_extended_list.has_value()) {
+      prefs().SetBoolean(prefs::kDefaultSearchProviderKeywordsUseExtendedList,
+                         *initial_state.use_extended_list);
     } else {
-      feature_list.InitWithFeatures({}, {switches::kSearchEngineChoiceTrigger});
+      prefs().ClearPref(prefs::kDefaultSearchProviderKeywordsUseExtendedList);
     }
 
     TemplateURLService::OwnedTemplateURLVector template_urls;
     WDKeywordsResult::Metadata resource_metadata;
-    resource_metadata.builtin_keyword_version = mocked_current_version;
-    CallGetSearchProvidersUsingLoadedEngines(&prefs,
+    resource_metadata.builtin_keyword_data_version = initial_state.data_version;
+    resource_metadata.builtin_keyword_milestone = initial_state.milestone;
+    resource_metadata.builtin_keyword_country = initial_state.country;
+    CallGetSearchProvidersUsingLoadedEngines(&prefs_,
                                              &search_engine_choice_service,
                                              &template_urls, resource_metadata);
 
-    struct {
-      size_t loaded_engines_count;
-      WDKeywordsResult::Metadata loaded_metadata;
-    } result{
-        template_urls.size() - starter_pack_engines_count,
-        resource_metadata,
-    };
+    std::optional<bool> use_extended_list_output =
+        prefs().HasPrefPath(
+            prefs::kDefaultSearchProviderKeywordsUseExtendedList)
+            ? std::optional<bool>(prefs().GetBoolean(
+                  prefs::kDefaultSearchProviderKeywordsUseExtendedList))
+            : std::nullopt;
+    size_t keyword_engines_count =
+        template_urls.size() -
+        TemplateURLStarterPackData::GetStarterPackEngines().size();
 
-    return result;
-  };
+    return {.data_version = resource_metadata.builtin_keyword_data_version,
+            .milestone = resource_metadata.builtin_keyword_milestone,
+            .country = resource_metadata.builtin_keyword_country,
+            .keyword_engines_count = keyword_engines_count,
+            .use_extended_list = use_extended_list_output};
+  }
 
-  TemplateURLPrepopulateData::RegisterProfilePrefs(prefs.registry());
-  prefs.SetInteger(country_codes::kCountryIDAtInstall,
-                   country_codes::CountryCharsToCountryID('B', 'E'));
+  PrefService& prefs() { return prefs_; }
 
-  // Users initially have the feature is disabled, we should load the 5 engines.
-  auto result = simulate_run(/*enable_feature=*/false,
-                             /*mocked_current_version=*/0);
-  EXPECT_EQ(result.loaded_engines_count, 5u);
-  EXPECT_TRUE(result.loaded_metadata.HasBuiltinKeywordData());
-  EXPECT_EQ(result.loaded_metadata.builtin_keyword_version,
-            kCurrentDataVersion);
-  EXPECT_FALSE(
-      prefs.GetBoolean(prefs::kDefaultSearchProviderKeywordsUseExtendedList));
+ private:
+  sync_preferences::TestingPrefServiceSyncable prefs_;
+  search_engines::SearchEngineChoiceService search_engine_choice_service{
+      prefs_};
+};
 
-  // When re-loading with the same configuration, no new load should happen as
-  // the data did not change.
-  result = simulate_run(/*enable_feature=*/false,
-                        /*mocked_current_version=*/kCurrentDataVersion);
-  EXPECT_EQ(result.loaded_engines_count, 0u);
-  EXPECT_FALSE(result.loaded_metadata.HasBuiltinKeywordData());
-  EXPECT_EQ(result.loaded_metadata.builtin_keyword_version, 0);
-  EXPECT_FALSE(
-      prefs.GetBoolean(prefs::kDefaultSearchProviderKeywordsUseExtendedList));
+TEST_F(TemplateURLServiceUtilLoadTest,
+       GetSearchProvidersUsingLoadedEngines_featureOff) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndDisableFeature(switches::kSearchEngineChoiceTrigger);
+  prefs().SetInteger(country_codes::kCountryIDAtInstall, kEeaCountryId);
 
-  // When loading with the feature this time, the engines should be reloaded.
-  result = simulate_run(/*enable_feature=*/true,
-                        /*mocked_current_version=*/kCurrentDataVersion);
-  EXPECT_EQ(result.loaded_engines_count, 12u);
-  EXPECT_TRUE(result.loaded_metadata.HasBuiltinKeywordData());
-  EXPECT_EQ(result.loaded_metadata.builtin_keyword_version,
-            kCurrentDataVersion);
-  EXPECT_TRUE(
-      prefs.GetBoolean(prefs::kDefaultSearchProviderKeywordsUseExtendedList));
+  const KeywordTestMetadata kDefaultUpdatedState = {
+      .data_version = kCurrentDataVersion,
+      .milestone = kCurrentMilestone,
+      .country = kEeaCountryId,
+      .keyword_engines_count = 5u};
+  const KeywordTestMetadata kNoUpdate = {.data_version = 0,
+                                         .milestone = 0,
+                                         .country = 0,
+                                         .keyword_engines_count = 0u};
 
-  // As for without the feature, no reload when the configuration is the same.
-  result = simulate_run(/*enable_feature=*/true,
-                        /*mocked_current_version=*/kCurrentDataVersion);
-  EXPECT_EQ(result.loaded_engines_count, 0u);
-  EXPECT_FALSE(result.loaded_metadata.HasBuiltinKeywordData());
-  EXPECT_TRUE(
-      prefs.GetBoolean(prefs::kDefaultSearchProviderKeywordsUseExtendedList));
+  // Initial state: nothing. Simulates a fresh install.
+  // The function should populate the profile with 5 engines and current
+  // metadata.
+  auto output = SimulateFromDatabaseState({});
+  EXPECT_EQ(output, kDefaultUpdatedState);
 
-  // And when disabling the feature, we should reload the shorter list.
-  result = simulate_run(/*enable_feature=*/false,
-                        /*mocked_current_version=*/kCurrentDataVersion);
-  EXPECT_EQ(result.loaded_engines_count, 5u);
-  EXPECT_TRUE(result.loaded_metadata.HasBuiltinKeywordData());
-  EXPECT_EQ(result.loaded_metadata.builtin_keyword_version,
-            kCurrentDataVersion);
-  EXPECT_FALSE(
-      prefs.GetBoolean(prefs::kDefaultSearchProviderKeywordsUseExtendedList));
+  // When using the latest metadata from the binary, the function should not
+  // update anything.
+  output = SimulateFromDatabaseState({.data_version = kCurrentDataVersion,
+                                      .milestone = kCurrentMilestone,
+                                      .country = kEeaCountryId});
+  EXPECT_EQ(output, (KeywordTestMetadata{.data_version = 0,
+                                         .milestone = 0,
+                                         .country = 0,
+                                         .keyword_engines_count = 0u}));
 
-  // Toggling the feature state with an older prepopulated data version should
-  // not force the data merge. Guards against a small edge cases, for example if
-  // a user has different versions of chrome with different sets of prepopulated
-  // engines running on different computers.
-  result = simulate_run(/*enable_feature=*/true,
-                        /*mocked_current_version=*/kCurrentDataVersion + 1);
-  EXPECT_EQ(result.loaded_engines_count, 0u);
-  EXPECT_EQ(result.loaded_metadata.builtin_keyword_version, 0);
-  EXPECT_FALSE(
-      prefs.GetBoolean(prefs::kDefaultSearchProviderKeywordsUseExtendedList));
+  // Missing country ID and milestone don't trigger an update either.
+  output = SimulateFromDatabaseState({.data_version = kCurrentDataVersion});
+  EXPECT_EQ(output, kNoUpdate);
+
+  // Out of date keyword data versions trigger updates
+  output = SimulateFromDatabaseState({.data_version = kCurrentDataVersion - 1});
+  EXPECT_EQ(output, kDefaultUpdatedState);
+
+  // Country changes trigger updates
+  output = SimulateFromDatabaseState(
+      {.data_version = kCurrentDataVersion, .country = kOtherEeaCountryId});
+  EXPECT_EQ(output, kDefaultUpdatedState);
+
+  // Milestone changes do NOT trigger updates
+  output = SimulateFromDatabaseState({.data_version = kCurrentDataVersion,
+                                      .milestone = kCurrentMilestone - 1});
+  EXPECT_EQ(output, kNoUpdate);
+
+  // If the extended list was previously used, the function will re-run to
+  // shorten it.
+  output = SimulateFromDatabaseState(
+      {.data_version = kCurrentDataVersion, .use_extended_list = true});
+  EXPECT_EQ(output, kDefaultUpdatedState);
+
+  // If database's data version is more recent than the one built-in to the
+  // client, the updates are suppressed, including shortening the list.
+  output = SimulateFromDatabaseState({.data_version = kCurrentDataVersion + 1,
+                                      .country = kOtherEeaCountryId,
+                                      .use_extended_list = true});
+  EXPECT_EQ(output, (KeywordTestMetadata{.data_version = 0,
+                                         .milestone = 0,
+                                         .country = 0,
+                                         .keyword_engines_count = 0u,
+                                         .use_extended_list = true}));
+}
+
+TEST_F(TemplateURLServiceUtilLoadTest,
+       GetSearchProvidersUsingLoadedEngines_featureOnOutOfEea) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndDisableFeature(switches::kSearchEngineChoiceTrigger);
+  prefs().SetInteger(country_codes::kCountryIDAtInstall, kNonEeaCountryId);
+
+  const KeywordTestMetadata kDefaultUpdatedState = {
+      .data_version = kCurrentDataVersion,
+      .milestone = kCurrentMilestone,
+      .country = kNonEeaCountryId,
+      .keyword_engines_count = 5u};
+  const KeywordTestMetadata kNoUpdate = {.data_version = 0,
+                                         .milestone = 0,
+                                         .country = 0,
+                                         .keyword_engines_count = 0u};
+
+  // Initial state: nothing. Simulates a fresh install.
+  // The function should populate the profile with 5 engines and current
+  // metadata.
+  auto output = SimulateFromDatabaseState({});
+  EXPECT_EQ(output, kDefaultUpdatedState);
+
+  // When using the latest metadata from the binary, the function should not
+  // update anything.
+  output = SimulateFromDatabaseState({.data_version = kCurrentDataVersion,
+                                      .milestone = kCurrentMilestone,
+                                      .country = kNonEeaCountryId});
+  EXPECT_EQ(output, (KeywordTestMetadata{.data_version = 0,
+                                         .milestone = 0,
+                                         .country = 0,
+                                         .keyword_engines_count = 0u}));
+
+  // Missing country ID and milestone don't trigger an update either.
+  output = SimulateFromDatabaseState({.data_version = kCurrentDataVersion});
+  EXPECT_EQ(output, kNoUpdate);
+
+  // Out of date keyword data versions trigger updates
+  output = SimulateFromDatabaseState({.data_version = kCurrentDataVersion - 1});
+  EXPECT_EQ(output, kDefaultUpdatedState);
+
+  // Country changes trigger updates
+  output = SimulateFromDatabaseState(
+      {.data_version = kCurrentDataVersion, .country = kOtherEeaCountryId});
+  EXPECT_EQ(output, kDefaultUpdatedState);
+
+  // Milestone changes do NOT trigger updates
+  output = SimulateFromDatabaseState({.data_version = kCurrentDataVersion,
+                                      .milestone = kCurrentMilestone - 1});
+  EXPECT_EQ(output, kNoUpdate);
+
+  // If the extended list was previously used, the function will re-run to
+  // shorten it.
+  output = SimulateFromDatabaseState(
+      {.data_version = kCurrentDataVersion, .use_extended_list = true});
+  EXPECT_EQ(output, kDefaultUpdatedState);
+
+  // If database's data version is more recent than the one built-in to the
+  // client, the updates are suppressed, including shortening the list.
+  output = SimulateFromDatabaseState({.data_version = kCurrentDataVersion + 1,
+                                      .country = kOtherEeaCountryId,
+                                      .use_extended_list = true});
+  EXPECT_EQ(output, (KeywordTestMetadata{.data_version = 0,
+                                         .milestone = 0,
+                                         .country = 0,
+                                         .keyword_engines_count = 0u,
+                                         .use_extended_list = true}));
+}
+
+TEST_F(TemplateURLServiceUtilLoadTest,
+       GetSearchProvidersUsingLoadedEngines_featureOnInEea) {
+  base::test::ScopedFeatureList feature_list{
+      switches::kSearchEngineChoiceTrigger};
+  prefs().SetInteger(country_codes::kCountryIDAtInstall, kEeaCountryId);
+
+  const KeywordTestMetadata kDefaultUpdatedState = {
+      .data_version = kCurrentDataVersion,
+      .milestone = kCurrentMilestone,
+      .country = kEeaCountryId,
+      .keyword_engines_count = 12u,
+      .use_extended_list = true};
+  const KeywordTestMetadata kNoUpdate = {.data_version = 0,
+                                         .milestone = 0,
+                                         .country = 0,
+                                         .keyword_engines_count = 0u,
+                                         .use_extended_list = true};
+
+  // Initial state: nothing. Simulates a fresh install.
+  // The function should populate the profile with 12 engines and current
+  // metadata.
+  auto output = SimulateFromDatabaseState({});
+  EXPECT_EQ(output, kDefaultUpdatedState);
+
+  // When using the latest metadata from the binary, the function should not
+  // update anything.
+  output = SimulateFromDatabaseState({.data_version = kCurrentDataVersion,
+                                      .milestone = kCurrentMilestone,
+                                      .country = kEeaCountryId,
+                                      .use_extended_list = true});
+  EXPECT_EQ(output, kNoUpdate);
+
+  // Missing country ID and milestone don't trigger an update either.
+  output = SimulateFromDatabaseState(
+      {.data_version = kCurrentDataVersion, .use_extended_list = true});
+  EXPECT_EQ(output, kNoUpdate);
+
+  // Out of date keyword data versions trigger updates
+  output = SimulateFromDatabaseState(
+      {.data_version = kCurrentDataVersion - 1, .use_extended_list = true});
+  EXPECT_EQ(output, kDefaultUpdatedState);
+
+  // Country changes trigger updates
+  output = SimulateFromDatabaseState({.data_version = kCurrentDataVersion,
+                                      .country = kOtherEeaCountryId,
+                                      .use_extended_list = true});
+  EXPECT_EQ(output, kDefaultUpdatedState);
+
+  // Milestone changes trigger updates
+  output = SimulateFromDatabaseState({.data_version = kCurrentDataVersion,
+                                      .milestone = kCurrentMilestone - 1,
+                                      .use_extended_list = true});
+  EXPECT_EQ(output, kDefaultUpdatedState);
+
+  // If the short list was previously used, the function will re-run to
+  // extend it.
+  output = SimulateFromDatabaseState(
+      {.data_version = kCurrentDataVersion, .use_extended_list = std::nullopt});
+  EXPECT_EQ(output, kDefaultUpdatedState);
+
+  // If database's data version is more recent than the one built-in to the
+  // client, the updates are suppressed, including extending the list.
+  output = SimulateFromDatabaseState({.data_version = kCurrentDataVersion + 1,
+                                      .country = kOtherEeaCountryId,
+                                      .use_extended_list = std::nullopt});
+  EXPECT_EQ(output, (KeywordTestMetadata{.data_version = 0,
+                                         .milestone = 0,
+                                         .country = 0,
+                                         .keyword_engines_count = 0u,
+                                         .use_extended_list = std::nullopt}));
 }
diff --git a/components/search_engines/util.cc b/components/search_engines/util.cc
index f27bbe02..f5fb765 100644
--- a/components/search_engines/util.cc
+++ b/components/search_engines/util.cc
@@ -19,6 +19,7 @@
 #include "base/feature_list.h"
 #include "base/ranges/algorithm.h"
 #include "base/time/time.h"
+#include "base/version_info/version_info.h"
 #include "components/country_codes/country_codes.h"
 #include "components/prefs/pref_service.h"
 #include "components/search_engines/keyword_web_data_service.h"
@@ -68,17 +69,26 @@
       search_engines::IsChoiceScreenFlagEnabled(
           search_engines::ChoicePromo::kAny) &&
       search_engines::IsEeaChoiceCountry(country_id);
+  const int milestone = version_info::GetMajorVersionNumberAsInt();
 
   bool update_builtin_keywords;
-  if (keywords_metadata.builtin_keyword_version >
+  if (keywords_metadata.builtin_keyword_data_version >
       prepopulate_resource_keyword_version) {
     // The version in the database is more recent than the version in the Chrome
     // binary. Downgrades are not supported, so don't update it.
     update_builtin_keywords = false;
-  } else if (keywords_metadata.builtin_keyword_version <
+  } else if (keywords_metadata.builtin_keyword_data_version <
              prepopulate_resource_keyword_version) {
     // The built-in data from `prepopulated_engines.json` has been updated.
     update_builtin_keywords = true;
+  } else if (keywords_metadata.builtin_keyword_country != 0 &&
+             keywords_metadata.builtin_keyword_country != country_id) {
+    // The country associated with the profile has changed.
+    // We skip cases where the country was not previously set to avoid
+    // unnecessary churn. We expect that by the time this might matter, the
+    // client will have this data populated when the search engine choice
+    // feature gets enabled.
+    update_builtin_keywords = true;
   } else if (prefs->GetBoolean(
                  prefs::kDefaultSearchProviderKeywordsUseExtendedList) !=
              should_keywords_use_extended_list) {
@@ -86,6 +96,16 @@
     // We started writing the pref while we were not checking the country
     // before. Once the feature flag is removed, we can clean up this pref.
     update_builtin_keywords = true;
+  } else if (should_keywords_use_extended_list &&
+             keywords_metadata.builtin_keyword_milestone != 0 &&
+             keywords_metadata.builtin_keyword_milestone < milestone) {
+    // The milestone changed and we need to recompute the list of visible search
+    // engines. This is needed only in the EEA.
+    // We skip cases where the milestone was not previously set to avoid
+    // unnecessary churn. We expect that by the time this might matter, the
+    // client will have this data populated when the search engine choice
+    // feature gets enabled.
+    update_builtin_keywords = true;
   } else {
     update_builtin_keywords = false;
   }
@@ -93,8 +113,10 @@
   MergeEngineRequirements merge_requirements;
 
   if (update_builtin_keywords) {
-    merge_requirements.metadata.builtin_keyword_version =
+    merge_requirements.metadata.builtin_keyword_data_version =
         prepopulate_resource_keyword_version;
+    merge_requirements.metadata.builtin_keyword_milestone = milestone;
+    merge_requirements.metadata.builtin_keyword_country = country_id;
     merge_requirements.should_keywords_use_extended_list =
         should_keywords_use_extended_list
             ? MergeEngineRequirements::ShouldKeywordsUseExtendedList::kYes
@@ -593,8 +615,8 @@
   // Upgrades (builtin > new) or feature-related merges (builtin == new) only
   // are expected.
   DCHECK(!out_updated_keywords_metadata.HasBuiltinKeywordData() ||
-         out_updated_keywords_metadata.builtin_keyword_version >=
-             keyword_result.metadata.builtin_keyword_version);
+         out_updated_keywords_metadata.builtin_keyword_data_version >=
+             keyword_result.metadata.builtin_keyword_data_version);
 }
 
 void GetSearchProvidersUsingLoadedEngines(
diff --git a/components/services/app_service/public/cpp/shortcut/shortcut.cc b/components/services/app_service/public/cpp/shortcut/shortcut.cc
index a630a94..dc7516f4 100644
--- a/components/services/app_service/public/cpp/shortcut/shortcut.cc
+++ b/components/services/app_service/public/cpp/shortcut/shortcut.cc
@@ -14,7 +14,7 @@
 
 namespace apps {
 
-APP_ENUM_TO_STRING(ShortcutSource, kUnknown, kUser, kDeveloper)
+APP_ENUM_TO_STRING(ShortcutSource, kUnknown, kUser, kPolicy, kDefault)
 
 Shortcut::Shortcut(const std::string& host_app_id, const std::string& local_id)
     : host_app_id(host_app_id),
diff --git a/components/services/app_service/public/cpp/shortcut/shortcut.h b/components/services/app_service/public/cpp/shortcut/shortcut.h
index 6ddef4ae..af870bb 100644
--- a/components/services/app_service/public/cpp/shortcut/shortcut.h
+++ b/components/services/app_service/public/cpp/shortcut/shortcut.h
@@ -23,11 +23,14 @@
 using ShortcutId = base::StrongAlias<class ShortcutIdTag, std::string>;
 
 // Where the shortcut was created from.
-ENUM_FOR_COMPONENT(SHORTCUT,
-                   ShortcutSource,
-                   kUnknown = 0,
-                   kUser = 1,      // Created by the user.
-                   kDeveloper = 2  // Created by the developer. e.g. jumplist
+ENUM_FOR_COMPONENT(
+    SHORTCUT,
+    ShortcutSource,
+    kUnknown = 0,
+    kUser = 1,    // Created by the user and managed by sync. This includes any
+                  // shortcuts created by syncing between devices.
+    kPolicy = 2,  // Created by organization policy.
+    kDefault = 3  // Created by default.
 )
 
 struct COMPONENT_EXPORT(SHORTCUT) Shortcut {
diff --git a/components/services/app_service/public/cpp/shortcut/shortcut_registry_cache_unittest.cc b/components/services/app_service/public/cpp/shortcut/shortcut_registry_cache_unittest.cc
index 5052fafa..b983163 100644
--- a/components/services/app_service/public/cpp/shortcut/shortcut_registry_cache_unittest.cc
+++ b/components/services/app_service/public/cpp/shortcut/shortcut_registry_cache_unittest.cc
@@ -121,7 +121,7 @@
 
   auto shortcut_delta = std::make_unique<Shortcut>(host_app_id, local_id);
   shortcut_delta->name = "new name";
-  shortcut_delta->shortcut_source = ShortcutSource::kDeveloper;
+  shortcut_delta->shortcut_source = ShortcutSource::kPolicy;
   shortcut_delta->icon_key =
       IconKey(IconKey::kInvalidResourceId, IconEffects::kCrOsStandardIcon);
   shortcut_delta->icon_key->update_version = true;
@@ -135,7 +135,7 @@
   ASSERT_TRUE(stored_shortcut);
   EXPECT_EQ(stored_shortcut->shortcut_id, shortcut_id);
   EXPECT_EQ(stored_shortcut->name, "new name");
-  EXPECT_EQ(stored_shortcut->shortcut_source, ShortcutSource::kDeveloper);
+  EXPECT_EQ(stored_shortcut->shortcut_source, ShortcutSource::kPolicy);
   EXPECT_EQ(stored_shortcut->host_app_id, host_app_id);
   EXPECT_EQ(stored_shortcut->local_id, local_id);
   IconKey icon_key(IconKey::kInvalidResourceId, IconEffects::kCrOsStandardIcon);
@@ -179,7 +179,7 @@
 
   auto shortcut_delta = std::make_unique<Shortcut>(host_app_id, local_id);
   shortcut_delta->name = "new name";
-  shortcut_delta->shortcut_source = ShortcutSource::kDeveloper;
+  shortcut_delta->shortcut_source = ShortcutSource::kPolicy;
   shortcut_delta->icon_key = IconKey(/*resource_id=*/1, /*icon_effects=*/1);
   shortcut_delta->icon_key->update_version = false;
   std::unique_ptr<Shortcut> current_state =
diff --git a/components/services/app_service/public/cpp/shortcut/shortcut_update_unittest.cc b/components/services/app_service/public/cpp/shortcut/shortcut_update_unittest.cc
index 6402775..6d1b7ff4 100644
--- a/components/services/app_service/public/cpp/shortcut/shortcut_update_unittest.cc
+++ b/components/services/app_service/public/cpp/shortcut/shortcut_update_unittest.cc
@@ -22,7 +22,7 @@
 TEST_F(ShortcutUpdateTest, StateIsNonNull) {
   Shortcut shortcut = Shortcut(host_app_id_, local_id_);
   shortcut.name = "Name";
-  shortcut.shortcut_source = ShortcutSource::kDeveloper;
+  shortcut.shortcut_source = ShortcutSource::kPolicy;
   shortcut.icon_key = IconKey();
   shortcut.icon_key->update_version = 100;
   shortcut.allow_removal = true;
@@ -35,7 +35,7 @@
   EXPECT_EQ(u.Name(), "Name");
   EXPECT_FALSE(u.NameChanged());
 
-  EXPECT_EQ(u.ShortcutSource(), ShortcutSource::kDeveloper);
+  EXPECT_EQ(u.ShortcutSource(), ShortcutSource::kPolicy);
   EXPECT_FALSE(u.ShortcutSourceChanged());
 
   IconKey icon_key;
@@ -52,7 +52,7 @@
 TEST_F(ShortcutUpdateTest, DeltaIsNonNull) {
   Shortcut shortcut = Shortcut(host_app_id_, local_id_);
   shortcut.name = "Name";
-  shortcut.shortcut_source = ShortcutSource::kDeveloper;
+  shortcut.shortcut_source = ShortcutSource::kPolicy;
   shortcut.icon_key = IconKey();
   shortcut.icon_key->update_version = false;
   shortcut.allow_removal = true;
@@ -65,7 +65,7 @@
   EXPECT_EQ(u.Name(), "Name");
   EXPECT_TRUE(u.NameChanged());
 
-  EXPECT_EQ(u.ShortcutSource(), ShortcutSource::kDeveloper);
+  EXPECT_EQ(u.ShortcutSource(), ShortcutSource::kPolicy);
   EXPECT_TRUE(u.ShortcutSourceChanged());
 
   IconKey icon_key;
@@ -82,7 +82,7 @@
 TEST_F(ShortcutUpdateTest, StateAndDeltaAreNonNull) {
   Shortcut shortcut_state = Shortcut(host_app_id_, local_id_);
   shortcut_state.name = "Name";
-  shortcut_state.shortcut_source = ShortcutSource::kDeveloper;
+  shortcut_state.shortcut_source = ShortcutSource::kPolicy;
   shortcut_state.icon_key = IconKey();
   shortcut_state.icon_key->update_version = 100;
   shortcut_state.allow_removal = true;
@@ -120,7 +120,7 @@
 TEST_F(ShortcutUpdateTest, Merge) {
   Shortcut shortcut_state = Shortcut(host_app_id_, local_id_);
   shortcut_state.name = "Name";
-  shortcut_state.shortcut_source = ShortcutSource::kDeveloper;
+  shortcut_state.shortcut_source = ShortcutSource::kPolicy;
   shortcut_state.icon_key = IconKey();
   shortcut_state.icon_key->update_version = 100;
   shortcut_state.allow_removal = true;
diff --git a/components/subresource_filter/content/browser/content_subresource_filter_throttle_manager.cc b/components/subresource_filter/content/browser/content_subresource_filter_throttle_manager.cc
index 8a257cc..4ad7636 100644
--- a/components/subresource_filter/content/browser/content_subresource_filter_throttle_manager.cc
+++ b/components/subresource_filter/content/browser/content_subresource_filter_throttle_manager.cc
@@ -12,7 +12,6 @@
 #include "base/functional/bind.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/metrics/histogram_macros.h"
-#include "base/trace_event/trace_conversion_helper.h"
 #include "base/trace_event/trace_event.h"
 #include "base/trace_event/traced_value.h"
 #include "components/subresource_filter/content/browser/activation_state_computing_navigation_throttle.h"
diff --git a/components/supervised_user/core/browser/supervised_user_content_settings_provider.cc b/components/supervised_user/core/browser/supervised_user_content_settings_provider.cc
index bb7cfc1f..ec967034 100644
--- a/components/supervised_user/core/browser/supervised_user_content_settings_provider.cc
+++ b/components/supervised_user/core/browser/supervised_user_content_settings_provider.cc
@@ -96,7 +96,8 @@
   }
   for (ContentSettingsType type : to_notify) {
     NotifyObservers(ContentSettingsPattern::Wildcard(),
-                    ContentSettingsPattern::Wildcard(), type);
+                    ContentSettingsPattern::Wildcard(), type,
+                    /*partition_key=*/nullptr);
   }
 }
 
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index 3adeb7a..a460c65 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -2742,10 +2742,10 @@
         "media/capture/desktop_capture_device.h",
         "media/capture/mouse_cursor_overlay_controller.cc",
         "media/capture/mouse_cursor_overlay_controller.h",
-        "renderer_host/media/captured_surface_control_permission_manager.cc",
-        "renderer_host/media/captured_surface_control_permission_manager.h",
-        "renderer_host/media/captured_surface_controller.cc",
-        "renderer_host/media/captured_surface_controller.h",
+        "media/captured_surface_control_permission_manager.cc",
+        "media/captured_surface_control_permission_manager.h",
+        "media/captured_surface_controller.cc",
+        "media/captured_surface_controller.h",
       ]
       public_deps += [ "//ui/base/cursor" ]
       deps += [ "//third_party/webrtc_overrides:webrtc_component" ]
diff --git a/content/browser/devtools/devtools_instrumentation.cc b/content/browser/devtools/devtools_instrumentation.cc
index 303c663..600827382 100644
--- a/content/browser/devtools/devtools_instrumentation.cc
+++ b/content/browser/devtools/devtools_instrumentation.cc
@@ -1557,17 +1557,6 @@
       event_time, type, unique_auction_id, parent_auction_id, auction_config);
 }
 
-void OnInterestGroupAuctionNetworkRequestCreated(
-    int frame_tree_node_id,
-    content::InterestGroupAuctionFetchType type,
-    const std::string& request_id,
-    const std::vector<std::string>& devtools_auction_ids) {
-  DispatchToAgents(frame_tree_node_id,
-                   &protocol::StorageHandler::
-                       NotifyInterestGroupAuctionNetworkRequestCreated,
-                   type, request_id, devtools_auction_ids);
-}
-
 void OnNavigationRequestWillBeSent(
     const NavigationRequest& navigation_request) {
   // Note this intentionally deviates from the usual instrumentation signal
diff --git a/content/browser/devtools/devtools_instrumentation.h b/content/browser/devtools/devtools_instrumentation.h
index b72b5e8e..67b7e7c 100644
--- a/content/browser/devtools/devtools_instrumentation.h
+++ b/content/browser/devtools/devtools_instrumentation.h
@@ -240,11 +240,6 @@
     const std::string& unique_auction_id,
     base::optional_ref<const std::string> parent_auction_id,
     const base::Value::Dict& auction_config);
-void OnInterestGroupAuctionNetworkRequestCreated(
-    int frame_tree_node_id,
-    content::InterestGroupAuctionFetchType type,
-    const std::string& request_id,
-    const std::vector<std::string>& devtools_auction_ids);
 
 bool ShouldBypassCSP(const NavigationRequest& nav_request);
 
diff --git a/content/browser/devtools/protocol/storage_handler.cc b/content/browser/devtools/protocol/storage_handler.cc
index c5593a98..c9a4243 100644
--- a/content/browser/devtools/protocol/storage_handler.cc
+++ b/content/browser/devtools/protocol/storage_handler.cc
@@ -2188,37 +2188,5 @@
       std::make_unique<base::Value::Dict>(auction_config.Clone()));
 }
 
-void StorageHandler::NotifyInterestGroupAuctionNetworkRequestCreated(
-    content::InterestGroupAuctionFetchType type,
-    const std::string& request_id,
-    const std::vector<std::string>& devtools_auction_ids) {
-  if (!interest_group_auction_tracking_enabled_) {
-    return;
-  }
-  std::string type_enum;
-  switch (type) {
-    case content::InterestGroupAuctionFetchType::kBidderJs:
-      type_enum = Storage::InterestGroupAuctionFetchTypeEnum::BidderJs;
-      break;
-    case content::InterestGroupAuctionFetchType::kBidderWasm:
-      type_enum = Storage::InterestGroupAuctionFetchTypeEnum::BidderWasm;
-      break;
-    case content::InterestGroupAuctionFetchType::kSellerJs:
-      type_enum = Storage::InterestGroupAuctionFetchTypeEnum::SellerJs;
-      break;
-    case content::InterestGroupAuctionFetchType::kBidderTrustedSignals:
-      type_enum =
-          Storage::InterestGroupAuctionFetchTypeEnum::BidderTrustedSignals;
-      break;
-    case content::InterestGroupAuctionFetchType::kSellerTrustedSignals:
-      type_enum =
-          Storage::InterestGroupAuctionFetchTypeEnum::SellerTrustedSignals;
-      break;
-  };
-  frontend_->InterestGroupAuctionNetworkRequestCreated(
-      type_enum, request_id,
-      std::make_unique<std::vector<std::string>>(devtools_auction_ids));
-}
-
 }  // namespace protocol
 }  // namespace content
diff --git a/content/browser/devtools/protocol/storage_handler.h b/content/browser/devtools/protocol/storage_handler.h
index 71642d2..20641890 100644
--- a/content/browser/devtools/protocol/storage_handler.h
+++ b/content/browser/devtools/protocol/storage_handler.h
@@ -163,11 +163,6 @@
       base::optional_ref<const std::string> parent_auction_id,
       const base::Value::Dict& auction_config);
 
-  void NotifyInterestGroupAuctionNetworkRequestCreated(
-      content::InterestGroupAuctionFetchType type,
-      const std::string& request_id,
-      const std::vector<std::string>& devtools_auction_ids);
-
  private:
   // See definition for lifetime information.
   class CacheStorageObserver;
diff --git a/content/browser/interest_group/auction_url_loader_factory_proxy.cc b/content/browser/interest_group/auction_url_loader_factory_proxy.cc
index ab2fe0d..c2a7e2d 100644
--- a/content/browser/interest_group/auction_url_loader_factory_proxy.cc
+++ b/content/browser/interest_group/auction_url_loader_factory_proxy.cc
@@ -62,7 +62,6 @@
     GetUrlLoaderFactoryCallback get_trusted_url_loader_factory,
     PreconnectSocketCallback preconnect_socket_callback,
     GetCookieDeprecationLabelCallback get_cookie_deprecation_label,
-    GetDevtoolsAuctionIdsCallback get_devtools_auction_ids,
     bool force_reload,
     const url::Origin& top_frame_origin,
     const url::Origin& frame_origin,
@@ -79,7 +78,6 @@
       get_trusted_url_loader_factory_(
           std::move(get_trusted_url_loader_factory)),
       get_cookie_deprecation_label_(std::move(get_cookie_deprecation_label)),
-      get_devtools_auction_ids_(std::move(get_devtools_auction_ids)),
       top_frame_origin_(top_frame_origin),
       frame_origin_(frame_origin),
       renderer_process_id_(renderer_process_id),
@@ -124,7 +122,6 @@
 
   bool is_request_allowed = false;
   bool is_trusted_bidding_signals_request = false;
-  std::optional<InterestGroupAuctionFetchType> event_type;
 
   const SubresourceUrlBuilder::BundleSubresourceInfo* maybe_subresource_info =
       nullptr;
@@ -133,17 +130,11 @@
   if (url_request.url == script_url_ &&
       accept_header == "application/javascript") {
     is_request_allowed = true;
-    event_type = is_for_seller_ ? InterestGroupAuctionFetchType::kSellerJs
-                                : InterestGroupAuctionFetchType::kBidderJs;
   } else if (wasm_url_.has_value() && url_request.url == wasm_url_.value() &&
              accept_header == "application/wasm") {
-    event_type = InterestGroupAuctionFetchType::kBidderWasm;
     is_request_allowed = true;
   } else if (CouldBeTrustedSignalsUrl(url_request.url) &&
              accept_header == "application/json") {
-    event_type = is_for_seller_
-                     ? InterestGroupAuctionFetchType::kSellerTrustedSignals
-                     : InterestGroupAuctionFetchType::kBidderTrustedSignals;
     is_request_allowed = true;
     is_trusted_bidding_signals_request = true;
   } else {
@@ -197,16 +188,6 @@
   new_request.request_initiator = frame_origin_;
   new_request.enable_load_timing = url_request.enable_load_timing;
 
-  if (event_type.has_value() && new_request.devtools_request_id.has_value() &&
-      devtools_instrumentation::NeedInterestGroupAuctionEvents(
-          owner_frame_tree_node_id_)) {
-    std::vector<std::string> relevant_auction_ids =
-        get_devtools_auction_ids_.Run();
-    devtools_instrumentation::OnInterestGroupAuctionNetworkRequestCreated(
-        owner_frame_tree_node_id_, *event_type,
-        *new_request.devtools_request_id, relevant_auction_ids);
-  }
-
   if (is_trusted_bidding_signals_request) {
     std::optional<std::string> maybe_deprecation_label =
         get_cookie_deprecation_label_.Run();
diff --git a/content/browser/interest_group/auction_url_loader_factory_proxy.h b/content/browser/interest_group/auction_url_loader_factory_proxy.h
index b7d3894b..b62f49ed 100644
--- a/content/browser/interest_group/auction_url_loader_factory_proxy.h
+++ b/content/browser/interest_group/auction_url_loader_factory_proxy.h
@@ -8,7 +8,6 @@
 #include <stdint.h>
 
 #include <optional>
-#include <vector>
 
 #include "base/functional/callback_forward.h"
 #include "base/strings/string_piece.h"
@@ -49,9 +48,6 @@
   using GetCookieDeprecationLabelCallback =
       base::RepeatingCallback<std::optional<std::string>()>;
 
-  using GetDevtoolsAuctionIdsCallback =
-      base::RepeatingCallback<std::vector<std::string>()>;
-
   // Passed in callbacks must be safe to call at any time during the lifetime of
   // the AuctionURLLoaderFactoryProxy.
   //
@@ -97,7 +93,6 @@
       GetUrlLoaderFactoryCallback get_trusted_url_loader_factory,
       PreconnectSocketCallback preconnect_socket_callback,
       GetCookieDeprecationLabelCallback get_cookie_deprecation_label,
-      GetDevtoolsAuctionIdsCallback get_devtools_auction_ids,
       bool force_reload,
       const url::Origin& top_frame_origin,
       const url::Origin& frame_origin,
@@ -146,7 +141,6 @@
   const GetUrlLoaderFactoryCallback get_frame_url_loader_factory_;
   const GetUrlLoaderFactoryCallback get_trusted_url_loader_factory_;
   const GetCookieDeprecationLabelCallback get_cookie_deprecation_label_;
-  const GetDevtoolsAuctionIdsCallback get_devtools_auction_ids_;
 
   // Manages the bundle subresource URLs that may be accessed by the worklet.
   SubresourceUrlAuthorizations subresource_url_authorizations_;
diff --git a/content/browser/interest_group/auction_url_loader_factory_proxy_unittest.cc b/content/browser/interest_group/auction_url_loader_factory_proxy_unittest.cc
index 49ce591..7080cec 100644
--- a/content/browser/interest_group/auction_url_loader_factory_proxy_unittest.cc
+++ b/content/browser/interest_group/auction_url_loader_factory_proxy_unittest.cc
@@ -133,7 +133,6 @@
                        base::Unretained(this)),
         base::BindRepeating(
             []() -> std::optional<std::string> { return std::nullopt; }),
-        base::BindRepeating([]() -> std::vector<std::string> { return {}; }),
         /*force_reload=*/force_reload_, top_frame_origin_, frame_origin_,
         /*renderer_process_id=*/kRenderProcessId, is_for_seller_,
         client_security_state_.Clone(), GURL(kScriptUrl), wasm_url_,
diff --git a/content/browser/interest_group/auction_worklet_manager.cc b/content/browser/interest_group/auction_worklet_manager.cc
index 7f8e6a65..e5a44e6 100644
--- a/content/browser/interest_group/auction_worklet_manager.cc
+++ b/content/browser/interest_group/auction_worklet_manager.cc
@@ -125,8 +125,6 @@
     return &url_loader_factory_proxy_->subresource_url_authorizations();
   }
 
-  std::vector<std::string> ComputeDevtoolsAuctionIds();
-
  private:
   friend class base::RefCounted<WorkletOwner>;
 
@@ -155,9 +153,6 @@
   void OnWorkletDisconnected(uint32_t /* custom_reason */,
                              const std::string& description);
 
-  static std::vector<std::string> GetDevtoolsAuctionIds(
-      base::WeakPtr<WorkletOwner> self);
-
   // Set to null once `this` is removed from AuctionWorkletManager's
   // WorkletOwner list, which happens on destruction or on Mojo pipe closure.
   // The latter allows a handle to still exist and refer to a WorkletOwner with
@@ -194,9 +189,6 @@
 
   uint64_t next_handle_seq_num_ = 0;
 
-  // Map from devtools auction ID to number of handles from that auction.
-  std::map<std::string, int> registered_devtools_auction_ids_;
-
   base::WeakPtrFactory<WorkletOwner> weak_ptr_factory_{this};
 };
 
@@ -220,21 +212,12 @@
 
 void AuctionWorkletManager::WorkletOwner::RegisterHandle(HandleKey handle) {
   handles_waiting_for_process_.insert(handle);
-  ++registered_devtools_auction_ids_[handle.second->devtools_auction_id_];
   if (worklet_created()) {
     MaybeQueueNotifications();
   }
 }
 
 void AuctionWorkletManager::WorkletOwner::UnregisterHandle(HandleKey handle) {
-  auto it = registered_devtools_auction_ids_.find(
-      handle.second->devtools_auction_id_);
-  DCHECK(it != registered_devtools_auction_ids_.end());
-  --it->second;
-  if (it->second == 0) {
-    registered_devtools_auction_ids_.erase(it);
-  }
-
   if (!handles_waiting_for_process_.erase(handle)) {
     // The handle should only be in one of the sets, so only need to search
     // `handles_with_process_` if it wasn't in `handles_waiting_for_process_`.
@@ -243,15 +226,6 @@
   DCHECK_EQ(handles_waiting_for_process_.count(handle), 0u);
 }
 
-std::vector<std::string>
-AuctionWorkletManager::WorkletOwner::ComputeDevtoolsAuctionIds() {
-  std::vector<std::string> result;
-  for (const auto& [id, count] : registered_devtools_auction_ids_) {
-    result.push_back(id);
-  }
-  return result;
-}
-
 AuctionWorkletManager::WorkletOwner::~WorkletOwner() {
   DCHECK(handles_waiting_for_process_.empty());
   DCHECK(handles_with_process_.empty());
@@ -370,8 +344,6 @@
       base::BindOnce(&Delegate::PreconnectSocket, base::Unretained(delegate)),
       base::BindRepeating(&Delegate::GetCookieDeprecationLabel,
                           base::Unretained(delegate)),
-      base::BindRepeating(&WorkletOwner::GetDevtoolsAuctionIds,
-                          weak_ptr_factory_.GetWeakPtr()),
       /*force_reload=*/rfh->reload_type() == ReloadType::BYPASSING_CACHE,
       worklet_manager_->top_window_origin(), worklet_manager_->frame_origin(),
       // NOTE: `rfh` can be null in tests.
@@ -460,17 +432,6 @@
   MaybeQueueNotifications();
 }
 
-// static
-std::vector<std::string>
-AuctionWorkletManager::WorkletOwner::GetDevtoolsAuctionIds(
-    base::WeakPtr<WorkletOwner> self) {
-  if (self) {
-    return self->ComputeDevtoolsAuctionIds();
-  }
-
-  return std::vector<std::string>();
-}
-
 AuctionWorkletManager::WorkletKey::WorkletKey(
     WorkletType type,
     const GURL& script_url,
@@ -563,18 +524,11 @@
   return *worklet_owner_->subresource_url_authorizations();
 }
 
-std::vector<std::string>
-AuctionWorkletManager::WorkletHandle::GetDevtoolsAuctionIdsForTesting() {
-  return worklet_owner_->ComputeDevtoolsAuctionIds();
-}
-
 AuctionWorkletManager::WorkletHandle::WorkletHandle(
-    std::string devtools_auction_id,
     scoped_refptr<WorkletOwner> worklet_owner,
     base::OnceClosure worklet_available_callback,
     FatalErrorCallback fatal_error_callback)
     : worklet_owner_(std::move(worklet_owner)),
-      devtools_auction_id_(std::move(devtools_auction_id)),
       worklet_available_callback_(std::move(worklet_available_callback)),
       fatal_error_callback_(std::move(fatal_error_callback)),
       seq_num_(worklet_owner_->GetNextSeqNum()) {
@@ -679,7 +633,6 @@
 }
 
 void AuctionWorkletManager::RequestBidderWorklet(
-    std::string devtools_auction_id,
     const GURL& bidding_logic_url,
     const std::optional<GURL>& wasm_url,
     const std::optional<GURL>& trusted_bidding_signals_url,
@@ -693,12 +646,11 @@
       BidderWorkletKey(bidding_logic_url, wasm_url, trusted_bidding_signals_url,
                        needs_cors_for_additional_bid, experiment_group_id,
                        trusted_bidding_signals_slot_size_param),
-      std::move(devtools_auction_id), std::move(worklet_available_callback),
-      std::move(fatal_error_callback), out_worklet_handle);
+      std::move(worklet_available_callback), std::move(fatal_error_callback),
+      out_worklet_handle);
 }
 
 void AuctionWorkletManager::RequestSellerWorklet(
-    std::string devtools_auction_id,
     const GURL& decision_logic_url,
     const std::optional<GURL>& trusted_scoring_signals_url,
     std::optional<uint16_t> experiment_group_id,
@@ -712,14 +664,13 @@
                           /*needs_cors_for_additional_bid=*/false,
                           experiment_group_id,
                           /*trusted_bidding_signals_slot_size_param=*/"");
-  RequestWorkletByKey(std::move(worklet_info), std::move(devtools_auction_id),
+  RequestWorkletByKey(std::move(worklet_info),
                       std::move(worklet_available_callback),
                       std::move(fatal_error_callback), out_worklet_handle);
 }
 
 void AuctionWorkletManager::RequestWorkletByKey(
     WorkletKey worklet_info,
-    std::string devtools_auction_id,
     base::OnceClosure worklet_available_callback,
     FatalErrorCallback fatal_error_callback,
     std::unique_ptr<WorkletHandle>& out_worklet_handle) {
@@ -735,8 +686,8 @@
     worklets_.emplace(std::pair(std::move(worklet_info), worklet.get()));
   }
   out_worklet_handle.reset(new WorkletHandle(
-      std::move(devtools_auction_id), std::move(worklet),
-      std::move(worklet_available_callback), std::move(fatal_error_callback)));
+      std::move(worklet), std::move(worklet_available_callback),
+      std::move(fatal_error_callback)));
 }
 
 void AuctionWorkletManager::OnWorkletNoLongerUsable(WorkletOwner* worklet) {
diff --git a/content/browser/interest_group/auction_worklet_manager.h b/content/browser/interest_group/auction_worklet_manager.h
index 2da6f15..53a427b 100644
--- a/content/browser/interest_group/auction_worklet_manager.h
+++ b/content/browser/interest_group/auction_worklet_manager.h
@@ -193,17 +193,12 @@
     const SubresourceUrlAuthorizations&
     GetSubresourceUrlAuthorizationsForTesting();
 
-    // Returns devtools IDs of all auctions that are using the worklet pointed
-    // to by this handle.
-    std::vector<std::string> GetDevtoolsAuctionIdsForTesting();
-
    private:
     friend class AuctionWorkletManager;
     friend class WorkletOwner;
 
     // These are only created by AuctionWorkletManager.
-    explicit WorkletHandle(std::string devtools_auction_id,
-                           scoped_refptr<WorkletOwner> worklet_owner,
+    explicit WorkletHandle(scoped_refptr<WorkletOwner> worklet_owner,
                            base::OnceClosure worklet_available_callback,
                            FatalErrorCallback fatal_error_callback);
 
@@ -217,7 +212,6 @@
     bool worklet_created() const;
 
     scoped_refptr<WorkletOwner> worklet_owner_;
-    std::string devtools_auction_id_;
 
     base::OnceClosure worklet_available_callback_;
     FatalErrorCallback fatal_error_callback_;
@@ -249,9 +243,6 @@
   // Requests a worklet with the specified properties. The top frame origin and
   // debugging information are obtained from the Delegate's RenderFrameHost.
   //
-  // `devtools_auction_id` will be used to related network events to given
-  // auction. It serves no other purpose and does not affect worklet sharing.
-  //
   // The AuctionWorkletManager will handle requesting a process, hooking up
   // DevTools, and merging requests with the same parameters so they can share a
   // single worklet.
@@ -277,7 +268,6 @@
   // The callbacks should not delete the AuctionWorkletManager itself, but are
   // free to release any WorkletHandle they wish.
   void RequestBidderWorklet(
-      std::string devtools_auction_id,
       const GURL& bidding_logic_url,
       const std::optional<GURL>& wasm_url,
       const std::optional<GURL>& trusted_bidding_signals_url,
@@ -288,7 +278,6 @@
       FatalErrorCallback fatal_error_callback,
       std::unique_ptr<WorkletHandle>& out_worklet_handle);
   void RequestSellerWorklet(
-      std::string devtools_auction_id,
       const GURL& decision_logic_url,
       const std::optional<GURL>& trusted_scoring_signals_url,
       std::optional<uint16_t> experiment_group_id,
@@ -296,7 +285,6 @@
       FatalErrorCallback fatal_error_callback,
       std::unique_ptr<WorkletHandle>& out_worklet_handle);
   void RequestWorkletByKey(WorkletKey worklet_info,
-                           std::string devtools_auction_id,
                            base::OnceClosure worklet_available_callback,
                            FatalErrorCallback fatal_error_callback,
                            std::unique_ptr<WorkletHandle>& out_worklet_handle);
diff --git a/content/browser/interest_group/auction_worklet_manager_unittest.cc b/content/browser/interest_group/auction_worklet_manager_unittest.cc
index 2863602a..88eb2a045 100644
--- a/content/browser/interest_group/auction_worklet_manager_unittest.cc
+++ b/content/browser/interest_group/auction_worklet_manager_unittest.cc
@@ -51,17 +51,9 @@
 #include "url/gurl.h"
 #include "url/origin.h"
 
-using testing::UnorderedElementsAre;
-
 namespace content {
 namespace {
 
-const char kAuction1[] = "a";
-const char kAuction2[] = "b";
-const char kAuction3[] = "c";
-const char kAuction4[] = "d";
-const char kAuction5[] = "e";
-
 using BundleSubresourceInfo = SubresourceUrlBuilder::BundleSubresourceInfo;
 
 constexpr char kBuyer1OriginStr[] = "https://origin.test";
@@ -747,7 +739,7 @@
   std::unique_ptr<AuctionWorkletManager::WorkletHandle> handle;
   base::test::TestFuture<void> worklet_available;
   auction_worklet_manager_->RequestBidderWorklet(
-      kAuction1, kDecisionLogicUrl, kWasmUrl, kTrustedSignalsUrl,
+      kDecisionLogicUrl, kWasmUrl, kTrustedSignalsUrl,
       /*needs_cors_for_additional_bid=*/false,
       /*experiment_group_id=*/std::nullopt,
       /*trusted_bidding_signals_slot_size_param=*/"",
@@ -755,8 +747,6 @@
       handle);
   ASSERT_TRUE(worklet_available.Wait());
   EXPECT_TRUE(handle->GetBidderWorklet());
-  EXPECT_THAT(handle->GetDevtoolsAuctionIdsForTesting(),
-              UnorderedElementsAre(kAuction1));
   handle->AuthorizeSubresourceUrls(kPopulatedSubresourceBuilder);
 
   std::unique_ptr<MockBidderWorklet> bidder_worklet =
@@ -798,13 +788,11 @@
   std::unique_ptr<AuctionWorkletManager::WorkletHandle> handle;
   base::test::TestFuture<void> worklet_available;
   auction_worklet_manager_->RequestSellerWorklet(
-      kAuction1, kDecisionLogicUrl, kTrustedSignalsUrl,
+      kDecisionLogicUrl, kTrustedSignalsUrl,
       /*experiment_group_id=*/std::nullopt, worklet_available.GetCallback(),
       NeverInvokedFatalErrorCallback(), handle);
   ASSERT_TRUE(worklet_available.Wait());
   EXPECT_TRUE(handle->GetSellerWorklet());
-  EXPECT_THAT(handle->GetDevtoolsAuctionIdsForTesting(),
-              UnorderedElementsAre(kAuction1));
   handle->AuthorizeSubresourceUrls(kPopulatedSubresourceBuilder);
 
   std::unique_ptr<MockSellerWorklet> seller_worklet =
@@ -846,7 +834,7 @@
   std::unique_ptr<AuctionWorkletManager::WorkletHandle> handle;
   base::test::TestFuture<void> worklet_available;
   auction_worklet_manager_->RequestBidderWorklet(
-      kAuction1, kDecisionLogicUrl, kWasmUrl, kTrustedSignalsUrl,
+      kDecisionLogicUrl, kWasmUrl, kTrustedSignalsUrl,
       /*needs_cors_for_additional_bid=*/false,
       /*experiment_group_id=*/std::nullopt,
       /*trusted_bidding_signals_slot_size_param=*/"",
@@ -868,7 +856,7 @@
   std::unique_ptr<AuctionWorkletManager::WorkletHandle> handle;
   base::test::TestFuture<void> worklet_available;
   auction_worklet_manager_->RequestSellerWorklet(
-      kAuction1, kDecisionLogicUrl, kTrustedSignalsUrl,
+      kDecisionLogicUrl, kTrustedSignalsUrl,
       /*experiment_group_id=*/std::nullopt, worklet_available.GetCallback(),
       NeverInvokedFatalErrorCallback(), handle);
   ASSERT_TRUE(worklet_available.Wait());
@@ -902,7 +890,7 @@
     std::unique_ptr<AuctionWorkletManager::WorkletHandle> handle;
     base::test::TestFuture<void> worklet_available;
     auction_worklet_manager_->RequestBidderWorklet(
-        kAuction1, decision_logic_url, /*wasm_url=*/std::nullopt,
+        decision_logic_url, /*wasm_url=*/std::nullopt,
         /*trusted_bidding_signals_url=*/std::nullopt,
         /*needs_cors_for_additional_bid=*/false,
         /*experiment_group_id=*/std::nullopt,
@@ -938,7 +926,7 @@
   base::test::TestFuture<void> worklet_available2;
   std::unique_ptr<AuctionWorkletManager::WorkletHandle> handle;
   auction_worklet_manager_->RequestBidderWorklet(
-      kAuction1, kDecisionLogicUrl, kWasmUrl, kTrustedSignalsUrl,
+      kDecisionLogicUrl, kWasmUrl, kTrustedSignalsUrl,
       /*needs_cors_for_additional_bid=*/false,
       /*experiment_group_id=*/std::nullopt,
       /*trusted_bidding_signals_slot_size_param=*/"",
@@ -992,8 +980,7 @@
     std::unique_ptr<AuctionWorkletManager::WorkletHandle> handle;
     base::test::TestFuture<void> worklet_available;
     auction_worklet_manager_->RequestSellerWorklet(
-        kAuction1, decision_logic_url,
-        /*trusted_scoring_signals_url=*/std::nullopt,
+        decision_logic_url, /*trusted_scoring_signals_url=*/std::nullopt,
         /*experiment_group_id=*/std::nullopt, worklet_available.GetCallback(),
         NeverInvokedFatalErrorCallback(), handle);
     ASSERT_TRUE(worklet_available.Wait());
@@ -1024,7 +1011,7 @@
   base::RunLoop worklet_available_loop;
   std::unique_ptr<AuctionWorkletManager::WorkletHandle> handle;
   auction_worklet_manager_->RequestSellerWorklet(
-      kAuction1, kDecisionLogicUrl, kTrustedSignalsUrl,
+      kDecisionLogicUrl, kTrustedSignalsUrl,
       /*experiment_group_id=*/std::nullopt,
       worklet_available_loop.QuitClosure(), NeverInvokedFatalErrorCallback(),
       handle);
@@ -1061,7 +1048,7 @@
   std::unique_ptr<AuctionWorkletManager::WorkletHandle> handle1;
   base::test::TestFuture<void> worklet_available1;
   auction_worklet_manager_->RequestBidderWorklet(
-      kAuction1, kDecisionLogicUrl, kWasmUrl, kTrustedSignalsUrl,
+      kDecisionLogicUrl, kWasmUrl, kTrustedSignalsUrl,
       /*needs_cors_for_additional_bid=*/false,
       /*experiment_group_id=*/std::nullopt,
       /*trusted_bidding_signals_slot_size_param=*/"",
@@ -1079,15 +1066,13 @@
   bidder_worklet1->WaitForSendPendingSignalsRequests(1);
   // Should only be one process.
   EXPECT_EQ(1u, auction_process_manager_.GetBidderProcessCountForTesting());
-  EXPECT_THAT(handle1->GetDevtoolsAuctionIdsForTesting(),
-              UnorderedElementsAre(kAuction1));
 
   // Load a bidder worklet with the same parameters. The worklet should be
   // reused.
   std::unique_ptr<AuctionWorkletManager::WorkletHandle> handle2;
   base::test::TestFuture<void> worklet_available2;
   auction_worklet_manager_->RequestBidderWorklet(
-      kAuction2, kDecisionLogicUrl, kWasmUrl, kTrustedSignalsUrl,
+      kDecisionLogicUrl, kWasmUrl, kTrustedSignalsUrl,
       /*needs_cors_for_additional_bid=*/false,
       /*experiment_group_id=*/std::nullopt,
       /*trusted_bidding_signals_slot_size_param=*/"",
@@ -1100,24 +1085,18 @@
   bidder_worklet1->WaitForSendPendingSignalsRequests(2);
   // Should still only be one process.
   EXPECT_EQ(1u, auction_process_manager_.GetBidderProcessCountForTesting());
-  // ... but used by both auctions.
-  EXPECT_THAT(handle2->GetDevtoolsAuctionIdsForTesting(),
-              UnorderedElementsAre(kAuction1, kAuction2));
 
   // Close original handle. Worklet should still be alive, and so should its
   // process.
   handle1.reset();
   EXPECT_EQ(1u, auction_process_manager_.GetBidderProcessCountForTesting());
-  // We should no longer attribute its work to the first auction, however.
-  EXPECT_THAT(handle2->GetDevtoolsAuctionIdsForTesting(),
-              UnorderedElementsAre(kAuction2));
 
   // Load a bidder worklet with the same parameters. The worklet should still be
   // reused again.
   std::unique_ptr<AuctionWorkletManager::WorkletHandle> handle3;
   base::test::TestFuture<void> worklet_available3;
   auction_worklet_manager_->RequestBidderWorklet(
-      kAuction3, kDecisionLogicUrl, kWasmUrl, kTrustedSignalsUrl,
+      kDecisionLogicUrl, kWasmUrl, kTrustedSignalsUrl,
       /*needs_cors_for_additional_bid=*/false,
       /*experiment_group_id=*/std::nullopt,
       /*trusted_bidding_signals_slot_size_param=*/"",
@@ -1130,8 +1109,6 @@
   bidder_worklet1->WaitForSendPendingSignalsRequests(3);
   // Should still only be one process.
   EXPECT_EQ(1u, auction_process_manager_.GetBidderProcessCountForTesting());
-  EXPECT_THAT(handle3->GetDevtoolsAuctionIdsForTesting(),
-              UnorderedElementsAre(kAuction2, kAuction3));
 
   // Close both remaining handles.
   handle2.reset();
@@ -1145,7 +1122,7 @@
   base::test::TestFuture<void> worklet_available4;
   std::unique_ptr<AuctionWorkletManager::WorkletHandle> handle4;
   auction_worklet_manager_->RequestBidderWorklet(
-      kAuction4, kDecisionLogicUrl, kWasmUrl, kTrustedSignalsUrl,
+      kDecisionLogicUrl, kWasmUrl, kTrustedSignalsUrl,
       /*needs_cors_for_additional_bid=*/false,
       /*experiment_group_id=*/std::nullopt,
       /*trusted_bidding_signals_slot_size_param=*/"",
@@ -1162,8 +1139,6 @@
   handle4->GetBidderWorklet()->SendPendingSignalsRequests();
   bidder_worklet2->WaitForSendPendingSignalsRequests(1);
   EXPECT_EQ(1u, auction_process_manager_.GetBidderProcessCountForTesting());
-  EXPECT_THAT(handle4->GetDevtoolsAuctionIdsForTesting(),
-              UnorderedElementsAre(kAuction4));
 }
 
 // Test that requests with the same parameters reuse seller worklets.
@@ -1172,7 +1147,7 @@
   std::unique_ptr<AuctionWorkletManager::WorkletHandle> handle1;
   base::test::TestFuture<void> worklet_available1;
   auction_worklet_manager_->RequestSellerWorklet(
-      kAuction1, kDecisionLogicUrl, kTrustedSignalsUrl,
+      kDecisionLogicUrl, kTrustedSignalsUrl,
       /*experiment_group_id=*/std::nullopt, worklet_available1.GetCallback(),
       NeverInvokedFatalErrorCallback(), handle1);
   ASSERT_TRUE(worklet_available1.Wait());
@@ -1186,15 +1161,13 @@
   seller_worklet1->WaitForSendPendingSignalsRequests(1);
   // Should only be one process.
   EXPECT_EQ(1u, auction_process_manager_.GetSellerProcessCountForTesting());
-  EXPECT_THAT(handle1->GetDevtoolsAuctionIdsForTesting(),
-              UnorderedElementsAre(kAuction1));
 
   // Load a seller worklet with the same parameters. The worklet should be
   // reused.
   std::unique_ptr<AuctionWorkletManager::WorkletHandle> handle2;
   base::test::TestFuture<void> worklet_available2;
   auction_worklet_manager_->RequestSellerWorklet(
-      kAuction2, kDecisionLogicUrl, kTrustedSignalsUrl,
+      kDecisionLogicUrl, kTrustedSignalsUrl,
       /*experiment_group_id=*/std::nullopt, worklet_available2.GetCallback(),
       NeverInvokedFatalErrorCallback(), handle2);
   ASSERT_TRUE(worklet_available2.Wait());
@@ -1204,24 +1177,18 @@
   seller_worklet1->WaitForSendPendingSignalsRequests(2);
   // Should still only be one process.
   EXPECT_EQ(1u, auction_process_manager_.GetSellerProcessCountForTesting());
-  // ... but used by both auctions.
-  EXPECT_THAT(handle2->GetDevtoolsAuctionIdsForTesting(),
-              UnorderedElementsAre(kAuction1, kAuction2));
 
   // Close original handle. Worklet should still be alive, and so should its
   // process.
   handle1.reset();
   EXPECT_EQ(1u, auction_process_manager_.GetSellerProcessCountForTesting());
-  // We should no longer attribute its work to the first auction, however.
-  EXPECT_THAT(handle2->GetDevtoolsAuctionIdsForTesting(),
-              UnorderedElementsAre(kAuction2));
 
   // Load a seller worklet with the same parameters. The worklet should still be
   // reused again.
   std::unique_ptr<AuctionWorkletManager::WorkletHandle> handle3;
   base::test::TestFuture<void> worklet_available3;
   auction_worklet_manager_->RequestSellerWorklet(
-      kAuction3, kDecisionLogicUrl, kTrustedSignalsUrl,
+      kDecisionLogicUrl, kTrustedSignalsUrl,
       /*experiment_group_id=*/std::nullopt, worklet_available3.GetCallback(),
       NeverInvokedFatalErrorCallback(), handle3);
   ASSERT_TRUE(worklet_available3.Wait());
@@ -1231,8 +1198,6 @@
   seller_worklet1->WaitForSendPendingSignalsRequests(3);
   // Should still only be one process.
   EXPECT_EQ(1u, auction_process_manager_.GetSellerProcessCountForTesting());
-  EXPECT_THAT(handle2->GetDevtoolsAuctionIdsForTesting(),
-              UnorderedElementsAre(kAuction2, kAuction3));
 
   // Close both remaining handles.
   handle2.reset();
@@ -1246,7 +1211,7 @@
   std::unique_ptr<AuctionWorkletManager::WorkletHandle> handle4;
   base::test::TestFuture<void> worklet_available4;
   auction_worklet_manager_->RequestSellerWorklet(
-      kAuction4, kDecisionLogicUrl, kTrustedSignalsUrl,
+      kDecisionLogicUrl, kTrustedSignalsUrl,
       /*experiment_group_id=*/std::nullopt, worklet_available4.GetCallback(),
       NeverInvokedFatalErrorCallback(), handle4);
   ASSERT_TRUE(worklet_available4.Wait());
@@ -1260,8 +1225,6 @@
   handle4->GetSellerWorklet()->SendPendingSignalsRequests();
   seller_worklet2->WaitForSendPendingSignalsRequests(1);
   EXPECT_EQ(1u, auction_process_manager_.GetSellerProcessCountForTesting());
-  EXPECT_THAT(handle4->GetDevtoolsAuctionIdsForTesting(),
-              UnorderedElementsAre(kAuction4));
 }
 
 // Make sure that worklets are not reused when parameters don't match.
@@ -1270,7 +1233,7 @@
   std::unique_ptr<AuctionWorkletManager::WorkletHandle> handle1;
   base::test::TestFuture<void> worklet_available1;
   auction_worklet_manager_->RequestBidderWorklet(
-      kAuction1, kDecisionLogicUrl, kWasmUrl, kTrustedSignalsUrl,
+      kDecisionLogicUrl, kWasmUrl, kTrustedSignalsUrl,
       /*needs_cors_for_additional_bid=*/false,
       /*experiment_group_id=*/std::nullopt,
       /*trusted_bidding_signals_slot_size_param=*/"",
@@ -1286,8 +1249,6 @@
   EXPECT_EQ(kTopWindowOrigin, bidder_worklet1->top_window_origin());
   // Should only be one process.
   EXPECT_EQ(1u, auction_process_manager_.GetBidderProcessCountForTesting());
-  EXPECT_THAT(handle1->GetDevtoolsAuctionIdsForTesting(),
-              UnorderedElementsAre(kAuction1));
 
   // Load a bidder worklet with a different decision logic URL. A new worklet
   // should be created, using the same process.
@@ -1296,7 +1257,7 @@
   std::unique_ptr<AuctionWorkletManager::WorkletHandle> handle2;
   base::test::TestFuture<void> worklet_available2;
   auction_worklet_manager_->RequestBidderWorklet(
-      kAuction2, kDifferentDecisionLogicUrl, kWasmUrl, kTrustedSignalsUrl,
+      kDifferentDecisionLogicUrl, kWasmUrl, kTrustedSignalsUrl,
       /*needs_cors_for_additional_bid=*/false,
       /*experiment_group_id=*/std::nullopt,
       /*trusted_bidding_signals_slot_size_param=*/"",
@@ -1313,15 +1274,13 @@
   EXPECT_EQ(kTopWindowOrigin, bidder_worklet2->top_window_origin());
   // Should still only be one process.
   EXPECT_EQ(1u, auction_process_manager_.GetBidderProcessCountForTesting());
-  EXPECT_THAT(handle2->GetDevtoolsAuctionIdsForTesting(),
-              UnorderedElementsAre(kAuction2));
 
   // Load a bidder worklet with a different (null) trusted signals URL. A new
   // worklet should be created, using the same process.
   std::unique_ptr<AuctionWorkletManager::WorkletHandle> handle3;
   base::test::TestFuture<void> worklet_available3;
   auction_worklet_manager_->RequestBidderWorklet(
-      kAuction3, kDecisionLogicUrl, kWasmUrl,
+      kDecisionLogicUrl, kWasmUrl,
       /*trusted_bidding_signals_url=*/std::nullopt,
       /*needs_cors_for_additional_bid=*/false,
       /*experiment_group_id=*/std::nullopt,
@@ -1340,16 +1299,13 @@
   EXPECT_EQ(kTopWindowOrigin, bidder_worklet3->top_window_origin());
   // Should still only be one process.
   EXPECT_EQ(1u, auction_process_manager_.GetBidderProcessCountForTesting());
-  EXPECT_THAT(handle3->GetDevtoolsAuctionIdsForTesting(),
-              UnorderedElementsAre(kAuction3));
 
   // Load a bidder worklet with a different (null) wasm helper URL. A new
   // worklet should be created, using the same process.
   std::unique_ptr<AuctionWorkletManager::WorkletHandle> handle4;
   base::test::TestFuture<void> worklet_available4;
   auction_worklet_manager_->RequestBidderWorklet(
-      kAuction4, kDecisionLogicUrl, /*wasm_url=*/std::nullopt,
-      kTrustedSignalsUrl,
+      kDecisionLogicUrl, /*wasm_url=*/std::nullopt, kTrustedSignalsUrl,
       /*needs_cors_for_additional_bid=*/false,
       /*experiment_group_id=*/std::nullopt,
       /*trusted_bidding_signals_slot_size_param=*/"",
@@ -1367,8 +1323,6 @@
   EXPECT_EQ(kTopWindowOrigin, bidder_worklet4->top_window_origin());
   // Should still only be one process.
   EXPECT_EQ(1u, auction_process_manager_.GetBidderProcessCountForTesting());
-  EXPECT_THAT(handle4->GetDevtoolsAuctionIdsForTesting(),
-              UnorderedElementsAre(kAuction4));
 }
 
 // Test bidder worklet matching with different experiment IDs.
@@ -1379,7 +1333,7 @@
   base::test::TestFuture<void> worklet_available1;
   std::unique_ptr<AuctionWorkletManager::WorkletHandle> handle1;
   auction_worklet_manager_->RequestBidderWorklet(
-      kAuction1, kDecisionLogicUrl, kWasmUrl, kTrustedSignalsUrl,
+      kDecisionLogicUrl, kWasmUrl, kTrustedSignalsUrl,
       /*needs_cors_for_additional_bid=*/false, kExperiment1,
       /*trusted_bidding_signals_slot_size_param=*/"",
       worklet_available1.GetCallback(), NeverInvokedFatalErrorCallback(),
@@ -1394,7 +1348,7 @@
   std::unique_ptr<AuctionWorkletManager::WorkletHandle> handle2;
   base::test::TestFuture<void> worklet_available2;
   auction_worklet_manager_->RequestBidderWorklet(
-      kAuction2, kDecisionLogicUrl, kWasmUrl, kTrustedSignalsUrl,
+      kDecisionLogicUrl, kWasmUrl, kTrustedSignalsUrl,
       /*needs_cors_for_additional_bid=*/false, kExperiment2,
       /*trusted_bidding_signals_slot_size_param=*/"",
       worklet_available2.GetCallback(), NeverInvokedFatalErrorCallback(),
@@ -1409,7 +1363,7 @@
   std::unique_ptr<AuctionWorkletManager::WorkletHandle> handle3;
   base::test::TestFuture<void> worklet_available3;
   auction_worklet_manager_->RequestBidderWorklet(
-      kAuction3, kDecisionLogicUrl, kWasmUrl, kWasmUrl,
+      kDecisionLogicUrl, kWasmUrl, kWasmUrl,
       /*needs_cors_for_additional_bid=*/false, kExperiment1,
       /*trusted_bidding_signals_slot_size_param=*/"",
       worklet_available3.GetCallback(), NeverInvokedFatalErrorCallback(),
@@ -1426,7 +1380,7 @@
   std::unique_ptr<AuctionWorkletManager::WorkletHandle> handle4;
   base::test::TestFuture<void> worklet_available4;
   auction_worklet_manager_->RequestBidderWorklet(
-      kAuction4, kDecisionLogicUrl, kWasmUrl,
+      kDecisionLogicUrl, kWasmUrl,
       /*trusted_bidding_signals_url=*/std::nullopt,
       /*needs_cors_for_additional_bid=*/false, kExperiment1,
       /*trusted_bidding_signals_slot_size_param=*/"",
@@ -1443,7 +1397,7 @@
   std::unique_ptr<AuctionWorkletManager::WorkletHandle> handle5;
   base::test::TestFuture<void> worklet_available5;
   auction_worklet_manager_->RequestBidderWorklet(
-      kAuction5, kDecisionLogicUrl, kWasmUrl,
+      kDecisionLogicUrl, kWasmUrl,
       /*trusted_bidding_signals_url=*/std::nullopt,
       /*needs_cors_for_additional_bid=*/false,
       /*experiment_group_id=*/std::nullopt,
@@ -1453,8 +1407,6 @@
   ASSERT_TRUE(worklet_available5.Wait());
   EXPECT_TRUE(handle5->GetBidderWorklet());
   EXPECT_EQ(handle5->GetBidderWorklet(), handle4->GetBidderWorklet());
-  EXPECT_THAT(handle4->GetDevtoolsAuctionIdsForTesting(),
-              UnorderedElementsAre(kAuction4, kAuction5));
 }
 
 // Test bidder worklet matching with different CORS mode.
@@ -1462,7 +1414,7 @@
   base::test::TestFuture<void> worklet_available1;
   std::unique_ptr<AuctionWorkletManager::WorkletHandle> handle1;
   auction_worklet_manager_->RequestBidderWorklet(
-      kAuction1, kDecisionLogicUrl, kWasmUrl, kTrustedSignalsUrl,
+      kDecisionLogicUrl, kWasmUrl, kTrustedSignalsUrl,
       /*needs_cors_for_additional_bid=*/false,
       /*experiment_group_id=*/std::nullopt,
       /*trusted_bidding_signals_slot_size_param=*/"",
@@ -1478,7 +1430,7 @@
   std::unique_ptr<AuctionWorkletManager::WorkletHandle> handle2;
   base::test::TestFuture<void> worklet_available2;
   auction_worklet_manager_->RequestBidderWorklet(
-      kAuction1, kDecisionLogicUrl, kWasmUrl, kTrustedSignalsUrl,
+      kDecisionLogicUrl, kWasmUrl, kTrustedSignalsUrl,
       /*needs_cors_for_additional_bid=*/true,
       /*experiment_group_id=*/std::nullopt,
       /*trusted_bidding_signals_slot_size_param=*/"",
@@ -1497,7 +1449,7 @@
   std::unique_ptr<AuctionWorkletManager::WorkletHandle> handle1;
   base::test::TestFuture<void> worklet_available1;
   auction_worklet_manager_->RequestSellerWorklet(
-      kAuction1, kDecisionLogicUrl, kTrustedSignalsUrl,
+      kDecisionLogicUrl, kTrustedSignalsUrl,
       /*experiment_group_id=*/std::nullopt, worklet_available1.GetCallback(),
       NeverInvokedFatalErrorCallback(), handle1);
   ASSERT_TRUE(worklet_available1.Wait());
@@ -1517,7 +1469,7 @@
   std::unique_ptr<AuctionWorkletManager::WorkletHandle> handle2;
   base::test::TestFuture<void> worklet_available2;
   auction_worklet_manager_->RequestSellerWorklet(
-      kAuction1, kDifferentDecisionLogicUrl, kTrustedSignalsUrl,
+      kDifferentDecisionLogicUrl, kTrustedSignalsUrl,
       /*experiment_group_id=*/std::nullopt, worklet_available2.GetCallback(),
       NeverInvokedFatalErrorCallback(), handle2);
   ASSERT_TRUE(worklet_available2.Wait());
@@ -1536,8 +1488,7 @@
   std::unique_ptr<AuctionWorkletManager::WorkletHandle> handle3;
   base::test::TestFuture<void> worklet_available3;
   auction_worklet_manager_->RequestSellerWorklet(
-      kAuction1, kDecisionLogicUrl,
-      /*trusted_scoring_signals_url=*/std::nullopt,
+      kDecisionLogicUrl, /*trusted_scoring_signals_url=*/std::nullopt,
       /*experiment_group_id=*/std::nullopt, worklet_available3.GetCallback(),
       NeverInvokedFatalErrorCallback(), handle3);
   ASSERT_TRUE(worklet_available3.Wait());
@@ -1561,7 +1512,7 @@
   std::unique_ptr<AuctionWorkletManager::WorkletHandle> handle1;
   base::test::TestFuture<void> worklet_available1;
   auction_worklet_manager_->RequestSellerWorklet(
-      kAuction1, kDecisionLogicUrl, kTrustedSignalsUrl, kExperiment1,
+      kDecisionLogicUrl, kTrustedSignalsUrl, kExperiment1,
       worklet_available1.GetCallback(), NeverInvokedFatalErrorCallback(),
       handle1);
   ASSERT_TRUE(worklet_available1.Wait());
@@ -1574,7 +1525,7 @@
   std::unique_ptr<AuctionWorkletManager::WorkletHandle> handle2;
   base::test::TestFuture<void> worklet_available2;
   auction_worklet_manager_->RequestSellerWorklet(
-      kAuction1, kDecisionLogicUrl, kTrustedSignalsUrl, kExperiment2,
+      kDecisionLogicUrl, kTrustedSignalsUrl, kExperiment2,
       worklet_available2.GetCallback(), NeverInvokedFatalErrorCallback(),
       handle2);
   ASSERT_TRUE(worklet_available2.Wait());
@@ -1587,7 +1538,7 @@
   std::unique_ptr<AuctionWorkletManager::WorkletHandle> handle3;
   base::test::TestFuture<void> worklet_available3;
   auction_worklet_manager_->RequestSellerWorklet(
-      kAuction1, kDecisionLogicUrl, kWasmUrl, kExperiment1,
+      kDecisionLogicUrl, kWasmUrl, kExperiment1,
       worklet_available3.GetCallback(), NeverInvokedFatalErrorCallback(),
       handle3);
   ASSERT_TRUE(worklet_available3.Wait());
@@ -1603,10 +1554,9 @@
   std::unique_ptr<AuctionWorkletManager::WorkletHandle> handle4;
   base::test::TestFuture<void> worklet_available4;
   auction_worklet_manager_->RequestSellerWorklet(
-      kAuction1, kDecisionLogicUrl,
-      /*trusted_scoring_signals_url=*/std::nullopt, kExperiment1,
-      worklet_available4.GetCallback(), NeverInvokedFatalErrorCallback(),
-      handle4);
+      kDecisionLogicUrl, /*trusted_scoring_signals_url=*/std::nullopt,
+      kExperiment1, worklet_available4.GetCallback(),
+      NeverInvokedFatalErrorCallback(), handle4);
   ASSERT_TRUE(worklet_available4.Wait());
   EXPECT_TRUE(handle4->GetSellerWorklet());
   std::unique_ptr<MockSellerWorklet> seller_worklet4 =
@@ -1618,8 +1568,7 @@
   std::unique_ptr<AuctionWorkletManager::WorkletHandle> handle5;
   base::test::TestFuture<void> worklet_available5;
   auction_worklet_manager_->RequestSellerWorklet(
-      kAuction1, kDecisionLogicUrl,
-      /*trusted_scoring_signals_url=*/std::nullopt,
+      kDecisionLogicUrl, /*trusted_scoring_signals_url=*/std::nullopt,
       /*experiment_group_id=*/std::nullopt, worklet_available5.GetCallback(),
       NeverInvokedFatalErrorCallback(), handle5);
   ASSERT_TRUE(worklet_available5.Wait());
@@ -1640,7 +1589,7 @@
   std::unique_ptr<AuctionWorkletManager::WorkletHandle> handle;
   base::test::TestFuture<void> worklet_available;
   auction_worklet_manager_->RequestBidderWorklet(
-      kAuction1, kDecisionLogicUrl, kWasmUrl, kTrustedSignalsUrl,
+      kDecisionLogicUrl, kWasmUrl, kTrustedSignalsUrl,
       /*needs_cors_for_additional_bid=*/false,
       /*experiment_group_id=*/std::nullopt,
       /*trusted_bidding_signals_slot_size_param=*/"",
@@ -1670,7 +1619,7 @@
   std::unique_ptr<AuctionWorkletManager::WorkletHandle> handle2;
   base::test::TestFuture<void> worklet_available2;
   auction_worklet_manager_->RequestBidderWorklet(
-      kAuction2, kDecisionLogicUrl, kWasmUrl, kTrustedSignalsUrl,
+      kDecisionLogicUrl, kWasmUrl, kTrustedSignalsUrl,
       /*needs_cors_for_additional_bid=*/false,
       /*experiment_group_id=*/std::nullopt,
       /*trusted_bidding_signals_slot_size_param=*/"",
@@ -1680,8 +1629,6 @@
   EXPECT_NE(handle->GetBidderWorklet(), handle2->GetBidderWorklet());
   std::unique_ptr<MockBidderWorklet> bidder_worklet2 =
       auction_process_manager_.WaitForBidderWorklet();
-  EXPECT_THAT(handle2->GetDevtoolsAuctionIdsForTesting(),
-              UnorderedElementsAre(kAuction2));
 }
 
 // Make sure that errors that occur when some worklets haven't gotten
@@ -1702,7 +1649,7 @@
   for (size_t i = 0; i < kNumWorklets; ++i) {
     handles.emplace_back();
     auction_worklet_manager_->RequestBidderWorklet(
-        kAuction1, kDecisionLogicUrl, kWasmUrl, kTrustedSignalsUrl,
+        kDecisionLogicUrl, kWasmUrl, kTrustedSignalsUrl,
         /*needs_cors_for_additional_bid=*/false,
         /*experiment_group_id=*/std::nullopt,
         /*trusted_bidding_signals_slot_size_param=*/"",
@@ -1748,7 +1695,7 @@
   for (size_t i = 0; i < kNumWorklets; ++i) {
     handles.emplace_back();
     auction_worklet_manager_->RequestBidderWorklet(
-        kAuction1, kDecisionLogicUrl, kWasmUrl, kTrustedSignalsUrl,
+        kDecisionLogicUrl, kWasmUrl, kTrustedSignalsUrl,
         /*needs_cors_for_additional_bid=*/false,
         /*experiment_group_id=*/std::nullopt,
         /*trusted_bidding_signals_slot_size_param=*/"",
@@ -1781,7 +1728,7 @@
   std::unique_ptr<AuctionWorkletManager::WorkletHandle> handle;
   base::test::TestFuture<void> worklet_available;
   auction_worklet_manager_->RequestSellerWorklet(
-      kAuction1, kDecisionLogicUrl, kTrustedSignalsUrl,
+      kDecisionLogicUrl, kTrustedSignalsUrl,
       /*experiment_group_id=*/std::nullopt, worklet_available.GetCallback(),
       load_error_helper.Callback(), handle);
   ASSERT_TRUE(worklet_available.Wait());
@@ -1809,7 +1756,7 @@
   std::unique_ptr<AuctionWorkletManager::WorkletHandle> handle2;
   base::test::TestFuture<void> worklet_available2;
   auction_worklet_manager_->RequestSellerWorklet(
-      kAuction1, kDecisionLogicUrl, kTrustedSignalsUrl,
+      kDecisionLogicUrl, kTrustedSignalsUrl,
       /*experiment_group_id=*/std::nullopt, worklet_available2.GetCallback(),
       load_error_helper.Callback(), handle2);
   ASSERT_TRUE(worklet_available2.Wait());
@@ -1825,7 +1772,7 @@
   std::unique_ptr<AuctionWorkletManager::WorkletHandle> handle;
   base::test::TestFuture<void> worklet_available;
   auction_worklet_manager_->RequestBidderWorklet(
-      kAuction1, kDecisionLogicUrl, kWasmUrl, kTrustedSignalsUrl,
+      kDecisionLogicUrl, kWasmUrl, kTrustedSignalsUrl,
       /*needs_cors_for_additional_bid=*/false,
       /*experiment_group_id=*/std::nullopt,
       /*trusted_bidding_signals_slot_size_param=*/"",
@@ -1857,7 +1804,7 @@
   std::unique_ptr<AuctionWorkletManager::WorkletHandle> handle2;
   base::test::TestFuture<void> worklet_available2;
   auction_worklet_manager_->RequestBidderWorklet(
-      kAuction1, kDecisionLogicUrl, kWasmUrl, kTrustedSignalsUrl,
+      kDecisionLogicUrl, kWasmUrl, kTrustedSignalsUrl,
       /*needs_cors_for_additional_bid=*/false,
       /*experiment_group_id=*/std::nullopt,
       /*trusted_bidding_signals_slot_size_param=*/"",
@@ -1875,7 +1822,7 @@
   std::unique_ptr<AuctionWorkletManager::WorkletHandle> handle;
   base::test::TestFuture<void> worklet_available;
   auction_worklet_manager_->RequestSellerWorklet(
-      kAuction1, kDecisionLogicUrl, kTrustedSignalsUrl,
+      kDecisionLogicUrl, kTrustedSignalsUrl,
       /*experiment_group_id=*/std::nullopt, worklet_available.GetCallback(),
       load_error_helper.Callback(), handle);
   ASSERT_TRUE(worklet_available.Wait());
@@ -1905,7 +1852,7 @@
   std::unique_ptr<AuctionWorkletManager::WorkletHandle> handle2;
   base::test::TestFuture<void> worklet_available2;
   auction_worklet_manager_->RequestSellerWorklet(
-      kAuction2, kDecisionLogicUrl, kTrustedSignalsUrl,
+      kDecisionLogicUrl, kTrustedSignalsUrl,
       /*experiment_group_id=*/std::nullopt, worklet_available2.GetCallback(),
       load_error_helper.Callback(), handle2);
   ASSERT_TRUE(worklet_available2.Wait());
@@ -1913,8 +1860,6 @@
   EXPECT_NE(handle->GetSellerWorklet(), handle2->GetSellerWorklet());
   std::unique_ptr<MockSellerWorklet> seller_worklet2 =
       auction_process_manager_.WaitForSellerWorklet();
-  EXPECT_THAT(handle2->GetDevtoolsAuctionIdsForTesting(),
-              UnorderedElementsAre(kAuction2));
 }
 
 // Test reentrant deletion of a WorkletHandle on error.
@@ -1926,7 +1871,7 @@
   std::unique_ptr<AuctionWorkletManager::WorkletHandle> handle;
   base::test::TestFuture<void> worklet_available;
   auction_worklet_manager_->RequestBidderWorklet(
-      kAuction1, kDecisionLogicUrl, kWasmUrl, kTrustedSignalsUrl,
+      kDecisionLogicUrl, kWasmUrl, kTrustedSignalsUrl,
       /*needs_cors_for_additional_bid=*/false,
       /*experiment_group_id=*/std::nullopt,
       /*trusted_bidding_signals_slot_size_param=*/"",
@@ -1969,7 +1914,7 @@
   for (int i = 0; i < 10; ++i) {
     handles.emplace_back();
     auction_worklet_manager_->RequestBidderWorklet(
-        kAuction1, kDecisionLogicUrl, kWasmUrl, kTrustedSignalsUrl,
+        kDecisionLogicUrl, kWasmUrl, kTrustedSignalsUrl,
         /*needs_cors_for_additional_bid=*/false,
         /*experiment_group_id=*/std::nullopt,
         /*trusted_bidding_signals_slot_size_param=*/"",
@@ -2024,7 +1969,7 @@
   std::unique_ptr<AuctionWorkletManager::WorkletHandle> handle;
   base::test::TestFuture<void> worklet_available;
   auction_worklet_manager_->RequestSellerWorklet(
-      kAuction1, kDecisionLogicUrl, kTrustedSignalsUrl,
+      kDecisionLogicUrl, kTrustedSignalsUrl,
       /*experiment_group_id=*/std::nullopt, worklet_available.GetCallback(),
       base::BindLambdaForTesting(
           [&](AuctionWorkletManager::FatalErrorType fatal_error_type,
@@ -2052,7 +1997,7 @@
   std::unique_ptr<AuctionWorkletManager::WorkletHandle> handle;
   base::test::TestFuture<void> worklet_available;
   auction_worklet_manager_->RequestBidderWorklet(
-      kAuction1, kDecisionLogicUrl, kWasmUrl, kTrustedSignalsUrl,
+      kDecisionLogicUrl, kWasmUrl, kTrustedSignalsUrl,
       /*needs_cors_for_additional_bid=*/false,
       /*experiment_group_id=*/std::nullopt,
       /*trusted_bidding_signals_slot_size_param=*/"",
@@ -2118,7 +2063,7 @@
   std::unique_ptr<AuctionWorkletManager::WorkletHandle> handle;
   base::test::TestFuture<void> worklet_available;
   auction_worklet_manager_->RequestSellerWorklet(
-      kAuction1, kDecisionLogicUrl, kTrustedSignalsUrl,
+      kDecisionLogicUrl, kTrustedSignalsUrl,
       /*experiment_group_id=*/std::nullopt, worklet_available.GetCallback(),
       NeverInvokedFatalErrorCallback(), handle);
   ASSERT_TRUE(worklet_available.Wait());
diff --git a/content/browser/interest_group/devtools_enums.h b/content/browser/interest_group/devtools_enums.h
index a687d91..6a67be75 100644
--- a/content/browser/interest_group/devtools_enums.h
+++ b/content/browser/interest_group/devtools_enums.h
@@ -9,14 +9,6 @@
 
 enum class InterestGroupAuctionEventType { kStarted, kConfigResolved };
 
-enum class InterestGroupAuctionFetchType {
-  kBidderJs,
-  kBidderWasm,
-  kSellerJs,
-  kBidderTrustedSignals,
-  kSellerTrustedSignals
-};
-
 }  // namespace content
 
 #endif  // CONTENT_BROWSER_INTEREST_GROUP_DEVTOOLS_ENUMS_H_
diff --git a/content/browser/interest_group/interest_group_auction.cc b/content/browser/interest_group/interest_group_auction.cc
index 71adc6a..e146c1e 100644
--- a/content/browser/interest_group/interest_group_auction.cc
+++ b/content/browser/interest_group/interest_group_auction.cc
@@ -1039,7 +1039,7 @@
       auto worklet_key = auction_->BidderWorkletKey(*bid_state);
       auction_->auction_metrics_recorder_->ReportBidderWorkletKey(worklet_key);
       auction_->auction_worklet_manager_->RequestWorkletByKey(
-          worklet_key, auction_->devtools_auction_id_,
+          worklet_key,
           base::BindOnce(&BuyerHelper::OnBidderWorkletReceived,
                          base::Unretained(this), bid_state.get()),
           base::BindOnce(&BuyerHelper::OnBidderWorkletGenerateBidFatalError,
@@ -3863,8 +3863,8 @@
   TRACE_EVENT_NESTABLE_ASYNC_BEGIN0("fledge", "request_seller_worklet",
                                     *trace_id_);
   auction_worklet_manager_->RequestSellerWorklet(
-      devtools_auction_id_, *config_->decision_logic_url,
-      config_->trusted_scoring_signals_url, config_->seller_experiment_group_id,
+      *config_->decision_logic_url, config_->trusted_scoring_signals_url,
+      config_->seller_experiment_group_id,
       base::BindOnce(&InterestGroupAuction::OnSellerWorkletReceived,
                      base::Unretained(this)),
       base::BindOnce(&InterestGroupAuction::OnSellerWorkletFatalError,
diff --git a/content/browser/interest_group/interest_group_auction_reporter.cc b/content/browser/interest_group/interest_group_auction_reporter.cc
index 96a2d05..fcc4921 100644
--- a/content/browser/interest_group/interest_group_auction_reporter.cc
+++ b/content/browser/interest_group/interest_group_auction_reporter.cc
@@ -392,7 +392,7 @@
   // `seller_worklet_handle_` will prevent the callbacks from being invoked, if
   // `this` is destroyed while still waiting on the callbacks.
   auction_worklet_manager_->RequestSellerWorklet(
-      devtools_auction_id_, *seller_info->auction_config->decision_logic_url,
+      *seller_info->auction_config->decision_logic_url,
       seller_info->auction_config->trusted_scoring_signals_url,
       seller_info->auction_config->seller_experiment_group_id,
       base::BindOnce(&InterestGroupAuctionReporter::OnSellerWorkletReceived,
@@ -679,7 +679,7 @@
   // `bidder_worklet_handle_` will prevent the callbacks from being invoked, if
   // `this` is destroyed while still waiting on the callbacks.
   auction_worklet_manager_->RequestBidderWorklet(
-      devtools_auction_id_, interest_group.bidding_url.value_or(GURL()),
+      interest_group.bidding_url.value_or(GURL()),
       interest_group.bidding_wasm_helper_url,
       interest_group.trusted_bidding_signals_url,
       /*needs_cors_for_additional_bid=*/
diff --git a/content/browser/interest_group/interest_group_auction_reporter.h b/content/browser/interest_group/interest_group_auction_reporter.h
index ca636bd..2fd5f1b 100644
--- a/content/browser/interest_group/interest_group_auction_reporter.h
+++ b/content/browser/interest_group/interest_group_auction_reporter.h
@@ -481,7 +481,7 @@
   // SellerWinningBidInfo, it points to an AuctionConfig contained within it.
   const std::unique_ptr<blink::AuctionConfig> auction_config_;
 
-  const std::string devtools_auction_id_;
+  const std::optional<std::string> devtools_auction_id_;
   const url::Origin main_frame_origin_;
   const url::Origin frame_origin_;
   const network::mojom::ClientSecurityStatePtr client_security_state_;
diff --git a/content/browser/media/OWNERS b/content/browser/media/OWNERS
index bb3ab82..c2ee860 100644
--- a/content/browser/media/OWNERS
+++ b/content/browser/media/OWNERS
@@ -3,6 +3,7 @@
 mfoltz@chromium.org
 
 per-file capture_handle_manager*=eladalon@chromium.org
+per-file captured_surface_control*=eladalon@chromium.org
 per-file media_devices_*=guidou@chromium.org
 per-file midi_*=toyoshim@chromium.org
 per-file midi_*=mjwilson@chromium.org
diff --git a/content/browser/renderer_host/media/captured_surface_control_permission_manager.cc b/content/browser/media/captured_surface_control_permission_manager.cc
similarity index 97%
rename from content/browser/renderer_host/media/captured_surface_control_permission_manager.cc
rename to content/browser/media/captured_surface_control_permission_manager.cc
index 90f5404b..120bf4c 100644
--- a/content/browser/renderer_host/media/captured_surface_control_permission_manager.cc
+++ b/content/browser/media/captured_surface_control_permission_manager.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/renderer_host/media/captured_surface_control_permission_manager.h"
+#include "content/browser/media/captured_surface_control_permission_manager.h"
 
 #include "base/command_line.h"
 #include "base/functional/callback.h"
diff --git a/content/browser/renderer_host/media/captured_surface_control_permission_manager.h b/content/browser/media/captured_surface_control_permission_manager.h
similarity index 92%
rename from content/browser/renderer_host/media/captured_surface_control_permission_manager.h
rename to content/browser/media/captured_surface_control_permission_manager.h
index 7871ed3..9fee618 100644
--- a/content/browser/renderer_host/media/captured_surface_control_permission_manager.h
+++ b/content/browser/media/captured_surface_control_permission_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 CONTENT_BROWSER_RENDERER_HOST_MEDIA_CAPTURED_SURFACE_CONTROL_PERMISSION_MANAGER_H_
-#define CONTENT_BROWSER_RENDERER_HOST_MEDIA_CAPTURED_SURFACE_CONTROL_PERMISSION_MANAGER_H_
+#ifndef CONTENT_BROWSER_MEDIA_CAPTURED_SURFACE_CONTROL_PERMISSION_MANAGER_H_
+#define CONTENT_BROWSER_MEDIA_CAPTURED_SURFACE_CONTROL_PERMISSION_MANAGER_H_
 
 #include "base/functional/callback.h"
 #include "base/memory/weak_ptr.h"
@@ -93,4 +93,4 @@
 
 }  // namespace content
 
-#endif  // CONTENT_BROWSER_RENDERER_HOST_MEDIA_CAPTURED_SURFACE_CONTROL_PERMISSION_MANAGER_H_
+#endif  // CONTENT_BROWSER_MEDIA_CAPTURED_SURFACE_CONTROL_PERMISSION_MANAGER_H_
diff --git a/content/browser/renderer_host/media/captured_surface_controller.cc b/content/browser/media/captured_surface_controller.cc
similarity index 65%
rename from content/browser/renderer_host/media/captured_surface_controller.cc
rename to content/browser/media/captured_surface_controller.cc
index 863a20b..bb7cd3565 100644
--- a/content/browser/renderer_host/media/captured_surface_controller.cc
+++ b/content/browser/media/captured_surface_controller.cc
@@ -2,15 +2,16 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "content/browser/renderer_host/media/captured_surface_controller.h"
+#include "content/browser/media/captured_surface_controller.h"
 
 #include <cmath>
 
 #include "base/task/bind_post_task.h"
+#include "content/browser/media/captured_surface_control_permission_manager.h"
 #include "content/browser/media/media_stream_web_contents_observer.h"
-#include "content/browser/renderer_host/media/captured_surface_control_permission_manager.h"
 #include "content/browser/renderer_host/render_frame_host_impl.h"
 #include "content/browser/renderer_host/render_widget_host_impl.h"
+#include "content/browser/web_contents/web_contents_impl.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/global_routing_id.h"
 #include "content/public/browser/host_zoom_map.h"
@@ -33,24 +34,46 @@
     base::OnceCallback<void(std::optional<int> zoom_level,
                             blink::mojom::CapturedSurfaceControlResult result)>;
 
-// Deliver a synthetic MouseWheel action on the tab whose ID is
-// `wc_id`, with the parameters described by the values in `action`.
+base::WeakPtr<WebContents> ResolveWebContentsOnUI(
+    WebContentsMediaCaptureId wc_id) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+  if (wc_id.is_null()) {
+    return nullptr;
+  }
+
+  WebContents* const wc =
+      WebContents::FromRenderFrameHost(RenderFrameHost::FromID(
+          wc_id.render_process_id, wc_id.main_render_frame_id));
+
+  return wc ? wc->GetWeakPtr() : nullptr;
+}
+
+// Deliver a synthetic MouseWheel action on `captured_wc` with the parameters
+// described by the values in `action`.
 //
 // Return `CapturedSurfaceControlResult` to be reported back to the renderer,
 // indicating success or failure (with reason).
-//
-// This function must be invoked on the UI thread with a non-null
-// `WebContentsMediaCaptureId`. Note however that the WebContents in question
-// might have been asynchronously destroyed in the intervening time, which is
-// one possible reason for failure.
 CapturedSurfaceControlResult DoSendWheel(
-    WebContentsMediaCaptureId wc_id,
+    GlobalRenderFrameHostId capturer_rfh_id,
+    base::WeakPtr<WebContents> captured_wc,
     blink::mojom::CapturedWheelActionPtr action) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  CHECK(!wc_id.is_null());
 
-  RenderFrameHostImpl* const rfhi = RenderFrameHostImpl::FromID(
-      wc_id.render_process_id, wc_id.main_render_frame_id);
+  RenderFrameHost* const rfh =
+      captured_wc ? captured_wc->GetPrimaryMainFrame() : nullptr;
+  if (!rfh) {
+    return CapturedSurfaceControlResult::kCapturedSurfaceNotFoundError;
+  }
+
+  if (WebContentsImpl::FromRenderFrameHostID(capturer_rfh_id) ==
+      captured_wc.get()) {
+    // TODO(crbug.com/1466247): Use a dedicated error for self-capture.
+    return CapturedSurfaceControlResult::kUnknownError;
+  }
+
+  RenderFrameHostImpl* const rfhi =
+      RenderFrameHostImpl::FromID(rfh->GetGlobalId());
   RenderWidgetHostImpl* const rwhi =
       rfhi ? rfhi->GetRenderWidgetHost() : nullptr;
   if (!rwhi) {
@@ -93,56 +116,55 @@
   return CapturedSurfaceControlResult::kSuccess;
 }
 
-// Set the zoom level of the tab indicated by `wc_id` to `zoom_level`.
+// Set the zoom level of the tab indicated by `captured_wc` to `zoom_level`.
 //
 // Return `CapturedSurfaceControlResult` to be reported back to the renderer,
 // indicating success or failure (with reason).
-//
-// This function must be invoked on the UI thread with a non-null
-// `WebContentsMediaCaptureId`. Note however that the WebContents in question
-// might have been asynchronously destroyed in the intervening time, which is
-// one possible reason for failure.
-CapturedSurfaceControlResult DoSetZoomLevel(WebContentsMediaCaptureId wc_id,
-                                            int zoom_level) {
+CapturedSurfaceControlResult DoSetZoomLevel(
+    GlobalRenderFrameHostId capturer_rfh_id,
+    base::WeakPtr<WebContents> captured_wc,
+    int zoom_level) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  CHECK(!wc_id.is_null());
 
-  RenderFrameHost* const rfh = RenderFrameHost::FromID(
-      wc_id.render_process_id, wc_id.main_render_frame_id);
-  WebContents* const captured_wc = WebContents::FromRenderFrameHost(rfh);
   if (!captured_wc) {
     return CapturedSurfaceControlResult::kCapturedSurfaceNotFoundError;
   }
+
+  if (WebContentsImpl::FromRenderFrameHostID(capturer_rfh_id) ==
+      captured_wc.get()) {
+    // TODO(crbug.com/1466247): Use a dedicated error for self-capture.
+    return CapturedSurfaceControlResult::kUnknownError;
+  }
+
   content::HostZoomMap::SetZoomLevel(
-      captured_wc,
+      captured_wc.get(),
       blink::PageZoomFactorToZoomLevel(static_cast<double>(zoom_level) / 100));
   return CapturedSurfaceControlResult::kSuccess;
 }
 
-// Get the zoom level of the tab indicated by `wc_id`.
+// Get the zoom level of the tab indicated by `captured_wc`.
 //
 // Return the zoom_level if successful or nullopt otherwise.
-//
-// This function must be invoked on the UI thread with a non-null
-// `WebContentsMediaCaptureId`. Note however that the WebContents in question
-// might have been asynchronously destroyed in the intervening time, which is
-// one possible reason for failure.
 std::pair<std::optional<int>, CapturedSurfaceControlResult> DoGetZoomLevel(
-    WebContentsMediaCaptureId wc_id) {
+    GlobalRenderFrameHostId capturer_rfh_id,
+    base::WeakPtr<WebContents> captured_wc) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  CHECK(!wc_id.is_null());
 
-  WebContents* const captured_wc =
-      WebContents::FromRenderFrameHost(RenderFrameHost::FromID(
-          wc_id.render_process_id, wc_id.main_render_frame_id));
   if (!captured_wc) {
     return std::make_pair(
         std::nullopt,
         CapturedSurfaceControlResult::kCapturedSurfaceNotFoundError);
   }
 
+  if (WebContentsImpl::FromRenderFrameHostID(capturer_rfh_id) ==
+      captured_wc.get()) {
+    // TODO(crbug.com/1466247): Use a dedicated error for self-capture.
+    return std::make_pair(std::nullopt,
+                          CapturedSurfaceControlResult::kUnknownError);
+  }
+
   double zoom_level = blink::PageZoomLevelToZoomFactor(
-      content::HostZoomMap::GetZoomLevel(captured_wc));
+      content::HostZoomMap::GetZoomLevel(captured_wc.get()));
   return std::make_pair(std::round(100 * zoom_level),
                         CapturedSurfaceControlResult::kSuccess);
 }
@@ -196,18 +218,36 @@
 
 }  // namespace
 
+std::unique_ptr<CapturedSurfaceController>
+CapturedSurfaceController::CreateForTesting(
+    GlobalRenderFrameHostId capturer_rfh_id,
+    WebContentsMediaCaptureId captured_wc_id,
+    std::unique_ptr<CapturedSurfaceControlPermissionManager> permission_manager,
+    base::RepeatingCallback<void()> wc_resolution_callback) {
+  return base::WrapUnique(new CapturedSurfaceController(
+      capturer_rfh_id, captured_wc_id, std::move(permission_manager),
+      std::move(wc_resolution_callback)));
+}
+
 CapturedSurfaceController::CapturedSurfaceController(
     GlobalRenderFrameHostId capturer_rfh_id,
     WebContentsMediaCaptureId captured_wc_id)
     : CapturedSurfaceController(
+          capturer_rfh_id,
           captured_wc_id,
-          std::make_unique<PermissionManager>(capturer_rfh_id)) {}
+          std::make_unique<PermissionManager>(capturer_rfh_id),
+          /*wc_resolution_callback=*/base::DoNothing()) {}
 
 CapturedSurfaceController::CapturedSurfaceController(
+    GlobalRenderFrameHostId capturer_rfh_id,
     WebContentsMediaCaptureId captured_wc_id,
-    std::unique_ptr<PermissionManager> permission_manager)
-    : captured_wc_id_(captured_wc_id),
-      permission_manager_(std::move(permission_manager)) {}
+    std::unique_ptr<PermissionManager> permission_manager,
+    base::RepeatingCallback<void()> wc_resolution_callback)
+    : capturer_rfh_id_(capturer_rfh_id),
+      permission_manager_(std::move(permission_manager)),
+      wc_resolution_callback_(std::move(wc_resolution_callback)) {
+  ResolveCapturedWebContents(captured_wc_id);
+}
 
 CapturedSurfaceController::~CapturedSurfaceController() = default;
 
@@ -215,7 +255,7 @@
     WebContentsMediaCaptureId captured_wc_id) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
-  captured_wc_id_ = captured_wc_id;
+  ResolveCapturedWebContents(captured_wc_id);
 }
 
 void CapturedSurfaceController::SendWheel(
@@ -223,7 +263,7 @@
     base::OnceCallback<void(CapturedSurfaceControlResult)> reply_callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
-  if (captured_wc_id_.is_null()) {
+  if (!captured_wc_.has_value()) {
     std::move(reply_callback)
         .Run(CapturedSurfaceControlResult::kCapturedSurfaceNotFoundError);
     return;
@@ -231,7 +271,8 @@
 
   // Action to be performed on the UI thread if permitted.
   base::OnceCallback<CapturedSurfaceControlResult(void)> action_callback =
-      base::BindOnce(&DoSendWheel, captured_wc_id_, std::move(action));
+      base::BindOnce(&DoSendWheel, capturer_rfh_id_, captured_wc_.value(),
+                     std::move(action));
 
   permission_manager_->CheckPermission(
       ComposeCallbacks(std::move(action_callback), std::move(reply_callback)));
@@ -241,7 +282,7 @@
     GetZoomLevelReplyCallback reply_callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
-  if (captured_wc_id_.is_null()) {
+  if (!captured_wc_.has_value()) {
     std::move(reply_callback)
         .Run(std::nullopt,
              CapturedSurfaceControlResult::kCapturedSurfaceNotFoundError);
@@ -249,7 +290,8 @@
   }
 
   GetUIThreadTaskRunner({})->PostTaskAndReplyWithResult(
-      FROM_HERE, base::BindOnce(&DoGetZoomLevel, captured_wc_id_),
+      FROM_HERE,
+      base::BindOnce(&DoGetZoomLevel, capturer_rfh_id_, captured_wc_.value()),
       base::BindOnce(
           [](GetZoomLevelReplyCallback reply_callback,
              std::pair<std::optional<int>, CapturedSurfaceControlResult>
@@ -264,7 +306,7 @@
     base::OnceCallback<void(CapturedSurfaceControlResult)> reply_callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
-  if (captured_wc_id_.is_null()) {
+  if (!captured_wc_.has_value()) {
     std::move(reply_callback)
         .Run(CapturedSurfaceControlResult::kCapturedSurfaceNotFoundError);
     return;
@@ -272,10 +314,34 @@
 
   // Action to be performed on the UI thread if permitted.
   base::OnceCallback<CapturedSurfaceControlResult(void)> action_callback =
-      base::BindOnce(&DoSetZoomLevel, captured_wc_id_, zoom_level);
+      base::BindOnce(&DoSetZoomLevel, capturer_rfh_id_, captured_wc_.value(),
+                     zoom_level);
 
   permission_manager_->CheckPermission(
       ComposeCallbacks(std::move(action_callback), std::move(reply_callback)));
 }
 
+void CapturedSurfaceController::ResolveCapturedWebContents(
+    WebContentsMediaCaptureId captured_wc_id) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+  // Avoid posting new tasks (DoSendWheel/DoSetZoomLevel) with the old target
+  // while pending resolution.
+  captured_wc_ = absl::nullopt;
+
+  GetUIThreadTaskRunner({})->PostTaskAndReplyWithResult(
+      FROM_HERE, base::BindOnce(&ResolveWebContentsOnUI, captured_wc_id),
+      base::BindOnce(&CapturedSurfaceController::OnCapturedWebContentsResolved,
+                     weak_factory_.GetWeakPtr()));
+}
+
+void CapturedSurfaceController::OnCapturedWebContentsResolved(
+    base::WeakPtr<WebContents> captured_wc) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  // Note that `captured_wc_` may be non-nullopt if a second call to
+  // ResolveWebContentsOnUI() was scheduled before the first one resolved.
+  captured_wc_ = captured_wc;
+  wc_resolution_callback_.Run();
+}
+
 }  // namespace content
diff --git a/content/browser/media/captured_surface_controller.h b/content/browser/media/captured_surface_controller.h
new file mode 100644
index 0000000..2bdd0cdf
--- /dev/null
+++ b/content/browser/media/captured_surface_controller.h
@@ -0,0 +1,118 @@
+// 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 CONTENT_BROWSER_MEDIA_CAPTURED_SURFACE_CONTROLLER_H_
+#define CONTENT_BROWSER_MEDIA_CAPTURED_SURFACE_CONTROLLER_H_
+
+#include <memory>
+
+#include "base/functional/callback.h"
+#include "base/memory/weak_ptr.h"
+#include "content/browser/media/captured_surface_control_permission_manager.h"
+#include "content/common/content_export.h"
+#include "content/public/browser/global_routing_id.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_contents_media_capture_id.h"
+#include "third_party/blink/public/mojom/mediastream/media_stream.mojom.h"
+
+namespace content {
+
+// Encapsulates the permission state and logic associated with the Captured
+// Surface Control API. Objects of this class live on the IO thread.
+class CONTENT_EXPORT CapturedSurfaceController {
+ public:
+  using CapturedSurfaceControlResult =
+      ::blink::mojom::CapturedSurfaceControlResult;
+
+  static std::unique_ptr<CapturedSurfaceController> CreateForTesting(
+      GlobalRenderFrameHostId capturer_rfh_id,
+      WebContentsMediaCaptureId captured_wc_id,
+      std::unique_ptr<CapturedSurfaceControlPermissionManager>
+          permission_manager,
+      base::RepeatingCallback<void()> wc_resolution_callback);
+
+  CapturedSurfaceController(GlobalRenderFrameHostId capturer_rfh_id,
+                            WebContentsMediaCaptureId captured_wc_id);
+
+  virtual ~CapturedSurfaceController();
+
+  CapturedSurfaceController(const CapturedSurfaceController&) = delete;
+  CapturedSurfaceController& operator=(const CapturedSurfaceController&) =
+      delete;
+
+  // Set the captured WebContents this controller is associated with.
+  // This may be called with a null `WebContentsMediaCaptureId`.
+  virtual void UpdateCaptureTarget(WebContentsMediaCaptureId captured_wc_id);
+
+  // Produce a wheel event on the captured surface.
+  virtual void SendWheel(
+      blink::mojom::CapturedWheelActionPtr action,
+      base::OnceCallback<void(CapturedSurfaceControlResult)> reply_callback);
+
+  // Get the zoom level of the captured tab.
+  virtual void GetZoomLevel(
+      base::OnceCallback<void(
+          std::optional<int> zoom_level,
+          blink::mojom::CapturedSurfaceControlResult result)> reply_callback);
+
+  // Set the zoom level of the captured tab.
+  virtual void SetZoomLevel(
+      int zoom_level,
+      base::OnceCallback<void(CapturedSurfaceControlResult)> reply_callback);
+
+ private:
+  using PermissionResult =
+      ::content::CapturedSurfaceControlPermissionManager::PermissionResult;
+
+  CapturedSurfaceController(
+      GlobalRenderFrameHostId capturer_rfh_id,
+      WebContentsMediaCaptureId captured_wc_id,
+      std::unique_ptr<CapturedSurfaceControlPermissionManager>
+          permission_manager,
+      base::RepeatingCallback<void()> wc_resolution_callback);
+
+  // Manage the resolution of WebContents-IDs into base::WeakPtr<WebContents>.
+  void ResolveCapturedWebContents(WebContentsMediaCaptureId captured_wc_id);
+  void OnCapturedWebContentsResolved(base::WeakPtr<WebContents> captured_wc);
+
+  const GlobalRenderFrameHostId capturer_rfh_id_;
+
+  // References the captured tab through its WebContents.
+  //
+  // Set to nullopt when:
+  // * The captured surface is not a tab.
+  // * Right after construction, before the ID is first resolved (on the
+  //   UI thread) to a valid base::WeakPtr<WebContents>.
+  // * Whenever the captured tab changes, and UpdateCaptureTarget() is
+  //   called. This triggers a new resolution, and in the intervening time,
+  //   this will be set back to nullptr.
+  //
+  // Set to a concrete value otherwise.
+  // However, this concrete value can be nullptr, (1) as with any WeakPtr,
+  // or (2) if the ID failed to resolve to a valid WebContents.
+  //
+  // Note that `this` lives on the IO thread, and it is not possible to
+  // check the value of the underlying WebContents* here, or even compare
+  // it to nullptr.
+  //
+  // In the unlikely-yet-possible case that SendWheel() or SetZoomLevel()
+  // are called while the task to resolve is pending, those calls will
+  // fail gracefully. Subsequent calls are valid and can succeed.
+  // TODO(crbug.com/1520375): Add UMA to measure how often this happens
+  // and determine whether it's worth the effort to fix.
+  absl::optional<base::WeakPtr<WebContents>> captured_wc_;
+
+  std::unique_ptr<CapturedSurfaceControlPermissionManager> permission_manager_;
+
+  // Callback to be invoked whenever an ID's resolution to a
+  // base::WeakPtr<WebContents> completes. (Successful and unsuccessful
+  // resolutions are treated identically.)
+  const base::RepeatingCallback<void()> wc_resolution_callback_;
+
+  base::WeakPtrFactory<CapturedSurfaceController> weak_factory_{this};
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_MEDIA_CAPTURED_SURFACE_CONTROLLER_H_
diff --git a/content/browser/renderer_host/media/captured_surface_controller_permission_manager_unittest.cc b/content/browser/media/captured_surface_controller_permission_manager_unittest.cc
similarity index 98%
rename from content/browser/renderer_host/media/captured_surface_controller_permission_manager_unittest.cc
rename to content/browser/media/captured_surface_controller_permission_manager_unittest.cc
index 5fa256b..ac0aafea 100644
--- a/content/browser/renderer_host/media/captured_surface_controller_permission_manager_unittest.cc
+++ b/content/browser/media/captured_surface_controller_permission_manager_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "content/browser/renderer_host/media/captured_surface_control_permission_manager.h"
+#include "content/browser/media/captured_surface_control_permission_manager.h"
 
 #include <memory>
 
diff --git a/content/browser/media/captured_surface_controller_unittest.cc b/content/browser/media/captured_surface_controller_unittest.cc
new file mode 100644
index 0000000..5051995
--- /dev/null
+++ b/content/browser/media/captured_surface_controller_unittest.cc
@@ -0,0 +1,763 @@
+// 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 "content/browser/media/captured_surface_controller.h"
+
+#include <list>
+#include <memory>
+
+#include "base/functional/bind.h"
+#include "base/run_loop.h"
+#include "content/browser/media/captured_surface_control_permission_manager.h"
+#include "content/public/browser/global_routing_id.h"
+#include "content/public/browser/host_zoom_map.h"
+#include "content/public/test/browser_task_environment.h"
+#include "content/test/test_web_contents.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/common/page/page_zoom.h"
+#include "third_party/blink/public/mojom/mediastream/media_stream.mojom.h"
+
+namespace content {
+namespace {
+
+using CapturedWheelAction = ::blink::mojom::CapturedWheelAction;
+using CapturedWheelActionPtr = ::blink::mojom::CapturedWheelActionPtr;
+using CSCResult = ::blink::mojom::CapturedSurfaceControlResult;
+using CSCPermissionResult =
+    ::content::CapturedSurfaceControlPermissionManager::PermissionResult;
+
+// Make an arbitrary valid CapturedWheelAction.
+CapturedWheelActionPtr MakeCapturedWheelActionPtr() {
+  return CapturedWheelAction::New(
+      /*x=*/0,
+      /*y=*/0,
+      /*wheel_delta_x=*/0,
+      /*wheel_delta_y=*/0);
+}
+
+class InputObserver : public RenderWidgetHost::InputEventObserver {
+ public:
+  struct ExpectedWheelEvent {
+    double x = 0;
+    double y = 0;
+    double delta_x = 0;
+    double delta_y = 0;
+  };
+
+  ~InputObserver() override { EXPECT_TRUE(expected_events_.empty()); }
+
+  void OnInputEvent(const blink::WebInputEvent& event) override {
+    CHECK_EQ(event.GetType(), blink::WebInputEvent::Type::kMouseWheel);
+
+    const blink::WebMouseWheelEvent& wheel_event =
+        static_cast<const blink::WebMouseWheelEvent&>(event);
+
+    CHECK(!expected_events_.empty());
+    ExpectedWheelEvent expected_event = expected_events_.front();
+    expected_events_.pop_front();
+
+    EXPECT_EQ(expected_event.x, wheel_event.PositionInWidget().x());
+    EXPECT_EQ(expected_event.y, wheel_event.PositionInWidget().y());
+    EXPECT_EQ(expected_event.delta_x, wheel_event.delta_x);
+    EXPECT_EQ(expected_event.delta_y, wheel_event.delta_y);
+  }
+
+  void AddExpectation(ExpectedWheelEvent expected_event) {
+    expected_events_.push_back(expected_event);
+
+    // The wheel event chains are closed with a scroll of zero
+    // magnitude in the same location.
+    expected_events_.push_back(ExpectedWheelEvent{.x = expected_event.x,
+                                                  .y = expected_event.y,
+                                                  .delta_x = 0,
+                                                  .delta_y = 0});
+  }
+
+ private:
+  std::list<ExpectedWheelEvent> expected_events_;
+};
+
+class TestView : public TestRenderWidgetHostView {
+ public:
+  explicit TestView(RenderWidgetHostImpl* rwhi)
+      : TestRenderWidgetHostView(rwhi) {}
+  ~TestView() override = default;
+
+  void SetSize(const gfx::Size& size) override { size_ = size; }
+
+  gfx::Size GetVisibleViewportSize() override { return size_; }
+
+ private:
+  gfx::Size size_;
+};
+
+// Simulates a tab.
+//
+// Wraps a `WebContents`, which is the main object of interest, along with a
+// `TestView`, which is essentially a `RenderWidgetHostView` that allows us to
+// set a custom size, which is needed when testing SendWheel().
+//
+// This object records the original `RenderWidgetHostView` and injects it back
+// from the destructor. This prevents LSAN/ASAN failures. This functionality is
+// the main value proposition of this class.
+class TestTab {
+ public:
+  static constexpr gfx::Size kDefaultViewportSize = gfx::Size(100, 400);
+
+  explicit TestTab(BrowserContext* browser_context)
+      : web_contents_(MakeTestWebContents(browser_context)) {
+    // Store the original RenderWidgetHost, allowing it to be injected back from
+    // the destructor.
+    original_rwhv_ = GetRenderWidgetHostImpl()->GetView();
+
+    // Set a new RenderWidgetHost that allows us control over its size.
+    rwhv_ = std::make_unique<TestView>(GetRenderWidgetHostImpl());
+    SetView(rwhv_.get());
+    SetSize(kDefaultViewportSize);
+  }
+
+  virtual ~TestTab() {
+    SetView(original_rwhv_);
+    original_rwhv_ = nullptr;
+  }
+
+  TestWebContents* web_contents() const { return web_contents_.get(); }
+
+  WebContentsMediaCaptureId GetWebContentsMediaCaptureId() const {
+    RenderFrameHost* const rfh = web_contents_->GetPrimaryMainFrame();
+    return WebContentsMediaCaptureId(rfh->GetProcess()->GetID(),
+                                     rfh->GetRoutingID());
+  }
+
+  void SetSize(const gfx::Size& size) { rwhv_->SetSize(size); }
+
+  RenderWidgetHostImpl* GetRenderWidgetHostImpl() const {
+    return RenderWidgetHostImpl::From(
+        web_contents_->GetPrimaryMainFrame()->GetRenderWidgetHost());
+  }
+
+ protected:
+  static std::unique_ptr<TestWebContents> MakeTestWebContents(
+      BrowserContext* browser_context) {
+    scoped_refptr<SiteInstance> instance =
+        SiteInstance::Create(browser_context);
+    instance->GetProcess()->Init();
+    return TestWebContents::Create(browser_context, std::move(instance));
+  }
+
+  void SetView(RenderWidgetHostViewBase* rwhv) {
+    GetRenderWidgetHostImpl()->SetView(rwhv);
+  }
+
+ private:
+  const std::unique_ptr<TestWebContents> web_contents_;
+  std::unique_ptr<TestView> rwhv_;
+  raw_ptr<RenderWidgetHostViewBase> original_rwhv_ = nullptr;
+};
+
+class MockCapturedSurfaceControlPermissionManager
+    : public CapturedSurfaceControlPermissionManager {
+ public:
+  MockCapturedSurfaceControlPermissionManager(
+      GlobalRenderFrameHostId capturer_rfh_id)
+      : CapturedSurfaceControlPermissionManager(capturer_rfh_id) {}
+  ~MockCapturedSurfaceControlPermissionManager() override = default;
+
+  void SetPermissionResult(CSCPermissionResult result) {
+    DCHECK_CURRENTLY_ON(BrowserThread::IO);
+    result_ = result;
+  }
+
+  void CheckPermission(
+      base::OnceCallback<void(CSCPermissionResult)> callback) override {
+    DCHECK_CURRENTLY_ON(BrowserThread::IO);
+    CHECK(result_.has_value());
+    std::move(callback).Run(result_.value());
+  }
+
+ private:
+  std::optional<CSCPermissionResult> result_;
+};
+
+using MockPermissionManager = MockCapturedSurfaceControlPermissionManager;
+
+// Make a callback that expects `result` and then unblock `run_loop`.
+base::OnceCallback<void(CSCResult)> MakeCallbackExpectingResult(
+    base::RunLoop* run_loop,
+    CSCResult expected_result) {
+  return base::BindOnce(
+      [](base::RunLoop* run_loop, CSCResult expected_result, CSCResult result) {
+        EXPECT_EQ(result, expected_result);
+        run_loop->Quit();
+      },
+      run_loop, expected_result);
+}
+
+// Equivalent to MakeCallbackExpectingResult, but for GetZoomLevel().
+base::OnceCallback<void(std::optional<int>, CSCResult)>
+MakeGetZoomCallbackExpectingResult(base::RunLoop* run_loop,
+                                   CSCResult expected_result) {
+  return base::BindOnce(
+      [](base::RunLoop* run_loop, CSCResult expected_result,
+         std::optional<int> zoom_level, CSCResult result) {
+        EXPECT_EQ(result, expected_result);
+        // `zoom_level` intentionally ignored.
+        run_loop->Quit();
+      },
+      run_loop, expected_result);
+}
+
+// Make a callback that expects `result` and then unblock `run_loop`.
+base::OnceCallback<void(std::optional<int>, CSCResult)>
+MakeGetZoomLevelCallbackExpectingResult(base::RunLoop* run_loop,
+                                        std::optional<int> expected_zoom_level,
+                                        CSCResult expected_result) {
+  return base::BindOnce(
+      [](base::RunLoop* run_loop, std::optional<int> expected_zoom_level,
+         CSCResult expected_result, std::optional<int> zoom_level,
+         CSCResult result) {
+        EXPECT_EQ(zoom_level, expected_zoom_level);
+        EXPECT_EQ(result, expected_result);
+        run_loop->Quit();
+      },
+      run_loop, expected_zoom_level, expected_result);
+}
+
+class CapturedSurfaceControllerTestBase : public RenderViewHostTestHarness {
+ public:
+  ~CapturedSurfaceControllerTestBase() override = default;
+
+  void SetUp() override {
+    RenderViewHostTestHarness::SetUp();
+
+    SetUpTestTabs();
+    StartCaptureOf(*capturee_);
+    AwaitWebContentsResolution();
+  }
+
+  void SetUpTestTabs() {
+    capturer_ = std::make_unique<TestTab>(GetBrowserContext());
+    capturee_ = std::make_unique<TestTab>(GetBrowserContext());
+  }
+
+  void StartCaptureOf(const TestTab& tab) {
+    auto permission_manager = std::make_unique<MockPermissionManager>(
+        capturer_->web_contents()->GetPrimaryMainFrame()->GetGlobalId());
+    permission_manager_ = permission_manager.get();
+
+    // `base::Unretained(this)` is safe because `this` owns `controller_` and
+    // therefore has a longer lifetime.
+    controller_ = CapturedSurfaceController::CreateForTesting(
+        capturer_->web_contents()->GetPrimaryMainFrame()->GetGlobalId(),
+        tab.GetWebContentsMediaCaptureId(), std::move(permission_manager),
+        base::BindRepeating(
+            &CapturedSurfaceControllerTestBase::OnWebContentsResolved,
+            base::Unretained(this)));
+  }
+
+  void TearDown() override {
+    permission_manager_ = nullptr;
+    controller_.reset();
+    capturer_.reset();
+    capturee_.reset();
+
+    RenderViewHostTestHarness::TearDown();
+  }
+
+  void AwaitWebContentsResolution() {
+    CHECK(!wc_resolution_run_loop_);
+    wc_resolution_run_loop_ = std::make_unique<base::RunLoop>();
+    wc_resolution_run_loop_->Run();
+    wc_resolution_run_loop_.reset();
+  }
+
+  void OnWebContentsResolved() {
+    if (wc_resolution_run_loop_) {
+      wc_resolution_run_loop_->Quit();
+    }
+  }
+
+ protected:
+  std::unique_ptr<CapturedSurfaceController> controller_;
+  raw_ptr<MockPermissionManager> permission_manager_ = nullptr;
+  std::unique_ptr<TestTab> capturer_;
+  std::unique_ptr<TestTab> capturee_;
+  std::unique_ptr<base::RunLoop> wc_resolution_run_loop_;
+};
+
+class CapturedSurfaceControllerSendWheelTest
+    : public CapturedSurfaceControllerTestBase {
+ public:
+  ~CapturedSurfaceControllerSendWheelTest() override = default;
+
+  void SetUp() override {
+    CapturedSurfaceControllerTestBase::SetUp();
+
+    input_observer_ = std::make_unique<InputObserver>();
+    capturee_->GetRenderWidgetHostImpl()->AddInputEventObserver(
+        input_observer_.get());
+  }
+
+  void TearDown() override {
+    capturee_->GetRenderWidgetHostImpl()->RemoveInputEventObserver(
+        input_observer_.get());
+
+    CapturedSurfaceControllerTestBase::TearDown();
+  }
+
+ protected:
+  std::unique_ptr<InputObserver> input_observer_;
+};
+
+TEST_F(CapturedSurfaceControllerSendWheelTest, CorrectScaling) {
+  permission_manager_->SetPermissionResult(CSCPermissionResult::kGranted);
+  capturee_->SetSize(gfx::Size(256, 4096));
+  base::RunLoop run_loop;
+  input_observer_->AddExpectation(InputObserver::ExpectedWheelEvent{
+      .x = 256 * 0.25, .y = 4096 * 0.5, .delta_x = 300, .delta_y = 400});
+  controller_->SendWheel(
+      CapturedWheelAction::New(
+          /*x=*/0.25,
+          /*y=*/0.5,
+          /*wheel_delta_x=*/300,
+          /*wheel_delta_y=*/400),
+      MakeCallbackExpectingResult(&run_loop, CSCResult::kSuccess));
+  run_loop.Run();
+}
+
+TEST_F(CapturedSurfaceControllerSendWheelTest,
+       GracefullyHandleZeroWidthCapturedSurface) {
+  permission_manager_->SetPermissionResult(CSCPermissionResult::kGranted);
+  capturee_->SetSize(gfx::Size(0, 4096));
+  base::RunLoop run_loop;
+  // Note absence of call to input_observer_->AddExpectation().
+  controller_->SendWheel(
+      CapturedWheelAction::New(
+          /*x=*/0.25,
+          /*y=*/0.5,
+          /*wheel_delta_x=*/300,
+          /*wheel_delta_y=*/400),
+      MakeCallbackExpectingResult(&run_loop, CSCResult::kUnknownError));
+  run_loop.Run();
+}
+
+TEST_F(CapturedSurfaceControllerSendWheelTest,
+       GracefullyHandleZeroHeightCapturedSurface) {
+  permission_manager_->SetPermissionResult(CSCPermissionResult::kGranted);
+  capturee_->SetSize(gfx::Size(256, 0));
+  base::RunLoop run_loop;
+  // Note absence of call to input_observer_->AddExpectation().
+  controller_->SendWheel(
+      CapturedWheelAction::New(
+          /*x=*/0.25,
+          /*y=*/0.5,
+          /*wheel_delta_x=*/300,
+          /*wheel_delta_y=*/400),
+      MakeCallbackExpectingResult(&run_loop, CSCResult::kUnknownError));
+  run_loop.Run();
+}
+
+TEST_F(CapturedSurfaceControllerSendWheelTest,
+       GracefullyHandleExtremelyNarrowCapturedSurface) {
+  permission_manager_->SetPermissionResult(CSCPermissionResult::kGranted);
+  capturee_->SetSize(gfx::Size(1, 4096));
+  base::RunLoop run_loop;
+  input_observer_->AddExpectation(InputObserver::ExpectedWheelEvent{
+      .x = 0, .y = 4096 * 0.5, .delta_x = 300, .delta_y = 400});
+  controller_->SendWheel(
+      CapturedWheelAction::New(
+          /*x=*/0.25,
+          /*y=*/0.5,
+          /*wheel_delta_x=*/300,
+          /*wheel_delta_y=*/400),
+      MakeCallbackExpectingResult(&run_loop, CSCResult::kSuccess));
+  run_loop.Run();
+}
+
+TEST_F(CapturedSurfaceControllerSendWheelTest,
+       GracefullyHandleExtremelyShortCapturedSurface) {
+  permission_manager_->SetPermissionResult(CSCPermissionResult::kGranted);
+  capturee_->SetSize(gfx::Size(256, 1));
+  base::RunLoop run_loop;
+  input_observer_->AddExpectation(InputObserver::ExpectedWheelEvent{
+      .x = 256 * 0.25, .y = 0, .delta_x = 300, .delta_y = 400});
+  controller_->SendWheel(
+      CapturedWheelAction::New(
+          /*x=*/0.25,
+          /*y=*/0.5,
+          /*wheel_delta_x=*/300,
+          /*wheel_delta_y=*/400),
+      MakeCallbackExpectingResult(&run_loop, CSCResult::kSuccess));
+  run_loop.Run();
+}
+
+// TODO(crbug.com/1466247): Remove this test suite after the getZoomLevel() API
+// is made synchronous.
+class CapturedSurfaceControllerGetZoomLevelTest
+    : public CapturedSurfaceControllerTestBase {
+ public:
+  ~CapturedSurfaceControllerGetZoomLevelTest() override = default;
+};
+
+TEST_F(CapturedSurfaceControllerGetZoomLevelTest, GetZoomLevelSuccess) {
+  content::HostZoomMap::SetZoomLevel(capturee_->web_contents(),
+                                     blink::PageZoomFactorToZoomLevel(0.9));
+  base::RunLoop run_loop;
+  controller_->GetZoomLevel(MakeGetZoomLevelCallbackExpectingResult(
+      &run_loop, 90, CSCResult::kSuccess));
+  run_loop.Run();
+}
+
+TEST_F(CapturedSurfaceControllerGetZoomLevelTest, GetZoomLevelUnknownError) {
+  base::RunLoop run_loop;
+  capturee_.reset();
+  controller_->GetZoomLevel(MakeGetZoomLevelCallbackExpectingResult(
+      &run_loop, std::nullopt, CSCResult::kCapturedSurfaceNotFoundError));
+  run_loop.Run();
+}
+
+class CapturedSurfaceControllerSetZoomLevelTest
+    : public CapturedSurfaceControllerTestBase,
+      public ::testing::WithParamInterface<int> {
+ public:
+  CapturedSurfaceControllerSetZoomLevelTest() : zoom_level_(GetParam()) {}
+  const int zoom_level_;
+};
+
+INSTANTIATE_TEST_SUITE_P(
+    ,
+    CapturedSurfaceControllerSetZoomLevelTest,
+    ::testing::Values(
+        static_cast<int>(std::ceil(100 * blink::kMinimumPageZoomFactor)),
+        static_cast<int>(std::floor(100 * blink::kMaximumPageZoomFactor))));
+
+TEST_P(CapturedSurfaceControllerSetZoomLevelTest, SetZoomLevelSuccess) {
+  permission_manager_->SetPermissionResult(CSCPermissionResult::kGranted);
+  base::RunLoop run_loop;
+  controller_->SetZoomLevel(
+      zoom_level_, MakeCallbackExpectingResult(&run_loop, CSCResult::kSuccess));
+  run_loop.Run();
+
+  EXPECT_EQ(zoom_level_,
+            std::round(100 * blink::PageZoomLevelToZoomFactor(
+                                 content::HostZoomMap::GetZoomLevel(
+                                     capturee_->web_contents()))));
+}
+
+enum class CapturedSurfaceControlAPI {
+  kSendWheel,
+  kSetZoomLevel,
+  // TODO(crbug.com/1466247): Remove kGetZoomLevel after making that API sync.
+  kGetZoomLevel,
+};
+
+class CapturedSurfaceControllerInterfaceTestBase
+    : public CapturedSurfaceControllerTestBase {
+ public:
+  CapturedSurfaceControllerInterfaceTestBase(
+      CapturedSurfaceControlAPI tested_interface)
+      : tested_interface_(tested_interface) {}
+  ~CapturedSurfaceControllerInterfaceTestBase() override = default;
+
+  void RunTestedActionAndExpect(base::RunLoop* run_loop,
+                                CSCResult expected_result) {
+    switch (tested_interface_) {
+      case CapturedSurfaceControlAPI::kSendWheel:
+        controller_->SendWheel(
+            MakeCapturedWheelActionPtr(),
+            MakeCallbackExpectingResult(run_loop, expected_result));
+        return;
+      case CapturedSurfaceControlAPI::kSetZoomLevel:
+        controller_->SetZoomLevel(
+            /*zoom_level=*/100,
+            MakeCallbackExpectingResult(run_loop, expected_result));
+        return;
+      case CapturedSurfaceControlAPI::kGetZoomLevel:
+        controller_->GetZoomLevel(
+            MakeGetZoomCallbackExpectingResult(run_loop, expected_result));
+        return;
+    }
+    NOTREACHED_NORETURN();
+  }
+
+ protected:
+  const CapturedSurfaceControlAPI tested_interface_;
+};
+
+class CapturedSurfaceControllerInterfaceTest
+    : public CapturedSurfaceControllerInterfaceTestBase,
+      public ::testing::WithParamInterface<CapturedSurfaceControlAPI> {
+ public:
+  CapturedSurfaceControllerInterfaceTest()
+      : CapturedSurfaceControllerInterfaceTestBase(GetParam()) {}
+
+  ~CapturedSurfaceControllerInterfaceTest() override = default;
+};
+
+// Note that kGetZoomLevel is not tested because it's currently allowed
+// without requiring permissions, and that is by design.
+// TODO(crbug.com/1466247): Remove above comment after making that API sync.
+INSTANTIATE_TEST_SUITE_P(
+    ,
+    CapturedSurfaceControllerInterfaceTest,
+    ::testing::Values(CapturedSurfaceControlAPI::kSendWheel,
+                      CapturedSurfaceControlAPI::kSetZoomLevel));
+
+TEST_P(CapturedSurfaceControllerInterfaceTest, SuccessReportedIfPermitted) {
+  base::RunLoop run_loop;
+  permission_manager_->SetPermissionResult(CSCPermissionResult::kGranted);
+  RunTestedActionAndExpect(&run_loop, CSCResult::kSuccess);
+  run_loop.Run();
+}
+
+TEST_P(CapturedSurfaceControllerInterfaceTest, NoPermissionReportedIfDenied) {
+  base::RunLoop run_loop;
+  permission_manager_->SetPermissionResult(CSCPermissionResult::kDenied);
+  RunTestedActionAndExpect(&run_loop, CSCResult::kNoPermissionError);
+  run_loop.Run();
+}
+
+TEST_P(CapturedSurfaceControllerInterfaceTest,
+       UnknownErrorReportedIfPermissionError) {
+  base::RunLoop run_loop;
+  permission_manager_->SetPermissionResult(CSCPermissionResult::kError);
+  RunTestedActionAndExpect(&run_loop, CSCResult::kUnknownError);
+  run_loop.Run();
+}
+
+// Simulate the captured tab being closed after permission is granted but before
+// the controller has time to process the response from the permission manager.
+TEST_P(CapturedSurfaceControllerInterfaceTest,
+       SurfaceNotFoundReportedIfTabClosedBeforePromptResponseHandled) {
+  base::RunLoop run_loop;
+  permission_manager_->SetPermissionResult(CSCPermissionResult::kGranted);
+  capturee_.reset();
+  RunTestedActionAndExpect(&run_loop, CSCResult::kCapturedSurfaceNotFoundError);
+  run_loop.Run();
+}
+
+TEST_P(CapturedSurfaceControllerInterfaceTest,
+       SurfaceNotFoundReportedIfCaptureTargetUpdatedToNonTabSurface) {
+  base::RunLoop run_loop;
+  permission_manager_->SetPermissionResult(CSCPermissionResult::kGranted);
+  controller_->UpdateCaptureTarget(WebContentsMediaCaptureId());
+  RunTestedActionAndExpect(&run_loop, CSCResult::kCapturedSurfaceNotFoundError);
+  run_loop.Run();
+}
+
+// Test suite ensuring that API calls before/after the WebContents ID is
+// resolved to a base::WeakPtr<WebContents> behave as expected.
+class CapturedSurfaceControllerWebContentsResolutionTest
+    : public CapturedSurfaceControllerInterfaceTestBase,
+      public ::testing::WithParamInterface<CapturedSurfaceControlAPI> {
+ public:
+  CapturedSurfaceControllerWebContentsResolutionTest()
+      : CapturedSurfaceControllerInterfaceTestBase(GetParam()) {}
+
+  ~CapturedSurfaceControllerWebContentsResolutionTest() override = default;
+
+  void SetUp() override {
+    // Intentionally skip CapturedSurfaceControllerInterfaceTestBase's SetUp(),
+    // and therefore also CapturedSurfaceControllerTestBase's SetUp().
+    RenderViewHostTestHarness::SetUp();
+  }
+};
+
+INSTANTIATE_TEST_SUITE_P(
+    ,
+    CapturedSurfaceControllerWebContentsResolutionTest,
+    ::testing::Values(CapturedSurfaceControlAPI::kSendWheel,
+                      CapturedSurfaceControlAPI::kSetZoomLevel,
+                      CapturedSurfaceControlAPI::kGetZoomLevel));
+
+TEST_P(CapturedSurfaceControllerWebContentsResolutionTest,
+       ApiInvocationAfterWebContentsResolutionSucceeds) {
+  SetUpTestTabs();  // Triggers resolution but does not await it.
+  StartCaptureOf(*capturee_);
+  permission_manager_->SetPermissionResult(CSCPermissionResult::kGranted);
+
+  AwaitWebContentsResolution();
+
+  base::RunLoop run_loop;
+  RunTestedActionAndExpect(&run_loop, CSCResult::kSuccess);
+  run_loop.Run();
+}
+
+TEST_P(CapturedSurfaceControllerWebContentsResolutionTest,
+       ApiInvocationPriorToWebContentsResolutionFails) {
+  SetUpTestTabs();  // Triggers resolution but does not await it.
+  StartCaptureOf(*capturee_);
+  permission_manager_->SetPermissionResult(CSCPermissionResult::kGranted);
+
+  base::RunLoop run_loop;
+  RunTestedActionAndExpect(&run_loop, CSCResult::kCapturedSurfaceNotFoundError);
+  run_loop.Run();
+
+  AwaitWebContentsResolution();
+}
+
+TEST_P(
+    CapturedSurfaceControllerWebContentsResolutionTest,
+    ApiInvocationPriorToWebContentsResolutionFailsButSubsequentCallsAreNotBlocked) {
+  // Setup - repeat ApiInvocationPriorToWebContentsResolutionFails.
+  {
+    SetUpTestTabs();  // Triggers resolution but does not await it.
+    StartCaptureOf(*capturee_);
+    permission_manager_->SetPermissionResult(CSCPermissionResult::kGranted);
+
+    base::RunLoop run_loop;
+    RunTestedActionAndExpect(&run_loop,
+                             CSCResult::kCapturedSurfaceNotFoundError);
+    run_loop.Run();
+
+    AwaitWebContentsResolution();
+  }
+
+  // After AwaitWebContentsResolution() is called, subsequent API calls succeed.
+  base::RunLoop run_loop;
+  RunTestedActionAndExpect(&run_loop, CSCResult::kSuccess);
+  run_loop.Run();
+}
+
+// Similar to CapturedSurfaceControllerWebContentsResolutionTest,
+// but focuses on calls to UpdateCaptureTarget(), which also trigger resolution.
+class CapturedSurfaceControllerWebContentsResolutionOfUpdatesTest
+    : public CapturedSurfaceControllerInterfaceTestBase,
+      public ::testing::WithParamInterface<CapturedSurfaceControlAPI> {
+ public:
+  CapturedSurfaceControllerWebContentsResolutionOfUpdatesTest()
+      : CapturedSurfaceControllerInterfaceTestBase(GetParam()) {}
+
+  ~CapturedSurfaceControllerWebContentsResolutionOfUpdatesTest() override =
+      default;
+
+  void SetUp() override {
+    // Unlike CapturedSurfaceControllerWebContentsResolutionTest, the current
+    // test works well with the parent's SetUp(), which awaits the resolution
+    // of the *first* ID. This is due to the current test's focus on what
+    // happens before/after the call to UpdateCaptureTarget().
+    CapturedSurfaceControllerInterfaceTestBase::SetUp();
+
+    permission_manager_->SetPermissionResult(CSCPermissionResult::kGranted);
+
+    // Prepare a new tab to capture instead of the original one.
+    new_capturee_ = std::make_unique<TestTab>(GetBrowserContext());
+  }
+
+  void TearDown() override {
+    new_capturee_.reset();
+
+    CapturedSurfaceControllerInterfaceTestBase::TearDown();
+  }
+
+ protected:
+  std::unique_ptr<TestTab> new_capturee_;
+};
+
+INSTANTIATE_TEST_SUITE_P(
+    ,
+    CapturedSurfaceControllerWebContentsResolutionOfUpdatesTest,
+    ::testing::Values(CapturedSurfaceControlAPI::kSendWheel,
+                      CapturedSurfaceControlAPI::kSetZoomLevel,
+                      CapturedSurfaceControlAPI::kGetZoomLevel));
+
+TEST_P(
+    CapturedSurfaceControllerWebContentsResolutionOfUpdatesTest,
+    AfterUpdateCaptureTargetApiInvocationAfterToWebContentsResolutionSucceeds) {
+  // Call UpdateCaptureTarget() - capturing a new tab.
+  controller_->UpdateCaptureTarget(
+      new_capturee_->GetWebContentsMediaCaptureId());
+  AwaitWebContentsResolution();
+
+  base::RunLoop run_loop;
+  RunTestedActionAndExpect(&run_loop, CSCResult::kSuccess);
+  run_loop.Run();
+}
+
+TEST_P(CapturedSurfaceControllerWebContentsResolutionOfUpdatesTest,
+       AfterUpdateCaptureTargetApiInvocationPriorToWebContentsResolutionFails) {
+  // Call UpdateCaptureTarget() - capturing a new tab.
+  controller_->UpdateCaptureTarget(
+      new_capturee_->GetWebContentsMediaCaptureId());
+  // Note absence of call to AwaitWebContentsResolution().
+
+  base::RunLoop run_loop;
+  RunTestedActionAndExpect(&run_loop, CSCResult::kCapturedSurfaceNotFoundError);
+  run_loop.Run();
+
+  AwaitWebContentsResolution();
+}
+
+// Test suite ensuring that API calls before/after the WebContents ID is
+// resolved to a base::WeakPtr<WebContents> behave as expected.
+class CapturedSurfaceControllerSelfCaptureTest
+    : public CapturedSurfaceControllerInterfaceTestBase,
+      public ::testing::WithParamInterface<CapturedSurfaceControlAPI> {
+ public:
+  CapturedSurfaceControllerSelfCaptureTest()
+      : CapturedSurfaceControllerInterfaceTestBase(GetParam()) {}
+
+  ~CapturedSurfaceControllerSelfCaptureTest() override = default;
+
+  void SetUp() override {
+    // Intentionally skip CapturedSurfaceControllerInterfaceTestBase's SetUp(),
+    // and therefore also CapturedSurfaceControllerTestBase's SetUp().
+    RenderViewHostTestHarness::SetUp();
+    SetUpTestTabs();
+  }
+};
+
+INSTANTIATE_TEST_SUITE_P(
+    ,
+    CapturedSurfaceControllerSelfCaptureTest,
+    ::testing::Values(CapturedSurfaceControlAPI::kSendWheel,
+                      CapturedSurfaceControlAPI::kSetZoomLevel,
+                      CapturedSurfaceControlAPI::kGetZoomLevel));
+
+TEST_P(CapturedSurfaceControllerSelfCaptureTest, SelfCaptureDisallowed) {
+  StartCaptureOf(*capturer_);
+  AwaitWebContentsResolution();
+  permission_manager_->SetPermissionResult(CSCPermissionResult::kGranted);
+
+  base::RunLoop run_loop;
+  RunTestedActionAndExpect(&run_loop, CSCResult::kUnknownError);
+  run_loop.Run();
+}
+
+TEST_P(CapturedSurfaceControllerSelfCaptureTest,
+       UpdateCaptureTargetToOtherTabEnablesCapturedSurfaceControl) {
+  StartCaptureOf(*capturer_);
+  AwaitWebContentsResolution();
+  permission_manager_->SetPermissionResult(CSCPermissionResult::kGranted);
+
+  controller_->UpdateCaptureTarget(capturee_->GetWebContentsMediaCaptureId());
+  AwaitWebContentsResolution();
+
+  base::RunLoop run_loop;
+  RunTestedActionAndExpect(&run_loop, CSCResult::kSuccess);
+  run_loop.Run();
+}
+
+TEST_P(CapturedSurfaceControllerSelfCaptureTest,
+       UpdateCaptureTargetToCapturingTabDisablesCapturedSurfaceControl) {
+  StartCaptureOf(*capturee_);
+  AwaitWebContentsResolution();
+  permission_manager_->SetPermissionResult(CSCPermissionResult::kGranted);
+
+  {
+    base::RunLoop run_loop;
+    RunTestedActionAndExpect(&run_loop, CSCResult::kSuccess);
+    run_loop.Run();
+  }
+
+  controller_->UpdateCaptureTarget(capturer_->GetWebContentsMediaCaptureId());
+  AwaitWebContentsResolution();
+
+  base::RunLoop run_loop;
+  RunTestedActionAndExpect(&run_loop, CSCResult::kUnknownError);
+  run_loop.Run();
+}
+
+}  // namespace
+}  // namespace content
diff --git a/content/browser/renderer_host/media/captured_surface_controller.h b/content/browser/renderer_host/media/captured_surface_controller.h
deleted file mode 100644
index 2eb008ca..0000000
--- a/content/browser/renderer_host/media/captured_surface_controller.h
+++ /dev/null
@@ -1,84 +0,0 @@
-// Copyright 2023 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CONTENT_BROWSER_RENDERER_HOST_MEDIA_CAPTURED_SURFACE_CONTROLLER_H_
-#define CONTENT_BROWSER_RENDERER_HOST_MEDIA_CAPTURED_SURFACE_CONTROLLER_H_
-
-#include <memory>
-
-#include "base/functional/callback.h"
-#include "content/browser/renderer_host/media/captured_surface_control_permission_manager.h"
-#include "content/common/content_export.h"
-#include "content/public/browser/global_routing_id.h"
-#include "content/public/browser/web_contents_media_capture_id.h"
-#include "third_party/blink/public/mojom/mediastream/media_stream.mojom.h"
-
-namespace content {
-
-// Encapsulates the permission state and logic associated with the Captured
-// Surface Control API. Objects of this class live on the IO thread.
-class CONTENT_EXPORT CapturedSurfaceController {
- public:
-  using CapturedSurfaceControlResult =
-      ::blink::mojom::CapturedSurfaceControlResult;
-
-  CapturedSurfaceController(GlobalRenderFrameHostId capturer_rfh_id,
-                            WebContentsMediaCaptureId captured_wc_id);
-
-  CapturedSurfaceController(
-      WebContentsMediaCaptureId captured_wc_id,
-      std::unique_ptr<CapturedSurfaceControlPermissionManager>
-          permission_manager);
-
-  virtual ~CapturedSurfaceController();
-
-  CapturedSurfaceController(const CapturedSurfaceController&) = delete;
-  CapturedSurfaceController& operator=(const CapturedSurfaceController&) =
-      delete;
-
-  // Set the captured WebContents this controller is associated with.
-  // This may be called with a null `WebContentsMediaCaptureId`.
-  virtual void UpdateCaptureTarget(WebContentsMediaCaptureId captured_wc_id);
-
-  // Produce a wheel event on the captured surface.
-  virtual void SendWheel(
-      blink::mojom::CapturedWheelActionPtr action,
-      base::OnceCallback<void(CapturedSurfaceControlResult)> reply_callback);
-
-  // Get the zoom level of the captured tab.
-  virtual void GetZoomLevel(
-      base::OnceCallback<void(
-          std::optional<int> zoom_level,
-          blink::mojom::CapturedSurfaceControlResult result)> reply_callback);
-
-  // Set the zoom level of the captured tab.
-  virtual void SetZoomLevel(
-      int zoom_level,
-      base::OnceCallback<void(CapturedSurfaceControlResult)> reply_callback);
-
- private:
-  using PermissionResult =
-      ::content::CapturedSurfaceControlPermissionManager::PermissionResult;
-
-  // The ID of the captured tab.
-  // * When `this` object is constructed, `captured_wc_id_` is set to a valid ID
-  // of a valid tab.
-  // * Later, through dynamic switching of the captured surface, the user might
-  // start capturing something other than a tab, leading to this field being set
-  // to the null ID.
-  //
-  // TODO(crbug.com/1511754): Whenever `captured_wc_id_` is recorded - either
-  // set to its initial value or updated to a new value - pipe along from the UI
-  // thread the knowledge of whether the capture-session is self-capture, record
-  // that here, and use it on the IO thread to reject API calls until the user
-  // invokes share-this-tab-instead to change self-capture into
-  // other-tab-capture.
-  WebContentsMediaCaptureId captured_wc_id_;
-
-  std::unique_ptr<CapturedSurfaceControlPermissionManager> permission_manager_;
-};
-
-}  // namespace content
-
-#endif  // CONTENT_BROWSER_RENDERER_HOST_MEDIA_CAPTURED_SURFACE_CONTROLLER_H_
diff --git a/content/browser/renderer_host/media/captured_surface_controller_unittest.cc b/content/browser/renderer_host/media/captured_surface_controller_unittest.cc
deleted file mode 100644
index 23e7e13..0000000
--- a/content/browser/renderer_host/media/captured_surface_controller_unittest.cc
+++ /dev/null
@@ -1,471 +0,0 @@
-// Copyright 2023 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "content/browser/renderer_host/media/captured_surface_controller.h"
-
-#include <list>
-#include <memory>
-
-#include "base/functional/bind.h"
-#include "base/run_loop.h"
-#include "content/browser/renderer_host/media/captured_surface_control_permission_manager.h"
-#include "content/public/browser/global_routing_id.h"
-#include "content/public/browser/host_zoom_map.h"
-#include "content/public/test/browser_task_environment.h"
-#include "content/test/test_web_contents.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/blink/public/common/page/page_zoom.h"
-#include "third_party/blink/public/mojom/mediastream/media_stream.mojom.h"
-
-namespace content {
-namespace {
-
-using CapturedWheelAction = ::blink::mojom::CapturedWheelAction;
-using CapturedWheelActionPtr = ::blink::mojom::CapturedWheelActionPtr;
-using CSCResult = ::blink::mojom::CapturedSurfaceControlResult;
-using CSCPermissionResult =
-    ::content::CapturedSurfaceControlPermissionManager::PermissionResult;
-
-// Make an arbitrary valid CapturedWheelAction.
-CapturedWheelActionPtr MakeCapturedWheelActionPtr() {
-  return CapturedWheelAction::New(
-      /*x=*/0,
-      /*y=*/0,
-      /*wheel_delta_x=*/0,
-      /*wheel_delta_y=*/0);
-}
-
-class InputObserver : public RenderWidgetHost::InputEventObserver {
- public:
-  struct ExpectedWheelEvent {
-    double x = 0;
-    double y = 0;
-    double delta_x = 0;
-    double delta_y = 0;
-  };
-
-  ~InputObserver() override { EXPECT_TRUE(expected_events_.empty()); }
-
-  void OnInputEvent(const blink::WebInputEvent& event) override {
-    CHECK_EQ(event.GetType(), blink::WebInputEvent::Type::kMouseWheel);
-
-    const blink::WebMouseWheelEvent& wheel_event =
-        static_cast<const blink::WebMouseWheelEvent&>(event);
-
-    CHECK(!expected_events_.empty());
-    ExpectedWheelEvent expected_event = expected_events_.front();
-    expected_events_.pop_front();
-
-    EXPECT_EQ(expected_event.x, wheel_event.PositionInWidget().x());
-    EXPECT_EQ(expected_event.y, wheel_event.PositionInWidget().y());
-    EXPECT_EQ(expected_event.delta_x, wheel_event.delta_x);
-    EXPECT_EQ(expected_event.delta_y, wheel_event.delta_y);
-  }
-
-  void AddExpectation(ExpectedWheelEvent expected_event) {
-    expected_events_.push_back(expected_event);
-
-    // The wheel event chains are closed with a scroll of zero
-    // magnitude in the same location.
-    expected_events_.push_back(ExpectedWheelEvent{.x = expected_event.x,
-                                                  .y = expected_event.y,
-                                                  .delta_x = 0,
-                                                  .delta_y = 0});
-  }
-
- private:
-  std::list<ExpectedWheelEvent> expected_events_;
-};
-
-class TestView : public TestRenderWidgetHostView {
- public:
-  explicit TestView(RenderWidgetHostImpl* rwhi)
-      : TestRenderWidgetHostView(rwhi) {}
-  ~TestView() override = default;
-
-  void SetSize(const gfx::Size& size) override { size_ = size; }
-
-  gfx::Size GetVisibleViewportSize() override { return size_; }
-
- private:
-  gfx::Size size_;
-};
-
-class MockCapturedSurfaceControlPermissionManager
-    : public CapturedSurfaceControlPermissionManager {
- public:
-  MockCapturedSurfaceControlPermissionManager(
-      GlobalRenderFrameHostId capturer_rfh_id)
-      : CapturedSurfaceControlPermissionManager(capturer_rfh_id) {}
-  ~MockCapturedSurfaceControlPermissionManager() override = default;
-
-  void SetPermissionResult(CSCPermissionResult result) {
-    DCHECK_CURRENTLY_ON(BrowserThread::IO);
-    result_ = result;
-  }
-
-  void CheckPermission(
-      base::OnceCallback<void(CSCPermissionResult)> callback) override {
-    DCHECK_CURRENTLY_ON(BrowserThread::IO);
-    CHECK(result_.has_value());
-    std::move(callback).Run(result_.value());
-  }
-
- private:
-  std::optional<CSCPermissionResult> result_;
-};
-
-using MockPermissionManager = MockCapturedSurfaceControlPermissionManager;
-
-// Make a callback that expects `result` and then unblock `run_loop`.
-base::OnceCallback<void(CSCResult)> MakeCallbackExpectingResult(
-    base::RunLoop* run_loop,
-    CSCResult expected_result) {
-  return base::BindOnce(
-      [](base::RunLoop* run_loop, CSCResult expected_result, CSCResult result) {
-        EXPECT_EQ(result, expected_result);
-        run_loop->Quit();
-      },
-      run_loop, expected_result);
-}
-
-// Make a callback that expects `result` and then unblock `run_loop`.
-base::OnceCallback<void(std::optional<int>, CSCResult)>
-MakeGetZoomLevelCallbackExpectingResult(base::RunLoop* run_loop,
-                                        std::optional<int> expected_zoom_level,
-                                        CSCResult expected_result) {
-  return base::BindOnce(
-      [](base::RunLoop* run_loop, std::optional<int> expected_zoom_level,
-         CSCResult expected_result, std::optional<int> zoom_level,
-         CSCResult result) {
-        EXPECT_EQ(zoom_level, expected_zoom_level);
-        EXPECT_EQ(result, expected_result);
-        run_loop->Quit();
-      },
-      run_loop, expected_zoom_level, expected_result);
-}
-
-class CapturedSurfaceControllerTestBase : public RenderViewHostTestHarness {
- public:
-  static constexpr gfx::Size kCapturedViewportSize = gfx::Size(100, 400);
-
-  ~CapturedSurfaceControllerTestBase() override = default;
-
-  std::unique_ptr<TestWebContents> MakeTestWebContents() {
-    scoped_refptr<SiteInstance> instance =
-        SiteInstance::Create(GetBrowserContext());
-    instance->GetProcess()->Init();
-    return TestWebContents::Create(GetBrowserContext(), std::move(instance));
-  }
-
-  RenderWidgetHostImpl* GetRenderWidgetHostImpl(WebContents& wc) {
-    return RenderWidgetHostImpl::From(
-        wc.GetPrimaryMainFrame()->GetRenderWidgetHost());
-  }
-
-  RenderWidgetHostViewBase* GetView(WebContents& wc) {
-    return GetRenderWidgetHostImpl(wc)->GetView();
-  }
-
-  void SetView(WebContents& wc, RenderWidgetHostViewBase* rwhv) {
-    GetRenderWidgetHostImpl(wc)->SetView(rwhv);
-  }
-
-  void SetUp() override {
-    RenderViewHostTestHarness::SetUp();
-
-    capturing_wc_ = MakeTestWebContents();
-    captured_wc_ = MakeTestWebContents();
-
-    RenderFrameHost* const captured_main_rfh =
-        captured_wc_->GetPrimaryMainFrame();
-    const WebContentsMediaCaptureId captured_wc_id(
-        captured_main_rfh->GetProcess()->GetID(),
-        captured_main_rfh->GetRoutingID());
-
-    old_rwhv_ = GetView(*captured_wc_);
-    captured_rwhv_ =
-        std::make_unique<TestView>(GetRenderWidgetHostImpl(*captured_wc_));
-    SetView(*captured_wc_, captured_rwhv_.get());
-    captured_rwhv_->SetSize(kCapturedViewportSize);
-
-    auto permission_manager = std::make_unique<MockPermissionManager>(
-        capturing_wc_->GetPrimaryMainFrame()->GetGlobalId());
-    permission_manager_ = permission_manager.get();
-
-    controller_ = std::make_unique<CapturedSurfaceController>(
-        captured_wc_id, std::move(permission_manager));
-  }
-
-  void UnloadCapturedWebContents() {
-    if (!captured_wc_) {
-      return;
-    }
-
-    SetView(*captured_wc_, old_rwhv_);
-    old_rwhv_ = nullptr;
-
-    captured_wc_.reset();
-  }
-
-  void TearDown() override {
-    permission_manager_ = nullptr;
-    controller_.reset();
-    capturing_wc_.reset();
-    UnloadCapturedWebContents();
-    captured_rwhv_.reset();
-
-    RenderViewHostTestHarness::TearDown();
-  }
-
-  std::unique_ptr<CapturedSurfaceController> controller_;
-  raw_ptr<MockPermissionManager> permission_manager_ = nullptr;
-  std::unique_ptr<TestView> captured_rwhv_;
-  raw_ptr<RenderWidgetHostViewBase> old_rwhv_ = nullptr;
-  std::unique_ptr<TestWebContents> capturing_wc_;
-  std::unique_ptr<TestWebContents> captured_wc_;
-};
-
-class CapturedSurfaceControllerSendWheelTest
-    : public CapturedSurfaceControllerTestBase {
- public:
-  ~CapturedSurfaceControllerSendWheelTest() override = default;
-
-  void SetUp() override {
-    CapturedSurfaceControllerTestBase::SetUp();
-
-    input_observer_ = std::make_unique<InputObserver>();
-    GetRenderWidgetHostImpl(*captured_wc_)
-        ->AddInputEventObserver(input_observer_.get());
-  }
-
-  void TearDown() override {
-    GetRenderWidgetHostImpl(*captured_wc_)
-        ->RemoveInputEventObserver(input_observer_.get());
-
-    CapturedSurfaceControllerTestBase::TearDown();
-  }
-
- protected:
-  std::unique_ptr<InputObserver> input_observer_;
-};
-
-TEST_F(CapturedSurfaceControllerSendWheelTest, CorrectScaling) {
-  permission_manager_->SetPermissionResult(CSCPermissionResult::kGranted);
-  captured_rwhv_->SetSize(gfx::Size(256, 4096));
-  base::RunLoop run_loop;
-  input_observer_->AddExpectation(InputObserver::ExpectedWheelEvent{
-      .x = 256 * 0.25, .y = 4096 * 0.5, .delta_x = 300, .delta_y = 400});
-  controller_->SendWheel(
-      CapturedWheelAction::New(
-          /*x=*/0.25,
-          /*y=*/0.5,
-          /*wheel_delta_x=*/300,
-          /*wheel_delta_y=*/400),
-      MakeCallbackExpectingResult(&run_loop, CSCResult::kSuccess));
-  run_loop.Run();
-}
-
-TEST_F(CapturedSurfaceControllerSendWheelTest,
-       GracefullyHandleZeroWidthCapturedSurface) {
-  permission_manager_->SetPermissionResult(CSCPermissionResult::kGranted);
-  captured_rwhv_->SetSize(gfx::Size(0, 4096));
-  base::RunLoop run_loop;
-  // Note absence of call to input_observer_->AddExpectation().
-  controller_->SendWheel(
-      CapturedWheelAction::New(
-          /*x=*/0.25,
-          /*y=*/0.5,
-          /*wheel_delta_x=*/300,
-          /*wheel_delta_y=*/400),
-      MakeCallbackExpectingResult(&run_loop, CSCResult::kUnknownError));
-  run_loop.Run();
-}
-
-TEST_F(CapturedSurfaceControllerSendWheelTest,
-       GracefullyHandleZeroHeightCapturedSurface) {
-  permission_manager_->SetPermissionResult(CSCPermissionResult::kGranted);
-  captured_rwhv_->SetSize(gfx::Size(256, 0));
-  base::RunLoop run_loop;
-  // Note absence of call to input_observer_->AddExpectation().
-  controller_->SendWheel(
-      CapturedWheelAction::New(
-          /*x=*/0.25,
-          /*y=*/0.5,
-          /*wheel_delta_x=*/300,
-          /*wheel_delta_y=*/400),
-      MakeCallbackExpectingResult(&run_loop, CSCResult::kUnknownError));
-  run_loop.Run();
-}
-
-TEST_F(CapturedSurfaceControllerSendWheelTest,
-       GracefullyHandleExtremelyNarrowCapturedSurface) {
-  permission_manager_->SetPermissionResult(CSCPermissionResult::kGranted);
-  captured_rwhv_->SetSize(gfx::Size(1, 4096));
-  base::RunLoop run_loop;
-  input_observer_->AddExpectation(InputObserver::ExpectedWheelEvent{
-      .x = 0, .y = 4096 * 0.5, .delta_x = 300, .delta_y = 400});
-  controller_->SendWheel(
-      CapturedWheelAction::New(
-          /*x=*/0.25,
-          /*y=*/0.5,
-          /*wheel_delta_x=*/300,
-          /*wheel_delta_y=*/400),
-      MakeCallbackExpectingResult(&run_loop, CSCResult::kSuccess));
-  run_loop.Run();
-}
-
-TEST_F(CapturedSurfaceControllerSendWheelTest,
-       GracefullyHandleExtremelyShortCapturedSurface) {
-  permission_manager_->SetPermissionResult(CSCPermissionResult::kGranted);
-  captured_rwhv_->SetSize(gfx::Size(256, 1));
-  base::RunLoop run_loop;
-  input_observer_->AddExpectation(InputObserver::ExpectedWheelEvent{
-      .x = 256 * 0.25, .y = 0, .delta_x = 300, .delta_y = 400});
-  controller_->SendWheel(
-      CapturedWheelAction::New(
-          /*x=*/0.25,
-          /*y=*/0.5,
-          /*wheel_delta_x=*/300,
-          /*wheel_delta_y=*/400),
-      MakeCallbackExpectingResult(&run_loop, CSCResult::kSuccess));
-  run_loop.Run();
-}
-
-// TODO(crbug.com/1466247): Remove this test suite after the getZoomLevel() API
-// is made synchronous.
-class CapturedSurfaceControllerGetZoomLevelTest
-    : public CapturedSurfaceControllerTestBase {
- public:
-  ~CapturedSurfaceControllerGetZoomLevelTest() override = default;
-};
-
-TEST_F(CapturedSurfaceControllerGetZoomLevelTest, GetZoomLevelSuccess) {
-  content::HostZoomMap::SetZoomLevel(captured_wc_.get(),
-                                     blink::PageZoomFactorToZoomLevel(0.9));
-  base::RunLoop run_loop;
-  controller_->GetZoomLevel(MakeGetZoomLevelCallbackExpectingResult(
-      &run_loop, 90, CSCResult::kSuccess));
-  run_loop.Run();
-}
-
-TEST_F(CapturedSurfaceControllerGetZoomLevelTest, GetZoomLevelUnknownError) {
-  base::RunLoop run_loop;
-  UnloadCapturedWebContents();
-  controller_->GetZoomLevel(MakeGetZoomLevelCallbackExpectingResult(
-      &run_loop, std::nullopt, CSCResult::kCapturedSurfaceNotFoundError));
-  run_loop.Run();
-}
-
-class CapturedSurfaceControllerSetZoomLevelTest
-    : public CapturedSurfaceControllerTestBase,
-      public ::testing::WithParamInterface<int> {
- public:
-  CapturedSurfaceControllerSetZoomLevelTest() : zoom_level_(GetParam()) {}
-  const int zoom_level_;
-};
-
-INSTANTIATE_TEST_SUITE_P(
-    ,
-    CapturedSurfaceControllerSetZoomLevelTest,
-    ::testing::Values(
-        static_cast<int>(std::ceil(100 * blink::kMinimumPageZoomFactor)),
-        static_cast<int>(std::floor(100 * blink::kMaximumPageZoomFactor))));
-
-TEST_P(CapturedSurfaceControllerSetZoomLevelTest, SetZoomLevelSuccess) {
-  permission_manager_->SetPermissionResult(CSCPermissionResult::kGranted);
-  base::RunLoop run_loop;
-  controller_->SetZoomLevel(
-      zoom_level_, MakeCallbackExpectingResult(&run_loop, CSCResult::kSuccess));
-  run_loop.Run();
-
-  EXPECT_EQ(zoom_level_,
-            std::round(100 * blink::PageZoomLevelToZoomFactor(
-                                 content::HostZoomMap::GetZoomLevel(
-                                     captured_wc_.get()))));
-}
-
-enum class CapturedSurfaceControlAPI {
-  kSendWheel,
-  kSetZoomLevel,
-};
-
-class CapturedSurfaceControllerInterfaceTest
-    : public CapturedSurfaceControllerTestBase,
-      public ::testing::WithParamInterface<CapturedSurfaceControlAPI> {
- public:
-  CapturedSurfaceControllerInterfaceTest() : tested_interface_(GetParam()) {}
-
-  ~CapturedSurfaceControllerInterfaceTest() override = default;
-
-  void RunTestedAction(base::OnceCallback<void(CSCResult)> callback) {
-    switch (tested_interface_) {
-      case CapturedSurfaceControlAPI::kSendWheel:
-        controller_->SendWheel(MakeCapturedWheelActionPtr(),
-                               std::move(callback));
-        return;
-      case CapturedSurfaceControlAPI::kSetZoomLevel:
-        controller_->SetZoomLevel(/*zoom_level=*/100, std::move(callback));
-        return;
-    }
-    NOTREACHED_NORETURN();
-  }
-
-  const CapturedSurfaceControlAPI tested_interface_;
-};
-
-INSTANTIATE_TEST_SUITE_P(
-    ,
-    CapturedSurfaceControllerInterfaceTest,
-    ::testing::Values(CapturedSurfaceControlAPI::kSendWheel,
-                      CapturedSurfaceControlAPI::kSetZoomLevel));
-
-TEST_P(CapturedSurfaceControllerInterfaceTest, SuccessReportedIfPermitted) {
-  base::RunLoop run_loop;
-  permission_manager_->SetPermissionResult(CSCPermissionResult::kGranted);
-  RunTestedAction(MakeCallbackExpectingResult(&run_loop, CSCResult::kSuccess));
-  run_loop.Run();
-}
-
-TEST_P(CapturedSurfaceControllerInterfaceTest, NoPermissionReportedIfDenied) {
-  base::RunLoop run_loop;
-  permission_manager_->SetPermissionResult(CSCPermissionResult::kDenied);
-  RunTestedAction(
-      MakeCallbackExpectingResult(&run_loop, CSCResult::kNoPermissionError));
-  run_loop.Run();
-}
-
-TEST_P(CapturedSurfaceControllerInterfaceTest,
-       UnknownErrorReportedIfPermissionError) {
-  base::RunLoop run_loop;
-  permission_manager_->SetPermissionResult(CSCPermissionResult::kError);
-  RunTestedAction(
-      MakeCallbackExpectingResult(&run_loop, CSCResult::kUnknownError));
-  run_loop.Run();
-}
-
-// Simulate the captured tab being closed after permission is granted but before
-// the controller has time to process the response from the permission manager.
-TEST_P(CapturedSurfaceControllerInterfaceTest,
-       SurfaceNotFoundReportedIfTabClosedBeforePromptResponseHandled) {
-  base::RunLoop run_loop;
-  permission_manager_->SetPermissionResult(CSCPermissionResult::kGranted);
-  UnloadCapturedWebContents();
-  RunTestedAction(MakeCallbackExpectingResult(
-      &run_loop, CSCResult::kCapturedSurfaceNotFoundError));
-  run_loop.Run();
-}
-
-TEST_P(CapturedSurfaceControllerInterfaceTest,
-       SurfaceNotFoundReportedIfCaptureTargetUpdatedToNonTabSurface) {
-  base::RunLoop run_loop;
-  permission_manager_->SetPermissionResult(CSCPermissionResult::kGranted);
-  controller_->UpdateCaptureTarget(WebContentsMediaCaptureId());
-  RunTestedAction(MakeCallbackExpectingResult(
-      &run_loop, CSCResult::kCapturedSurfaceNotFoundError));
-  run_loop.Run();
-}
-
-}  // namespace
-}  // namespace content
diff --git a/content/browser/renderer_host/media/media_stream_manager.cc b/content/browser/renderer_host/media/media_stream_manager.cc
index fb07a6a..5db8665 100644
--- a/content/browser/renderer_host/media/media_stream_manager.cc
+++ b/content/browser/renderer_host/media/media_stream_manager.cc
@@ -105,7 +105,7 @@
 #endif
 
 #if !BUILDFLAG(IS_ANDROID)
-#include "content/browser/renderer_host/media/captured_surface_controller.h"
+#include "content/browser/media/captured_surface_controller.h"
 #endif
 
 using ::blink::mojom::MediaDeviceType;
@@ -459,7 +459,8 @@
                            media_id.ToString());
   device.display_media_info = media::mojom::DisplayMediaInformation::New(
       display_surface, /*logical_surface=*/true,
-      media::mojom::CursorCaptureType::NEVER, /*capture_handle=*/nullptr);
+      media::mojom::CursorCaptureType::NEVER, /*capture_handle=*/nullptr,
+      /*initial_zoom_level=*/100);
   devices.push_back(device);
   if (!request_audio) {
     return devices;
@@ -470,7 +471,8 @@
       media::AudioDeviceDescription::kDefaultDeviceId, "Fake audio");
   audio_device.display_media_info = media::mojom::DisplayMediaInformation::New(
       display_surface, /*logical_surface=*/true,
-      media::mojom::CursorCaptureType::NEVER, /*capture_handle=*/nullptr);
+      media::mojom::CursorCaptureType::NEVER, /*capture_handle=*/nullptr,
+      /*initial_zoom_level=*/100);
   devices.emplace_back(audio_device);
   return devices;
 }
diff --git a/content/browser/renderer_host/media/media_stream_manager.h b/content/browser/renderer_host/media/media_stream_manager.h
index bda9699..f4a297b 100644
--- a/content/browser/renderer_host/media/media_stream_manager.h
+++ b/content/browser/renderer_host/media/media_stream_manager.h
@@ -50,7 +50,7 @@
 #include "third_party/blink/public/mojom/permissions/permission_status.mojom.h"
 
 #if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
-#include "content/browser/renderer_host/media/captured_surface_controller.h"
+#include "content/browser/media/captured_surface_controller.h"
 #endif
 
 namespace media {
diff --git a/content/browser/renderer_host/navigation_controller_impl.cc b/content/browser/renderer_host/navigation_controller_impl.cc
index 1a7d7a8..639562a5 100644
--- a/content/browser/renderer_host/navigation_controller_impl.cc
+++ b/content/browser/renderer_host/navigation_controller_impl.cc
@@ -51,7 +51,6 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/time/time.h"
 #include "base/trace_event/optional_trace_event.h"
-#include "base/trace_event/trace_conversion_helper.h"
 #include "base/trace_event/trace_event.h"
 #include "base/types/optional_util.h"
 #include "build/build_config.h"
diff --git a/content/browser/renderer_host/navigation_request.cc b/content/browser/renderer_host/navigation_request.cc
index a8c49c8..dead8456 100644
--- a/content/browser/renderer_host/navigation_request.cc
+++ b/content/browser/renderer_host/navigation_request.cc
@@ -38,7 +38,6 @@
 #include "base/time/time.h"
 #include "base/timer/elapsed_timer.h"
 #include "base/trace_event/base_tracing.h"
-#include "base/trace_event/trace_conversion_helper.h"
 #include "base/types/optional_util.h"
 #include "base/types/pass_key.h"
 #include "build/build_config.h"
@@ -2157,7 +2156,7 @@
           "navigation", "Navigation StartToCommit",
           TRACE_ID_WITH_SCOPE("StartToCommit", TRACE_ID_LOCAL(this)), "URL",
           common_params_->url.spec(), "Net Error Code", net_error_);
-      MaybeRecordTraceEvents();
+      MaybeRecordTraceEventsAndHistograms();
     }
 
     // Abandon the prerender host reserved for activation if it exists.
@@ -10238,11 +10237,12 @@
   return loader_.get() != nullptr;
 }
 
-void NavigationRequest::MaybeRecordTraceEvents() {
+void NavigationRequest::MaybeRecordTraceEventsAndHistograms() {
   if (navigation_handle_timing_.navigation_commit_sent_time.is_null() ||
       IsSameDocument() || IsRestore() ||
       NavigationTypeUtils::IsHistory(common_params_->navigation_type) ||
-      NavigationTypeUtils::IsReload(common_params_->navigation_type)) {
+      NavigationTypeUtils::IsReload(common_params_->navigation_type) ||
+      !common_params_->url.SchemeIsHTTPOrHTTPS()) {
     return;
   }
 
@@ -10252,7 +10252,7 @@
   const auto trace_id = TRACE_ID_WITH_SCOPE("NavigationBreakdown",
                                             TRACE_ID_LOCAL(navigation_id_));
 
-#define TRACE_WITH_TIMESTAMP0(name, begin_time, end_time)                     \
+#define MAYBE_RECORD_TRACE_AND_HISTOGRAM0(name, begin_time, end_time)         \
   do {                                                                        \
     if (!begin_time.is_null() && !end_time.is_null() &&                       \
         navigation_start_time <= begin_time &&                                \
@@ -10261,44 +10261,55 @@
                                                        trace_id, begin_time); \
       TRACE_EVENT_NESTABLE_ASYNC_END_WITH_TIMESTAMP0("navigation", name,      \
                                                      trace_id, end_time);     \
+      base::UmaHistogramTimes(                                                \
+          "Navigation.MainFrame.NewNavigation.IgnoreRestore."                 \
+          "IsHTTPOrHTTPS." name ".Time",                                      \
+          end_time - begin_time);                                             \
     }                                                                         \
   } while (0)
 
-#define TRACE_WITH_TIMESTAMP1(name, begin_time, end_time, arg1_name, arg1_val) \
-  do {                                                                         \
-    if (!begin_time.is_null() && !end_time.is_null() &&                        \
-        navigation_start_time <= begin_time &&                                 \
-        end_time <= navigation_handle_timing_.navigation_commit_sent_time) {   \
-      TRACE_EVENT_NESTABLE_ASYNC_BEGIN_WITH_TIMESTAMP1(                        \
-          "navigation", name, trace_id, begin_time, arg1_name, arg1_val);      \
-      TRACE_EVENT_NESTABLE_ASYNC_END_WITH_TIMESTAMP0("navigation", name,       \
-                                                     trace_id, end_time);      \
-    }                                                                          \
+#define MAYBE_RECORD_TRACE_AND_HISTOGRAM1(name, begin_time, end_time,        \
+                                          arg1_name, arg1_val)               \
+  do {                                                                       \
+    if (!begin_time.is_null() && !end_time.is_null() &&                      \
+        navigation_start_time <= begin_time &&                               \
+        end_time <= navigation_handle_timing_.navigation_commit_sent_time) { \
+      TRACE_EVENT_NESTABLE_ASYNC_BEGIN_WITH_TIMESTAMP1(                      \
+          "navigation", name, trace_id, begin_time, arg1_name, arg1_val);    \
+      TRACE_EVENT_NESTABLE_ASYNC_END_WITH_TIMESTAMP0("navigation", name,     \
+                                                     trace_id, end_time);    \
+      base::UmaHistogramTimes(                                               \
+          "Navigation.MainFrame.NewNavigation.IgnoreRestore."                \
+          "IsHTTPOrHTTPS." name ".Time",                                     \
+          end_time - begin_time);                                            \
+    }                                                                        \
   } while (0)
 
-  TRACE_WITH_TIMESTAMP0("NavigationStartToBeginNavigation",
-                        navigation_start_time, begin_navigation_time_);
-  TRACE_WITH_TIMESTAMP0("BeginNavigationToLoaderStart", begin_navigation_time_,
-                        loader_start_time_);
-  TRACE_WITH_TIMESTAMP1("LoaderStartToReceiveResponse", loader_start_time_,
-                        receive_response_time_, "URL",
-                        common_params_->url.spec());
-  TRACE_WITH_TIMESTAMP0("LoaderStartToFetchStart", loader_start_time_,
-                        first_fetch_start_time_);
-  TRACE_WITH_TIMESTAMP0("FetchStart", first_fetch_start_time_,
-                        navigation_handle_timing_.first_request_start_time);
-  TRACE_WITH_TIMESTAMP0("ReceiveHeaders",
-                        navigation_handle_timing_.first_request_start_time,
-                        final_receive_headers_end_time_);
-  TRACE_WITH_TIMESTAMP0("ReceiveHeadersToReceiveResponse",
-                        final_receive_headers_end_time_,
-                        receive_response_time_);
-  TRACE_WITH_TIMESTAMP0("ReceiveResponseToCommitNavigation",
-                        receive_response_time_,
-                        navigation_handle_timing_.navigation_commit_sent_time);
+  MAYBE_RECORD_TRACE_AND_HISTOGRAM0("NavigationStartToBeginNavigation",
+                                    navigation_start_time,
+                                    begin_navigation_time_);
+  MAYBE_RECORD_TRACE_AND_HISTOGRAM0("BeginNavigationToLoaderStart",
+                                    begin_navigation_time_, loader_start_time_);
+  MAYBE_RECORD_TRACE_AND_HISTOGRAM1("LoaderStartToReceiveResponse",
+                                    loader_start_time_, receive_response_time_,
+                                    "URL", common_params_->url.spec());
+  MAYBE_RECORD_TRACE_AND_HISTOGRAM0(
+      "LoaderStartToFetchStart", loader_start_time_, first_fetch_start_time_);
+  MAYBE_RECORD_TRACE_AND_HISTOGRAM0(
+      "FetchStart", first_fetch_start_time_,
+      navigation_handle_timing_.first_request_start_time);
+  MAYBE_RECORD_TRACE_AND_HISTOGRAM0(
+      "ReceiveHeaders", navigation_handle_timing_.first_request_start_time,
+      final_receive_headers_end_time_);
+  MAYBE_RECORD_TRACE_AND_HISTOGRAM0("ReceiveHeadersToReceiveResponse",
+                                    final_receive_headers_end_time_,
+                                    receive_response_time_);
+  MAYBE_RECORD_TRACE_AND_HISTOGRAM0(
+      "ReceiveResponseToCommitNavigation", receive_response_time_,
+      navigation_handle_timing_.navigation_commit_sent_time);
 
-#undef TRACE_WITH_TIMESTAMP0
-#undef TRACE_WITH_TIMESTAMP1
+#undef MAYBE_RECORD_TRACE_AND_HISTOGRAM0
+#undef MAYBE_RECORD_TRACE_AND_HISTOGRAM1
 }
 
 }  // namespace content
diff --git a/content/browser/renderer_host/navigation_request.h b/content/browser/renderer_host/navigation_request.h
index bfc60a1..5d7ef29 100644
--- a/content/browser/renderer_host/navigation_request.h
+++ b/content/browser/renderer_host/navigation_request.h
@@ -2035,7 +2035,7 @@
   GetOriginForURLLoaderFactoryUncheckedWithDebugInfo();
   url::Origin GetOriginForURLLoaderFactoryUnchecked();
 
-  void MaybeRecordTraceEvents();
+  void MaybeRecordTraceEventsAndHistograms();
 
   // Never null. The pointee node owns this navigation request instance.
   // This field is not a raw_ptr because of incompatibilities with tracing
diff --git a/content/public/test/DEPS b/content/public/test/DEPS
index 2e91878..0d15af6 100644
--- a/content/public/test/DEPS
+++ b/content/public/test/DEPS
@@ -82,7 +82,7 @@
     "+content/browser/fenced_frame/fenced_frame_reporter.h"
   ],
   "mock_captured_surface_controller.h": [
-    "+content/browser/renderer_host/media/captured_surface_controller.h"
+    "+content/browser/media/captured_surface_controller.h"
   ],
   "test_shared_storage_header_observer.h": [
     "+content/browser/shared_storage/shared_storage_header_observer.h"
diff --git a/content/public/test/browser_test_utils.cc b/content/public/test/browser_test_utils.cc
index 520fdb0..cd23ceda 100644
--- a/content/public/test/browser_test_utils.cc
+++ b/content/public/test/browser_test_utils.cc
@@ -137,7 +137,7 @@
 #include "ui/latency/latency_info.h"
 
 #if !BUILDFLAG(IS_ANDROID)
-#include "content/browser/renderer_host/media/captured_surface_controller.h"
+#include "content/browser/media/captured_surface_controller.h"
 #include "content/public/test/mock_captured_surface_controller.h"
 #endif  // !BUILDFLAG(IS_ANDROID)
 
diff --git a/content/public/test/mock_captured_surface_controller.cc b/content/public/test/mock_captured_surface_controller.cc
index a4b40df..f9582a6c 100644
--- a/content/public/test/mock_captured_surface_controller.cc
+++ b/content/public/test/mock_captured_surface_controller.cc
@@ -4,7 +4,7 @@
 
 #include "content/public/test/mock_captured_surface_controller.h"
 
-#include "content/browser/renderer_host/media/captured_surface_control_permission_manager.h"
+#include "content/browser/media/captured_surface_control_permission_manager.h"
 #include "third_party/blink/public/mojom/mediastream/media_stream.mojom.h"
 
 namespace content {
diff --git a/content/public/test/mock_captured_surface_controller.h b/content/public/test/mock_captured_surface_controller.h
index 36948f7..f63b4bb 100644
--- a/content/public/test/mock_captured_surface_controller.h
+++ b/content/public/test/mock_captured_surface_controller.h
@@ -6,7 +6,7 @@
 #define CONTENT_PUBLIC_TEST_MOCK_CAPTURED_SURFACE_CONTROLLER_H_
 
 #include "base/functional/callback.h"
-#include "content/browser/renderer_host/media/captured_surface_controller.h"
+#include "content/browser/media/captured_surface_controller.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "third_party/blink/public/mojom/mediastream/media_stream.mojom.h"
 
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index 8397c1e0..581d0df 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -3179,8 +3179,8 @@
       ]
       sources += [
         "../browser/media/capture/desktop_capture_device_unittest.cc",
-        "../browser/renderer_host/media/captured_surface_controller_permission_manager_unittest.cc",
-        "../browser/renderer_host/media/captured_surface_controller_unittest.cc",
+        "../browser/media/captured_surface_controller_permission_manager_unittest.cc",
+        "../browser/media/captured_surface_controller_unittest.cc",
         "../browser/renderer_host/media/sub_capture_target_id_web_contents_helper_unittest.cc",
       ]
     }
diff --git a/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt
index 3127bfb..d1170ff 100644
--- a/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt
@@ -387,7 +387,6 @@
 crbug.com/1468826 [ ventura angle-opengl amd-0x67ef asan ] Pixel_WebGPUToDataURL [ Failure ]
 
 crbug.com/1469875 [ mac graphite-enabled amd release no-asan ] Pixel_SVGHuge [ Failure ]
-crbug.com/1469875 [ apple-angle-metal-renderer:-apple-m2 graphite-enabled mac ] Pixel_SVGHuge [ Failure ]
 crbug.com/1469875 [ mac graphite-enabled intel release no-asan ] Pixel_SVGHuge [ Failure ]
 crbug.com/1469875 [ mac graphite-enabled intel release asan ] Pixel_CSSFilterEffects_NoOverlays [ Failure ]
 
diff --git a/content/test/gpu/gpu_tests/test_expectations/trace_test_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/trace_test_expectations.txt
index eeb3816..01e249e 100644
--- a/content/test/gpu/gpu_tests/test_expectations/trace_test_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/trace_test_expectations.txt
@@ -246,10 +246,7 @@
 crbug.com/1446057 [ win10 amd-0x7340 angle-d3d11 ] VideoPathTraceTest_DirectComposition_Video_SW_Decode [ Failure ]
 
 # Broadly flaky unsymbolized crashes on Jacuzzi
-crbug.com/1518007 [ chromeos cros-chrome chromeos-board-jacuzzi ] TraceTest_BackgroundImage [ RetryOnFailure ]
 crbug.com/1518007 [ chromeos cros-chrome chromeos-board-jacuzzi ] TraceTest_Canvas2D* [ RetryOnFailure ]
-crbug.com/1518007 [ chromeos cros-chrome chromeos-board-jacuzzi ] TraceTest_SolidColorBackground [ RetryOnFailure ]
-crbug.com/1518007 [ chromeos cros-chrome chromeos-board-jacuzzi ] TraceTest_Video_* [ RetryOnFailure ]
 crbug.com/1518007 [ chromeos cros-chrome chromeos-board-jacuzzi ] TraceTest_WebGL* [ RetryOnFailure ]
 crbug.com/1518007 [ chromeos cros-chrome chromeos-board-jacuzzi ] WebGLCanvasCaptureTraceTest_VideoStream* [ RetryOnFailure ]
 
diff --git a/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
index acc678a..a7611029 100644
--- a/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
@@ -727,11 +727,6 @@
 ## Linux AMD RX 5500 XT ##
 
 crbug.com/1152588 [ linux amd-0x7340 angle-opengl passthrough ] conformance2/rendering/multisampling-fragment-evaluation.html [ Failure ]
-crbug.com/1516822 [ linux amd-0x7340 angle-opengl passthrough ] deqp/functional/gles3/transformfeedback/basic_types_separate_lines.html [ RetryOnFailure ]
-crbug.com/1516822 [ linux amd-0x7340 angle-opengl passthrough ] deqp/functional/gles3/transformfeedback/basic_types_separate_triangles.html [ RetryOnFailure ]
-crbug.com/1516822 [ linux amd-0x7340 angle-opengl passthrough ] deqp/functional/gles3/transformfeedback/interpolation_centroid.html [ RetryOnFailure ]
-crbug.com/1516822 [ linux amd-0x7340 angle-opengl passthrough ] deqp/functional/gles3/transformfeedback/interpolation_flat.html [ RetryOnFailure ]
-crbug.com/1516822 [ linux amd-0x7340 angle-opengl passthrough ] deqp/functional/gles3/uniformapi/value_assigned.html [ RetryOnFailure ]
 
 ####################
 # Android failures #
@@ -869,7 +864,6 @@
 crbug.com/1499536 [ chromeos cros-chrome chromeos-board-jacuzzi ] deqp/functional/gles3/uniformbuffers/single_basic_type.html [ Failure ]
 crbug.com/1500045 [ chromeos cros-chrome chromeos-board-jacuzzi ] conformance2/rendering/blitframebuffer-r11f-g11f-b10f.html [ Failure ]
 # Broadly flaky unsymbolized crashes on Jacuzzi
-crbug.com/1518007 [ chromeos cros-chrome chromeos-board-jacuzzi ] WebglExtension_* [ RetryOnFailure ]
 crbug.com/1518007 [ chromeos cros-chrome chromeos-board-jacuzzi ] conformance/* [ RetryOnFailure ]
 crbug.com/1518007 [ chromeos cros-chrome chromeos-board-jacuzzi ] conformance2/* [ RetryOnFailure ]
 crbug.com/1518007 [ chromeos cros-chrome chromeos-board-jacuzzi ] deqp/* [ RetryOnFailure ]
diff --git a/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
index f5066257..d94231a9 100644
--- a/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
@@ -447,7 +447,6 @@
 crbug.com/1489477 [ fuchsia fuchsia-board-sherlock ] conformance/state/gl-object-get-calls.html [ Failure ]
 crbug.com/1489477 [ fuchsia fuchsia-board-sherlock ] conformance/textures/canvas/tex-2d-luminance-luminance-unsigned_byte.html [ Failure ]
 crbug.com/1489477 [ fuchsia fuchsia-board-astro ] conformance/textures/canvas/tex-2d-rgb-rgb-unsigned_short_5_6_5.html [ Failure ]
-crbug.com/1489477 [ fuchsia fuchsia-board-nelson ] conformance/textures/canvas/tex-2d-rgb-rgb-unsigned_short_5_6_5.html [ Failure ]
 crbug.com/1489477 [ fuchsia ] conformance/textures/misc/texture-size-limit.html [ Failure ]
 crbug.com/1489477 [ fuchsia fuchsia-board-astro ] conformance/textures/misc/texture-sub-image-cube-maps.html [ Failure ]
 crbug.com/1489477 [ fuchsia fuchsia-board-sherlock ] conformance/textures/misc/texture-sub-image-cube-maps.html [ Failure ]
diff --git a/extensions/browser/api/content_settings/content_settings_custom_extension_provider.cc b/extensions/browser/api/content_settings/content_settings_custom_extension_provider.cc
index 3cb27ce..f4c9e6b 100644
--- a/extensions/browser/api/content_settings/content_settings_custom_extension_provider.cc
+++ b/extensions/browser/api/content_settings/content_settings_custom_extension_provider.cc
@@ -53,7 +53,7 @@
   // changed.
   NotifyObservers(ContentSettingsPattern::Wildcard(),
                   ContentSettingsPattern::Wildcard(),
-                  ContentSettingsType::DEFAULT);
+                  ContentSettingsType::DEFAULT, /*partition_key=*/nullptr);
 }
 
 }  // namespace content_settings
diff --git a/gpu/command_buffer/service/webgpu_decoder_impl.cc b/gpu/command_buffer/service/webgpu_decoder_impl.cc
index 834fafad..4074e54d 100644
--- a/gpu/command_buffer/service/webgpu_decoder_impl.cc
+++ b/gpu/command_buffer/service/webgpu_decoder_impl.cc
@@ -1215,8 +1215,7 @@
         return true;
       }
 
-      auto* info =
-          dawn_instance_->GetFeatureInfo(static_cast<WGPUFeatureName>(feature));
+      auto* info = dawn::native::GetFeatureInfo(feature);
       if (info == nullptr) {
         return false;
       }
diff --git a/infra/config/generated/testing/variants.pyl b/infra/config/generated/testing/variants.pyl
index bcfb553..51c09e9 100644
--- a/infra/config/generated/testing/variants.pyl
+++ b/infra/config/generated/testing/variants.pyl
@@ -141,7 +141,7 @@
     'identifier': 'JACUZZI_CQ_PUBLIC_LKGM',
     'skylab': {
       'cros_board': 'jacuzzi',
-      'cros_img': 'jacuzzi-public/R122-15724.0.0',
+      'use_lkgm': True,
       'bucket': 'chromiumos-image-archive',
       'public_builder': 'cros_test_platform_public',
       'public_builder_bucket': 'testplatform-public',
@@ -165,7 +165,7 @@
     'identifier': 'OCTOPUS_PUBLIC_LKGM',
     'skylab': {
       'cros_board': 'octopus',
-      'cros_img': 'octopus-public/R122-15724.0.0',
+      'use_lkgm': True,
       'bucket': 'chromiumos-image-archive',
     },
   },
diff --git a/infra/config/targets/cros-skylab-variants.json b/infra/config/targets/cros-skylab-variants.json
index 931e5f9..1c29b75 100644
--- a/infra/config/targets/cros-skylab-variants.json
+++ b/infra/config/targets/cros-skylab-variants.json
@@ -216,8 +216,7 @@
   "CROS_JACUZZI_CQ_PUBLIC_LKGM": {
     "skylab": {
       "cros_board": "jacuzzi",
-      "cros_chrome_version": "122.0.6195.0",
-      "cros_img": "jacuzzi-public/R122-15724.0.0",
+      "use_lkgm": true,
       "bucket": "chromiumos-image-archive",
       "public_builder": "cros_test_platform_public",
       "public_builder_bucket": "testplatform-public"
@@ -238,8 +237,7 @@
   "CROS_OCTOPUS_PUBLIC_LKGM": {
     "skylab": {
       "cros_board": "octopus",
-      "cros_chrome_version": "122.0.6195.0",
-      "cros_img": "octopus-public/R122-15724.0.0",
+      "use_lkgm": true,
       "bucket": "chromiumos-image-archive"
     },
     "enabled": true,
diff --git a/ios/chrome/browser/ui/first_run/first_run_egtest.mm b/ios/chrome/browser/ui/first_run/first_run_egtest.mm
index 6db44c0..6a43e37 100644
--- a/ios/chrome/browser/ui/first_run/first_run_egtest.mm
+++ b/ios/chrome/browser/ui/first_run/first_run_egtest.mm
@@ -53,6 +53,7 @@
 #import "ios/testing/earl_grey/app_launch_configuration.h"
 #import "ios/testing/earl_grey/app_launch_manager.h"
 #import "ios/testing/earl_grey/earl_grey_test.h"
+#import "ui/base/device_form_factor.h"
 #import "ui/base/l10n/l10n_util.h"
 #import "ui/base/test/ios/ui_image_test_utils.h"
 
@@ -1500,6 +1501,13 @@
       performAction:grey_tap()];
   // Checks that the choice screen is shown
   [SearchEngineChoiceEarlGreyUI verifySearchEngineChoiceScreenIsDisplayed];
+
+  BOOL isPhone = (ui::GetDeviceFormFactor() ==
+                  ui::DeviceFormFactor::DEVICE_FORM_FACTOR_PHONE);
+  // Checks that the fake omnibox illustration is displayed on phones and is
+  // initially empty
+  [SearchEngineChoiceEarlGreyUI
+      verifyFakeOmniboxIllustrationState:isPhone ? kEmpty : kHidden];
   // Verifies that the primary button is initially the "More" button.
   id<GREYMatcher> moreButtonMatcher =
       grey_accessibilityID(kSearchEngineMoreButtonIdentifier);
@@ -1512,6 +1520,10 @@
       selectSearchEngineCellWithName:searchEngineToSelect
                      scrollDirection:kGREYDirectionDown
                               amount:50];
+  // Checks that the fake omnibox illustration is still displayed on phones but
+  // is no longer empty
+  [SearchEngineChoiceEarlGreyUI
+      verifyFakeOmniboxIllustrationState:isPhone ? kFull : kHidden];
   // Taps the primary button. This scrolls the table down to the bottom.
   [[[EarlGrey selectElementWithMatcher:moreButtonMatcher]
       assertWithMatcher:grey_notNil()] performAction:grey_tap()];
@@ -1536,6 +1548,13 @@
       performAction:grey_tap()];
   // Checks that the choice screen is shown
   [SearchEngineChoiceEarlGreyUI verifySearchEngineChoiceScreenIsDisplayed];
+
+  BOOL isPhone = (ui::GetDeviceFormFactor() ==
+                  ui::DeviceFormFactor::DEVICE_FORM_FACTOR_PHONE);
+  // Checks that the fake omnibox illustration is displayed on phones and is
+  // initially empty
+  [SearchEngineChoiceEarlGreyUI
+      verifyFakeOmniboxIllustrationState:isPhone ? kEmpty : kHidden];
   // Verifies that the primary button is initially the "More" button.
   id<GREYMatcher> moreButtonMatcher =
       grey_accessibilityID(kSearchEngineMoreButtonIdentifier);
@@ -1560,6 +1579,10 @@
       selectSearchEngineCellWithName:searchEngineToSelect
                      scrollDirection:kGREYDirectionUp
                               amount:300];
+  // Checks that the fake omnibox illustration is still displayed on phones but
+  // is no longer empty
+  [SearchEngineChoiceEarlGreyUI
+      verifyFakeOmniboxIllustrationState:isPhone ? kFull : kHidden];
   [SearchEngineChoiceEarlGreyUI confirmSearchEngineChoiceScreen];
 
   DismissDefaultBrowserAndOmniboxPositionSelectionScreens();
diff --git a/ios/chrome/browser/ui/search_engine_choice/fake_omnibox/fake_omnibox_view.mm b/ios/chrome/browser/ui/search_engine_choice/fake_omnibox/fake_omnibox_view.mm
index e2c2d901..c5b94a8 100644
--- a/ios/chrome/browser/ui/search_engine_choice/fake_omnibox/fake_omnibox_view.mm
+++ b/ios/chrome/browser/ui/search_engine_choice/fake_omnibox/fake_omnibox_view.mm
@@ -175,9 +175,12 @@
     if (_isEmptyFakeOmnibox) {
       _imageView.image = DefaultSymbolWithPointSize(kMagnifyingglassSymbol,
                                                     kMagnifyingGlassSize);
+      [self
+          setAccessibilityIdentifier:kFakeEmptyOmniboxAccessibilityIdentifier];
     } else {
       self.faviconImage = image;
       _imageView.image = image;
+      [self setAccessibilityIdentifier:kFakeOmniboxAccessibilityIdentifier];
     }
   }
   return self;
diff --git a/ios/chrome/browser/ui/search_engine_choice/search_engine_choice_constants.h b/ios/chrome/browser/ui/search_engine_choice/search_engine_choice_constants.h
index 7c23768b..fb55a1c 100644
--- a/ios/chrome/browser/ui/search_engine_choice/search_engine_choice_constants.h
+++ b/ios/chrome/browser/ui/search_engine_choice/search_engine_choice_constants.h
@@ -26,6 +26,10 @@
 
 // Accessibility identifier for the choice screen title.
 extern NSString* const kSearchEngineChoiceTitleAccessibilityIdentifier;
+// Accessibility identifier for the fake empty omnibox illustration.
+extern NSString* const kFakeEmptyOmniboxAccessibilityIdentifier;
+// Accessibility identifier for the fake omnibox illustration.
+extern NSString* const kFakeOmniboxAccessibilityIdentifier;
 // Prefix for the SearchEngineCell accessibility identifier.
 extern NSString* const kSnippetSearchEngineIdentifierPrefix;
 // `Set as Default` button accessibility identifier.
diff --git a/ios/chrome/browser/ui/search_engine_choice/search_engine_choice_constants.mm b/ios/chrome/browser/ui/search_engine_choice/search_engine_choice_constants.mm
index d641f27..5649902 100644
--- a/ios/chrome/browser/ui/search_engine_choice/search_engine_choice_constants.mm
+++ b/ios/chrome/browser/ui/search_engine_choice/search_engine_choice_constants.mm
@@ -12,6 +12,10 @@
 
 NSString* const kSearchEngineChoiceTitleAccessibilityIdentifier =
     @"SearchEngineChoiceTitleAccessibilityIdentifier";
+NSString* const kFakeEmptyOmniboxAccessibilityIdentifier =
+    @"FakeEmptyOmniboxAccessibilityIdentifier";
+NSString* const kFakeOmniboxAccessibilityIdentifier =
+    @"FakeOmniboxAccessibilityIdentifier";
 NSString* const kSnippetSearchEngineIdentifierPrefix =
     @"SnippetSearchEngineIdentifierPrefix_";
 NSString* const kSetAsDefaultSearchEngineIdentifier =
diff --git a/ios/chrome/browser/ui/search_engine_choice/search_engine_choice_coordinator.mm b/ios/chrome/browser/ui/search_engine_choice/search_engine_choice_coordinator.mm
index f051128..8ed2d92 100644
--- a/ios/chrome/browser/ui/search_engine_choice/search_engine_choice_coordinator.mm
+++ b/ios/chrome/browser/ui/search_engine_choice/search_engine_choice_coordinator.mm
@@ -86,8 +86,8 @@
   _searchEnginesTableViewController.delegate = self;
 
   _viewController = [[SearchEngineChoiceViewController alloc]
-      initWithSearchEngineTableViewController:
-          _searchEnginesTableViewController];
+      initWithSearchEngineTableViewController:_searchEnginesTableViewController
+                                       forFRE:_firstRun];
   _viewController.actionDelegate = self;
 
   _mediator = [[SearchEngineChoiceMediator alloc] init];
diff --git a/ios/chrome/browser/ui/search_engine_choice/search_engine_choice_earl_grey_ui_test_util.h b/ios/chrome/browser/ui/search_engine_choice/search_engine_choice_earl_grey_ui_test_util.h
index 357a387..476ce68 100644
--- a/ios/chrome/browser/ui/search_engine_choice/search_engine_choice_earl_grey_ui_test_util.h
+++ b/ios/chrome/browser/ui/search_engine_choice/search_engine_choice_earl_grey_ui_test_util.h
@@ -9,6 +9,13 @@
 
 #import "ios/third_party/earl_grey2/src/CommonLib/GREYConstants.h"
 
+// State of the fake omnibox illustration
+typedef NS_ENUM(NSUInteger, FakeOmniboxState) {
+  kHidden,
+  kEmpty,
+  kFull,
+};
+
 // Test methods that perform actions on the search engine choice UI
 @interface SearchEngineChoiceEarlGreyUI : NSObject
 
@@ -27,6 +34,9 @@
 // Checks that the default search engine was correctly set.
 + (void)verifyDefaultSearchEngineSetting:(NSString*)searchEngineName;
 
+// Checks the state of the fake omnibox illustration.
++ (void)verifyFakeOmniboxIllustrationState:(FakeOmniboxState)state;
+
 @end
 
 #endif  // IOS_CHROME_BROWSER_UI_SEARCH_ENGINE_CHOICE_SEARCH_ENGINE_CHOICE_EARL_GREY_UI_TEST_UTIL_H_
diff --git a/ios/chrome/browser/ui/search_engine_choice/search_engine_choice_earl_grey_ui_test_util.mm b/ios/chrome/browser/ui/search_engine_choice/search_engine_choice_earl_grey_ui_test_util.mm
index d2dd7706..b78304c 100644
--- a/ios/chrome/browser/ui/search_engine_choice/search_engine_choice_earl_grey_ui_test_util.mm
+++ b/ios/chrome/browser/ui/search_engine_choice/search_engine_choice_earl_grey_ui_test_util.mm
@@ -76,4 +76,34 @@
                                    grey_sufficientlyVisible(), nil)];
 }
 
++ (void)verifyFakeOmniboxIllustrationState:(FakeOmniboxState)state {
+  switch (state) {
+    case kHidden:
+      [[EarlGrey selectElementWithMatcher:
+                     grey_allOf(grey_accessibilityID(
+                                    kFakeEmptyOmniboxAccessibilityIdentifier),
+                                grey_sufficientlyVisible(), nil)]
+          assertWithMatcher:grey_nil()];
+      [[EarlGrey
+          selectElementWithMatcher:grey_allOf(
+                                       grey_accessibilityID(
+                                           kFakeOmniboxAccessibilityIdentifier),
+                                       grey_sufficientlyVisible(), nil)]
+          assertWithMatcher:grey_nil()];
+      break;
+    case kEmpty:
+      [[EarlGrey
+          selectElementWithMatcher:
+              grey_accessibilityID(kFakeEmptyOmniboxAccessibilityIdentifier)]
+          assertWithMatcher:grey_sufficientlyVisible()];
+      break;
+    case kFull:
+      [[EarlGrey
+          selectElementWithMatcher:grey_accessibilityID(
+                                       kFakeOmniboxAccessibilityIdentifier)]
+          assertWithMatcher:grey_sufficientlyVisible()];
+      break;
+  }
+}
+
 @end
diff --git a/ios/chrome/browser/ui/search_engine_choice/search_engine_choice_egtest.mm b/ios/chrome/browser/ui/search_engine_choice/search_engine_choice_egtest.mm
index deca1a5e..46bdf96 100644
--- a/ios/chrome/browser/ui/search_engine_choice/search_engine_choice_egtest.mm
+++ b/ios/chrome/browser/ui/search_engine_choice/search_engine_choice_egtest.mm
@@ -81,6 +81,9 @@
 - (void)testSearchEngineChoiceScreenSelectThenScroll {
   // Checks that the choice screen is shown
   [SearchEngineChoiceEarlGreyUI verifySearchEngineChoiceScreenIsDisplayed];
+  // Checks that the fake omnibox illustration is displayed and is initially
+  // empty
+  [SearchEngineChoiceEarlGreyUI verifyFakeOmniboxIllustrationState:kEmpty];
   // Verifies that the primary button is initially the "More" button.
   id<GREYMatcher> moreButtonMatcher =
       grey_accessibilityID(kSearchEngineMoreButtonIdentifier);
@@ -93,6 +96,9 @@
       selectSearchEngineCellWithName:searchEngineToSelect
                      scrollDirection:kGREYDirectionDown
                               amount:50];
+  // Checks that the fake omnibox illustration is still displayed but is no
+  // longer empty
+  [SearchEngineChoiceEarlGreyUI verifyFakeOmniboxIllustrationState:kFull];
   // Taps the primary button. This scrolls the table down to the bottom.
   [[[EarlGrey selectElementWithMatcher:moreButtonMatcher]
       assertWithMatcher:grey_notNil()] performAction:grey_tap()];
@@ -111,6 +117,9 @@
 - (void)testSearchEngineChoiceScreenScrollThenSelect {
   // Checks that the choice screen is shown
   [SearchEngineChoiceEarlGreyUI verifySearchEngineChoiceScreenIsDisplayed];
+  // Checks that the fake omnibox illustration is displayed and is initially
+  // empty
+  [SearchEngineChoiceEarlGreyUI verifyFakeOmniboxIllustrationState:kEmpty];
   // Verifies that the primary button is initially the "More" button.
   id<GREYMatcher> moreButtonMatcher =
       grey_accessibilityID(kSearchEngineMoreButtonIdentifier);
@@ -135,6 +144,9 @@
       selectSearchEngineCellWithName:searchEngineToSelect
                      scrollDirection:kGREYDirectionUp
                               amount:300];
+  // Checks that the fake omnibox illustration is still displayed but is no
+  // longer empty
+  [SearchEngineChoiceEarlGreyUI verifyFakeOmniboxIllustrationState:kFull];
   [SearchEngineChoiceEarlGreyUI confirmSearchEngineChoiceScreen];
   [SearchEngineChoiceEarlGreyUI
       verifyDefaultSearchEngineSetting:searchEngineToSelect];
diff --git a/ios/chrome/browser/ui/search_engine_choice/search_engine_choice_view_controller.h b/ios/chrome/browser/ui/search_engine_choice/search_engine_choice_view_controller.h
index 92e3a9b..2ae0487 100644
--- a/ios/chrome/browser/ui/search_engine_choice/search_engine_choice_view_controller.h
+++ b/ios/chrome/browser/ui/search_engine_choice/search_engine_choice_view_controller.h
@@ -33,7 +33,8 @@
 @property(nonatomic, assign, readwrite) BOOL didUserSelectARow;
 
 - (instancetype)initWithSearchEngineTableViewController:
-    (SearchEngineChoiceTableViewController*)tableViewController
+                    (SearchEngineChoiceTableViewController*)tableViewController
+                                                 forFRE:(BOOL)isForFRE
     NS_DESIGNATED_INITIALIZER;
 
 - (instancetype)initWithNibName:(NSString*)nibNameOrNil
diff --git a/ios/chrome/browser/ui/search_engine_choice/search_engine_choice_view_controller.mm b/ios/chrome/browser/ui/search_engine_choice/search_engine_choice_view_controller.mm
index 1ff53cd..a4e6b48 100644
--- a/ios/chrome/browser/ui/search_engine_choice/search_engine_choice_view_controller.mm
+++ b/ios/chrome/browser/ui/search_engine_choice/search_engine_choice_view_controller.mm
@@ -20,6 +20,7 @@
 #import "ios/chrome/common/ui/util/device_util.h"
 #import "ios/chrome/common/ui/util/sdk_forward_declares.h"
 #import "net/base/mac/url_conversions.h"
+#import "ui/base/device_form_factor.h"
 #import "ui/base/l10n/l10n_util_mac.h"
 #import "url/gurl.h"
 
@@ -81,14 +82,18 @@
   // Scrollable content containing everything above the primary button.
   UIScrollView* _scrollView;
   UIView* _scrollContentView;
+  // Whether the choice screen is being displayed for the FRE.
+  BOOL _isForFRE;
 }
 
 - (instancetype)initWithSearchEngineTableViewController:
-    (SearchEngineChoiceTableViewController*)tableViewController {
+                    (SearchEngineChoiceTableViewController*)tableViewController
+                                                 forFRE:(BOOL)isForFRE {
   CHECK(tableViewController);
   self = [super initWithNibName:nil bundle:nil];
   if (self) {
     _searchEngineTableViewController = tableViewController;
+    _isForFRE = isForFRE;
   }
   return self;
 }
@@ -155,8 +160,8 @@
   _fakeEmptyOmniboxView =
       [[FakeOmniboxView alloc] initWithSearchEngineName:nil faviconImage:nil];
   [_topZoneStackView addArrangedSubview:_fakeEmptyOmniboxView];
-  if (self.traitCollection.verticalSizeClass ==
-      UIUserInterfaceSizeClassCompact) {
+  if ([self shouldHideFakeOmniboxForVerticalSizeClass:self.traitCollection
+                                                          .verticalSizeClass]) {
     _fakeEmptyOmniboxView.hidden = YES;
   }
   _fakeEmptyOmniboxView.translatesAutoresizingMaskIntoConstraints = NO;
@@ -313,8 +318,8 @@
   _fakeOmniboxView.translatesAutoresizingMaskIntoConstraints = NO;
   [_topZoneStackView addSubview:_fakeOmniboxView];
   AddSameConstraints(_fakeOmniboxView, _fakeEmptyOmniboxView);
-  if (self.traitCollection.verticalSizeClass ==
-      UIUserInterfaceSizeClassCompact) {
+  if ([self shouldHideFakeOmniboxForVerticalSizeClass:self.traitCollection
+                                                          .verticalSizeClass]) {
     // If the vertical size is compact, the new fake omnibox should be added but
     // hidden (just in case the user rotate the device in portrait mode).
     // And the previous fake omnibox should be removed.
@@ -374,6 +379,15 @@
   }
 }
 
+// Whether the choice screen should hide the fake omnibox illustration.
+- (BOOL)shouldHideFakeOmniboxForVerticalSizeClass:
+    (UIUserInterfaceSizeClass)sizeClass {
+  // Hide the fake omnibox for the FRE on iPads or in landscape mode on iPhones.
+  return sizeClass == UIUserInterfaceSizeClassCompact ||
+         (_isForFRE && (ui::GetDeviceFormFactor() ==
+                        ui::DeviceFormFactor::DEVICE_FORM_FACTOR_TABLET));
+}
+
 #pragma mark - UITextViewDelegate
 
 - (BOOL)textView:(UITextView*)textView
@@ -397,10 +411,14 @@
     case UIUserInterfaceSizeClassRegular:
       _logoView.alpha = 1;
       _logoView.hidden = NO;
-      _fakeEmptyOmniboxView.alpha = 1;
-      _fakeEmptyOmniboxView.hidden = NO;
-      _fakeOmniboxView.alpha = 1;
-      _fakeOmniboxView.hidden = NO;
+      // The fake omnibox stays hidden in the FRE for iPads.
+      if (![self shouldHideFakeOmniboxForVerticalSizeClass:
+                     UIUserInterfaceSizeClassRegular]) {
+        _fakeEmptyOmniboxView.alpha = 1;
+        _fakeEmptyOmniboxView.hidden = NO;
+        _fakeOmniboxView.alpha = 1;
+        _fakeOmniboxView.hidden = NO;
+      }
       break;
 
     case UIUserInterfaceSizeClassCompact:
diff --git a/ios_internal b/ios_internal
index 2946112..e0312db 160000
--- a/ios_internal
+++ b/ios_internal
@@ -1 +1 @@
-Subproject commit 29461128ddc44d4245c80f5c10c859c21b325fbb
+Subproject commit e0312dbf4d16d483662226c982fafc3165f1aecb
diff --git a/media/base/media_switches.cc b/media/base/media_switches.cc
index 6d46f1b..4749674 100644
--- a/media/base/media_switches.cc
+++ b/media/base/media_switches.cc
@@ -1230,7 +1230,12 @@
 // ChromeOS.
 BASE_FEATURE(kChromeOSHWVBREncoding,
              "ChromeOSHWVBREncoding",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+#if defined(ARCH_CPU_X86_FAMILY)
+             base::FEATURE_ENABLED_BY_DEFAULT
+#else
+             base::FEATURE_DISABLED_BY_DEFAULT
+#endif
+);
 
 // Use a dedicated thread for hardware video decoding in Video decoder process.
 // If this is disabled, a sequenced task runner in base::ThreadPool is used for
diff --git a/media/cast/encoding/av1_encoder.cc b/media/cast/encoding/av1_encoder.cc
index ff629fd..bd6c12e 100644
--- a/media/cast/encoding/av1_encoder.cc
+++ b/media/cast/encoding/av1_encoder.cc
@@ -174,6 +174,11 @@
                << aom_codec_err_to_string(ret);
   }
 
+  CHECK_EQ(
+      aom_codec_control(&encoder_, AV1E_SET_TUNE_CONTENT, AOM_CONTENT_SCREEN),
+      AOM_CODEC_OK);
+  CHECK_EQ(aom_codec_control(&encoder_, AV1E_SET_ENABLE_PALETTE, 1),
+           AOM_CODEC_OK);
   // This cpu_used setting is a trade-off between cpu usage and encoded video
   // quality. The default is zero, with increasingly less CPU to be used as the
   // value is more positive. Starting with the highest encoding speed
diff --git a/media/cast/encoding/vpx_encoder.cc b/media/cast/encoding/vpx_encoder.cc
index 9a4d7bb..55d866f7 100644
--- a/media/cast/encoding/vpx_encoder.cc
+++ b/media/cast/encoding/vpx_encoder.cc
@@ -188,10 +188,22 @@
   // Raise the threshold for considering macroblocks as static.  The default is
   // zero, so this setting makes the encoder less sensitive to motion.  This
   // lowers the probability of needing to utilize more CPU to search for motion
-  // vectors.
-  CHECK_EQ(vpx_codec_control(&encoder_, VP8E_SET_STATIC_THRESHOLD, 1),
+  // vectors. The value is the same as WebRTC.
+  // https://source.chromium.org/chromium/chromium/src/+/main:third_party/webrtc/modules/video_coding/codecs/vp8/libvpx_vp8_encoder.cc
+  CHECK_EQ(vpx_codec_control(&encoder_, VP8E_SET_STATIC_THRESHOLD, 100),
            VPX_CODEC_OK);
 
+  if (cast_config_.codec == Codec::kVideoVp9) {
+    CHECK_EQ(vpx_codec_control(&encoder_, VP9E_SET_TUNE_CONTENT,
+                               VP9E_CONTENT_SCREEN),
+             VPX_CODEC_OK);
+  } else {
+    // Setting 1, not 2, so the libvpx encoder doesn't drop a frame.
+    CHECK_EQ(
+        vpx_codec_control(&encoder_, VP8E_SET_SCREEN_CONTENT_MODE, 1 /*On*/),
+        VPX_CODEC_OK);
+  }
+
   // This cpu_used setting is a trade-off between cpu usage and encoded video
   // quality. The default is zero, with increasingly less CPU to be used as the
   // value is more negative or more positive. The encoder does some automatic
diff --git a/media/gpu/vaapi/vaapi_wrapper.cc b/media/gpu/vaapi/vaapi_wrapper.cc
index fd97b7f..e1da8985 100644
--- a/media/gpu/vaapi/vaapi_wrapper.cc
+++ b/media/gpu/vaapi/vaapi_wrapper.cc
@@ -3045,7 +3045,7 @@
     pipeline_param->output_region = &output_region;
     pipeline_param->output_background_color = 0xff000000;
     pipeline_param->output_color_standard = VAProcColorStandardNone;
-    pipeline_param->filter_flags = VA_FILTER_SCALING_HQ;
+    pipeline_param->filter_flags = VA_FILTER_SCALING_DEFAULT;
     pipeline_param->rotation_state = VA_ROTATION_NONE;
   }
 
diff --git a/media/mojo/mojom/display_media_information.mojom b/media/mojo/mojom/display_media_information.mojom
index 7b909cf..f0693cb 100644
--- a/media/mojo/mojom/display_media_information.mojom
+++ b/media/mojo/mojom/display_media_information.mojom
@@ -25,4 +25,5 @@
   bool logical_surface;
   CursorCaptureType cursor;
   CaptureHandle? capture_handle;
+  int32 initial_zoom_level;
 };
diff --git a/net/cert/ev_root_ca_metadata.h b/net/cert/ev_root_ca_metadata.h
index c1a31c7..29ceafe4 100644
--- a/net/cert/ev_root_ca_metadata.h
+++ b/net/cert/ev_root_ca_metadata.h
@@ -28,11 +28,13 @@
 struct LazyInstanceTraitsBase;
 }  // namespace base
 
-namespace net {
-
+namespace bssl {
 namespace der {
 class Input;
 }  // namespace der
+}  // namespace bssl
+
+namespace net {
 
 // A singleton.  This class stores the meta data of the root CAs that issue
 // extended-validation (EV) certificates.
diff --git a/net/cert/time_conversions.h b/net/cert/time_conversions.h
index 84162a76..37a1030f 100644
--- a/net/cert/time_conversions.h
+++ b/net/cert/time_conversions.h
@@ -15,9 +15,13 @@
 class Time;
 }
 
-namespace net {
-
+namespace bssl {
+namespace der {
 struct GeneralizedTime;
+}  // namespace der
+}  // namespace bssl
+
+namespace net {
 
 // Encodes |time|, a UTC-based time, to DER |generalized_time|, for comparing
 // against other GeneralizedTime objects. Returns true on success or false if
diff --git a/net/dns/context_host_resolver.cc b/net/dns/context_host_resolver.cc
index c0e6054..d56aa53 100644
--- a/net/dns/context_host_resolver.cc
+++ b/net/dns/context_host_resolver.cc
@@ -74,8 +74,11 @@
     std::optional<ResolveHostParameters> optional_parameters) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-  if (shutting_down_)
+  // TODO(crbug.com/1520189): `resolve_context_` should never be nullptr when
+  // `shutting_down_` is true.
+  if (shutting_down_ || !resolve_context_) {
     return HostResolver::CreateFailingRequest(ERR_CONTEXT_SHUT_DOWN);
+  }
 
   return manager_->CreateRequest(
       Host(std::move(host)), std::move(network_anonymization_key),
@@ -91,8 +94,11 @@
     const std::optional<ResolveHostParameters>& optional_parameters) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-  if (shutting_down_)
+  // TODO(crbug.com/1520189): `resolve_context_` should never be nullptr when
+  // `shutting_down_` is true.
+  if (shutting_down_ || !resolve_context_) {
     return HostResolver::CreateFailingRequest(ERR_CONTEXT_SHUT_DOWN);
+  }
 
   return manager_->CreateRequest(host, network_anonymization_key,
                                  source_net_log, optional_parameters,
diff --git a/net/test/cert_builder.h b/net/test/cert_builder.h
index e9ce9f9..594f185 100644
--- a/net/test/cert_builder.h
+++ b/net/test/cert_builder.h
@@ -28,11 +28,13 @@
 class FilePath;
 }
 
-namespace net {
-
+namespace bssl {
 namespace der {
 class Input;
-}
+}  // namespace der
+}  // namespace bssl
+
+namespace net {
 
 // CertBuilder is a helper class to dynamically create a test certificate.
 //
diff --git a/net/test/embedded_test_server/request_handler_util.h b/net/test/embedded_test_server/request_handler_util.h
index 2128e2e..aca2d28 100644
--- a/net/test/embedded_test_server/request_handler_util.h
+++ b/net/test/embedded_test_server/request_handler_util.h
@@ -15,9 +15,7 @@
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "net/test/embedded_test_server/http_response.h"
 
-namespace url {
 class GURL;
-}
 
 namespace net::test_server {
 struct HttpRequest;
diff --git a/testing/buildbot/chrome.json b/testing/buildbot/chrome.json
index 41873506..aeebe86 100644
--- a/testing/buildbot/chrome.json
+++ b/testing/buildbot/chrome.json
@@ -1431,20 +1431,6 @@
       },
       {
         "args": [
-          "--vpython-dir=../../vpython_dir_linux_amd64",
-          "--gtest_filter=-PythonUtils.PythonRunTime"
-        ],
-        "autotest_name": "chromium",
-        "cros_board": "brya",
-        "dut_pool": "chrome",
-        "name": "net_unittests BRYA_RELEASE_LKGM",
-        "test": "net_unittests",
-        "test_id_prefix": "ninja://net:net_unittests/",
-        "use_lkgm": true,
-        "variant_id": "BRYA_RELEASE_LKGM"
-      },
-      {
-        "args": [
           "--stop-ui"
         ],
         "autotest_name": "chromium",
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index d75639a3..581decc 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -4636,7 +4636,6 @@
         "autotest_name": "tast.chrome-from-gcs",
         "bucket": "chromiumos-image-archive",
         "cros_board": "jacuzzi",
-        "cros_img": "jacuzzi-public/R122-15724.0.0",
         "name": "chrome_all_tast_tests JACUZZI_CQ_PUBLIC_LKGM",
         "public_builder": "cros_test_platform_public",
         "public_builder_bucket": "testplatform-public",
@@ -4646,6 +4645,7 @@
         "test_id_prefix": "ninja://chromeos:chrome_all_tast_tests/",
         "test_level_retries": 2,
         "timeout_sec": 14400,
+        "use_lkgm": true,
         "variant_id": "JACUZZI_CQ_PUBLIC_LKGM"
       },
       {
@@ -4653,7 +4653,6 @@
         "bucket": "chromiumos-image-archive",
         "ci_only": true,
         "cros_board": "jacuzzi",
-        "cros_img": "jacuzzi-public/R122-15724.0.0",
         "experiment_percentage": 100,
         "name": "chrome_criticalstaging_tast_tests JACUZZI_CQ_PUBLIC_LKGM",
         "public_builder": "cros_test_platform_public",
@@ -4664,6 +4663,7 @@
         "test_id_prefix": "ninja://chromeos:chrome_criticalstaging_tast_tests/",
         "test_level_retries": 2,
         "timeout_sec": 14400,
+        "use_lkgm": true,
         "variant_id": "JACUZZI_CQ_PUBLIC_LKGM"
       },
       {
@@ -4671,7 +4671,6 @@
         "bucket": "chromiumos-image-archive",
         "ci_only": true,
         "cros_board": "jacuzzi",
-        "cros_img": "jacuzzi-public/R122-15724.0.0",
         "experiment_percentage": 100,
         "name": "chrome_disabled_tast_tests JACUZZI_CQ_PUBLIC_LKGM",
         "public_builder": "cros_test_platform_public",
@@ -4682,6 +4681,7 @@
         "test_id_prefix": "ninja://chromeos:chrome_disabled_tast_tests/",
         "test_level_retries": 1,
         "timeout_sec": 14400,
+        "use_lkgm": true,
         "variant_id": "JACUZZI_CQ_PUBLIC_LKGM"
       }
     ]
@@ -4695,7 +4695,6 @@
         "autotest_name": "tast.chrome-from-gcs",
         "bucket": "chromiumos-image-archive",
         "cros_board": "octopus",
-        "cros_img": "octopus-public/R122-15724.0.0",
         "name": "chrome_all_tast_tests OCTOPUS_PUBLIC_LKGM",
         "shards": 15,
         "tast_expr": "STUB_STRING_TO_RUN_TAST_TESTS",
@@ -4703,6 +4702,7 @@
         "test_id_prefix": "ninja://chromeos:chrome_all_tast_tests/",
         "test_level_retries": 2,
         "timeout_sec": 14400,
+        "use_lkgm": true,
         "variant_id": "OCTOPUS_PUBLIC_LKGM"
       }
     ]
diff --git a/testing/buildbot/test_suite_exceptions.pyl b/testing/buildbot/test_suite_exceptions.pyl
index e9c89e50..2991105 100644
--- a/testing/buildbot/test_suite_exceptions.pyl
+++ b/testing/buildbot/test_suite_exceptions.pyl
@@ -3655,6 +3655,24 @@
         },
       },
     },
+    # TODO(b/319364912): vpython cannot be delivered via CIPD on skylab builders
+    'remove_from': [
+      'chromeos-brya-chrome-dchecks-skylab',
+      'chromeos-brya-chrome-skylab',
+      'chromeos-jacuzzi-chrome-dchecks-skylab',
+      'chromeos-jacuzzi-chrome-skylab',
+      'chromeos-jacuzzi-rel-skylab-fyi',
+      'chromeos-octopus-chrome-skylab',
+      'chromeos-octopus-rel-skylab-fyi',
+      'chromeos-trogdor-chrome-skylab-tests-fyi',
+      'chromeos-volteer-chrome-skylab',
+    ],
+  },
+  'net_unittests BRYA_RELEASE_LKGM': {
+    # TODO(b/319364912): vpython cannot be delivered via CIPD on skylab builders
+    'remove_from': [
+      'chromeos-brya-chrome-skylab',
+    ],
   },
   'not_site_per_process_blink_web_tests': {
     'remove_from': [
diff --git a/testing/buildbot/variants.pyl b/testing/buildbot/variants.pyl
index bcfb553..51c09e9 100644
--- a/testing/buildbot/variants.pyl
+++ b/testing/buildbot/variants.pyl
@@ -141,7 +141,7 @@
     'identifier': 'JACUZZI_CQ_PUBLIC_LKGM',
     'skylab': {
       'cros_board': 'jacuzzi',
-      'cros_img': 'jacuzzi-public/R122-15724.0.0',
+      'use_lkgm': True,
       'bucket': 'chromiumos-image-archive',
       'public_builder': 'cros_test_platform_public',
       'public_builder_bucket': 'testplatform-public',
@@ -165,7 +165,7 @@
     'identifier': 'OCTOPUS_PUBLIC_LKGM',
     'skylab': {
       'cros_board': 'octopus',
-      'cros_img': 'octopus-public/R122-15724.0.0',
+      'use_lkgm': True,
       'bucket': 'chromiumos-image-archive',
     },
   },
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 9f09cb9..fbf23ba 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -209,6 +209,25 @@
             ]
         }
     ],
+    "AllowBFCacheWhenClosedMediaStreamTrack": [
+        {
+            "platforms": [
+                "android",
+                "chromeos",
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled_20231219",
+                    "enable_features": [
+                        "AllowBFCacheWhenClosedMediaStreamTrack"
+                    ]
+                }
+            ]
+        }
+    ],
     "AllowUndamagedNonrootRenderPassToSkip": [
         {
             "platforms": [
@@ -6060,6 +6079,26 @@
             ]
         }
     ],
+    "DevToolsStructuredMetrics": [
+        {
+            "platforms": [
+                "windows",
+                "linux",
+                "mac",
+                "chromeos",
+                "chromeos_lacros"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "ChromeStructuredMetrics",
+                        "DevToolsVeLogging"
+                    ]
+                }
+            ]
+        }
+    ],
     "DevToolsTabTarget": [
         {
             "platforms": [
@@ -15408,7 +15447,7 @@
             ],
             "experiments": [
                 {
-                    "name": "EnabledCrashedFrameWithQueueing_20230526",
+                    "name": "EnabledCrashedFrameWithQueueing_20240108",
                     "params": {
                         "level": "crashed-frame",
                         "queueing_level": "full"
@@ -15416,6 +15455,63 @@
                     "enable_features": [
                         "QueueNavigationsWhileWaitingForCommit",
                         "RenderDocument"
+                    ],
+                    "disable_features": [
+                        "RenderDocumentCompositorReuse"
+                    ]
+                },
+                {
+                    "name": "EnabledNLRSubframeWithQueueing_20240108",
+                    "params": {
+                        "level": "non-local-root",
+                        "queueing_level": "full"
+                    },
+                    "enable_features": [
+                        "QueueNavigationsWhileWaitingForCommit",
+                        "RenderDocument"
+                    ],
+                    "disable_features": [
+                        "RenderDocumentCompositorReuse"
+                    ]
+                },
+                {
+                    "name": "EnabledSubframeWithQueueing_20240108",
+                    "params": {
+                        "level": "subframe",
+                        "queueing_level": "full"
+                    },
+                    "enable_features": [
+                        "QueueNavigationsWhileWaitingForCommit",
+                        "RenderDocument"
+                    ],
+                    "disable_features": [
+                        "RenderDocumentCompositorReuse"
+                    ]
+                },
+                {
+                    "name": "EnabledSubframeWithQueueingAndCompReuse_20240108",
+                    "params": {
+                        "level": "subframe",
+                        "queueing_level": "full"
+                    },
+                    "enable_features": [
+                        "QueueNavigationsWhileWaitingForCommit",
+                        "RenderDocument",
+                        "RenderDocumentCompositorReuse"
+                    ]
+                },
+                {
+                    "name": "EnabledAllFramesWithQueueing_20240108",
+                    "params": {
+                        "level": "all-frames",
+                        "queueing_level": "full"
+                    },
+                    "enable_features": [
+                        "QueueNavigationsWhileWaitingForCommit",
+                        "RenderDocument"
+                    ],
+                    "disable_features": [
+                        "RenderDocumentCompositorReuse"
                     ]
                 }
             ]
diff --git a/third_party/blink/public/devtools_protocol/browser_protocol.pdl b/third_party/blink/public/devtools_protocol/browser_protocol.pdl
index 97e11ee..9fe9a28 100644
--- a/third_party/blink/public/devtools_protocol/browser_protocol.pdl
+++ b/third_party/blink/public/devtools_protocol/browser_protocol.pdl
@@ -9546,15 +9546,6 @@
       started
       configResolved
 
-  # Enum of network fetches auctions can do.
-  type InterestGroupAuctionFetchType extends string
-    enum
-      bidderJs
-      bidderWasm
-      sellerJs
-      bidderTrustedSignals
-      sellerTrustedSignals
-
   # Ad advertising element inside an interest group.
   type InterestGroupAd extends object
     properties
@@ -9834,8 +9825,7 @@
     parameters
       boolean enable
 
-  # Enables/Disables issuing of interestGroupAuctionEventOccurred and
-  # interestGroupAuctionNetworkRequestCreated.
+  # Enables/Disables issuing of interestGroupAuctionEvent events.
   experimental command setInterestGroupAuctionTracking
     parameters
       boolean enable
@@ -9976,19 +9966,6 @@
       # Set for started and configResolved
       optional object auctionConfig
 
-  # Specifies which auctions a particular network fetch may be related to, and
-  # in what role. Note that it is not ordered with respect to
-  # Network.requestWillBeSent (but will happen before loadingFinished
-  # loadingFailed).
-  event interestGroupAuctionNetworkRequestCreated
-    parameters
-      InterestGroupAuctionFetchType type
-      Network.RequestId requestId
-      # This is the set of the auctions using the worklet that issued this
-      # request.  In the case of trusted signals, it's possible that only some of
-      # them actually care about the keys being queried.
-      array of InterestGroupAuctionId auctions
-
   # Shared storage was accessed by the associated page.
   # The following parameters are included in all events.
   event sharedStorageAccessed
diff --git a/third_party/blink/public/web/web_settings.h b/third_party/blink/public/web/web_settings.h
index 114e2724..c311c80 100644
--- a/third_party/blink/public/web/web_settings.h
+++ b/third_party/blink/public/web/web_settings.h
@@ -75,14 +75,6 @@
     kSubtitles
   };
 
-  // Defines the default for 'passive' field used in the AddEventListenerOptions
-  // interface when javascript calls addEventListener.
-  enum class PassiveEventListenerDefault {
-    kFalse,        // Default of false.
-    kTrue,         // Default of true.
-    kForceAllTrue  // Force all values to be true even when specified.
-  };
-
   // Sets value of a setting by its string identifier from Settings.in and
   // string representation of value. An enum's string representation is the
   // string representation of the integer value of the enum.
diff --git a/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_serializer_for_modules_test.cc b/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_serializer_for_modules_test.cc
index 9a9c75bf..2b954f84 100644
--- a/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_serializer_for_modules_test.cc
+++ b/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_serializer_for_modules_test.cc
@@ -1694,7 +1694,8 @@
   device.display_media_info = media::mojom::DisplayMediaInformation::New(
       media::mojom::DisplayCaptureSurfaceType::MONITOR,
       /*logical_surface=*/true, media::mojom::CursorCaptureType::NEVER,
-      /*capture_handle=*/nullptr);
+      /*capture_handle=*/nullptr,
+      /*initial_zoom_level=*/100);
   mock_source->SetDevice(device);
   MediaStreamSource* source = MakeGarbageCollected<MediaStreamSource>(
       "test_id", MediaStreamSource::StreamType::kTypeVideo, "test_name",
@@ -1748,7 +1749,8 @@
   device.display_media_info = media::mojom::DisplayMediaInformation::New(
       media::mojom::DisplayCaptureSurfaceType::WINDOW,
       /*logical_surface=*/true, media::mojom::CursorCaptureType::NEVER,
-      /*capture_handle=*/nullptr);
+      /*capture_handle=*/nullptr,
+      /*zoom_level=*/100);
   mock_source->SetDevice(device);
   MediaStreamSource* source = MakeGarbageCollected<MediaStreamSource>(
       "test_id", MediaStreamSource::StreamType::kTypeVideo, "test_name",
@@ -1872,7 +1874,8 @@
   device.display_media_info = media::mojom::DisplayMediaInformation::New(
       media::mojom::DisplayCaptureSurfaceType::BROWSER,
       /*logical_surface=*/true, media::mojom::CursorCaptureType::NEVER,
-      /*capture_handle=*/nullptr);
+      /*capture_handle=*/nullptr,
+      /*zoom_level=*/100);
   mock_source->SetDevice(device);
   MediaStreamSource* source = MakeGarbageCollected<MediaStreamSource>(
       "test_id", MediaStreamSource::StreamType::kTypeVideo, "test_name",
diff --git a/third_party/blink/renderer/core/dom/build.gni b/third_party/blink/renderer/core/dom/build.gni
index 29f26ed..72186e93 100644
--- a/third_party/blink/renderer/core/dom/build.gni
+++ b/third_party/blink/renderer/core/dom/build.gni
@@ -110,7 +110,6 @@
   "element_traversal.h",
   "empty_node_list.cc",
   "empty_node_list.h",
-  "events/add_event_listener_options_defaults.h",
   "events/add_event_listener_options_resolved.cc",
   "events/add_event_listener_options_resolved.h",
   "events/custom_event.cc",
diff --git a/third_party/blink/renderer/core/dom/events/add_event_listener_options_defaults.h b/third_party/blink/renderer/core/dom/events/add_event_listener_options_defaults.h
deleted file mode 100644
index 253fac0..0000000
--- a/third_party/blink/renderer/core/dom/events/add_event_listener_options_defaults.h
+++ /dev/null
@@ -1,24 +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.
-
-#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_DOM_EVENTS_ADD_EVENT_LISTENER_OPTIONS_DEFAULTS_H_
-#define THIRD_PARTY_BLINK_RENDERER_CORE_DOM_EVENTS_ADD_EVENT_LISTENER_OPTIONS_DEFAULTS_H_
-
-namespace blink {
-
-// Defines the default for 'passive' field used in the AddEventListenerOptions
-// interface when javascript calls addEventListener.
-// |False| is the default specified in
-// https://dom.spec.whatwg.org/#dictdef-addeventlisteneroptions. However
-// specifying a different default value is useful in demonstrating the
-// power of passive event listeners.
-enum class PassiveListenerDefault {
-  kFalse,        // Default of false.
-  kTrue,         // Default of true.
-  kForceAllTrue  // Force all values to be true even when specified.
-};
-
-}  // namespace blink
-
-#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_DOM_EVENTS_ADD_EVENT_LISTENER_OPTIONS_DEFAULTS_H_
diff --git a/third_party/blink/renderer/core/dom/events/event_target.cc b/third_party/blink/renderer/core/dom/events/event_target.cc
index b53751e..3aa4d80 100644
--- a/third_party/blink/renderer/core/dom/events/event_target.cc
+++ b/third_party/blink/renderer/core/dom/events/event_target.cc
@@ -35,7 +35,6 @@
 
 #include "base/format_macros.h"
 #include "base/time/time.h"
-#include "third_party/blink/public/web/web_settings.h"
 #include "third_party/blink/renderer/bindings/core/v8/js_based_event_listener.h"
 #include "third_party/blink/renderer/bindings/core/v8/js_event_listener.h"
 #include "third_party/blink/renderer/bindings/core/v8/to_v8_traits.h"
@@ -69,7 +68,6 @@
 #include "third_party/blink/renderer/platform/instrumentation/histogram.h"
 #include "third_party/blink/renderer/platform/instrumentation/use_counter.h"
 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
-#include "third_party/blink/renderer/platform/wtf/assertions.h"
 #include "third_party/blink/renderer/platform/wtf/functional.h"
 #include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
 #include "third_party/blink/renderer/platform/wtf/threading.h"
@@ -1165,11 +1163,4 @@
   visitor->Trace(data_);
 }
 
-STATIC_ASSERT_ENUM(WebSettings::PassiveEventListenerDefault::kFalse,
-                   PassiveListenerDefault::kFalse);
-STATIC_ASSERT_ENUM(WebSettings::PassiveEventListenerDefault::kTrue,
-                   PassiveListenerDefault::kTrue);
-STATIC_ASSERT_ENUM(WebSettings::PassiveEventListenerDefault::kForceAllTrue,
-                   PassiveListenerDefault::kForceAllTrue);
-
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/frame/settings.h b/third_party/blink/renderer/core/frame/settings.h
index d07abd5..5d8f3e0 100644
--- a/third_party/blink/renderer/core/frame/settings.h
+++ b/third_party/blink/renderer/core/frame/settings.h
@@ -34,7 +34,6 @@
 #include "third_party/blink/public/mojom/webpreferences/web_preferences.mojom-blink.h"
 #include "third_party/blink/public/platform/web_effective_connection_type.h"
 #include "third_party/blink/renderer/core/core_export.h"
-#include "third_party/blink/renderer/core/dom/events/add_event_listener_options_defaults.h"
 #include "third_party/blink/renderer/core/editing/selection_strategy.h"
 #include "third_party/blink/renderer/core/frame/settings_delegate.h"
 #include "third_party/blink/renderer/core/html/media/autoplay_policy.h"
diff --git a/third_party/blink/renderer/modules/BUILD.gn b/third_party/blink/renderer/modules/BUILD.gn
index 1cd16d863..b277878 100644
--- a/third_party/blink/renderer/modules/BUILD.gn
+++ b/third_party/blink/renderer/modules/BUILD.gn
@@ -4,11 +4,11 @@
 
 import("//build/config/cast.gni")
 import("//build/config/python.gni")
+import("//media/media_options.gni")
 import("//testing/libfuzzer/fuzzer_test.gni")
 import("//third_party/blink/renderer/bindings/bindings.gni")
 import("//third_party/blink/renderer/build/scripts/scripts.gni")
 import("//third_party/blink/renderer/modules/modules.gni")
-import("//third_party/webrtc/webrtc.gni")
 
 if (is_android) {
   import("//build/config/android/rules.gni")
@@ -301,7 +301,6 @@
     "//third_party/blink/public/mojom:mojom_modules_blink_headers",
     "//third_party/blink/renderer/bindings/modules/v8:generated",
     "//third_party/blink/renderer/core:core_event_interfaces",
-    "//third_party/blink/renderer/modules/mediarecorder:buildflags",
     "//ui/accessibility:ax_enums_mojo_blink_headers",
     "//ui/display/mojom:mojom_blink_headers",
   ]
@@ -640,7 +639,7 @@
     ]
   }
 
-  if (rtc_use_h264) {
+  if (media_use_openh264) {
     sources += [ "mediarecorder/h264_encoder_unittest.cc" ]
   }
 
@@ -688,7 +687,6 @@
     "//third_party/blink/renderer/modules/hid:unit_tests",
     "//third_party/blink/renderer/modules/idle:unit_tests",
     "//third_party/blink/renderer/modules/indexeddb:unit_tests",
-    "//third_party/blink/renderer/modules/mediarecorder:buildflags",
     "//third_party/blink/renderer/modules/mediastream:test_support",
     "//third_party/blink/renderer/modules/ml:unit_tests",
     "//third_party/blink/renderer/modules/peerconnection:test_support",
@@ -982,7 +980,7 @@
     ]
   }
 
-  if (rtc_use_h264) {
+  if (media_use_openh264) {
     sources += [ "mediarecorder/h264_encoder_unittest.cc" ]
   }
 
@@ -1030,7 +1028,6 @@
     "//third_party/blink/renderer/modules/hid:unit_tests",
     "//third_party/blink/renderer/modules/idle:unit_tests",
     "//third_party/blink/renderer/modules/indexeddb:unit_tests",
-    "//third_party/blink/renderer/modules/mediarecorder:buildflags",
     "//third_party/blink/renderer/modules/mediastream:test_support",
     "//third_party/blink/renderer/modules/ml:unit_tests",
     "//third_party/blink/renderer/modules/peerconnection:test_support",
diff --git a/third_party/blink/renderer/modules/mediarecorder/BUILD.gn b/third_party/blink/renderer/modules/mediarecorder/BUILD.gn
index 078ae08..f05ad3e 100644
--- a/third_party/blink/renderer/modules/mediarecorder/BUILD.gn
+++ b/third_party/blink/renderer/modules/mediarecorder/BUILD.gn
@@ -6,12 +6,6 @@
 import("//media/media_options.gni")
 import("//third_party/blink/renderer/modules/modules.gni")
 import("//third_party/libaom/options.gni")
-import("//third_party/webrtc/webrtc.gni")
-
-buildflag_header("buildflags") {
-  header = "buildflags.h"
-  flags = [ "RTC_USE_H264=$rtc_use_h264" ]
-}
 
 blink_modules_sources("mediarecorder") {
   sources = [
@@ -46,7 +40,6 @@
   ]
 
   deps = [
-    ":buildflags",
     "//build:chromeos_buildflags",
     "//media",
     "//media/mojo/clients",
@@ -57,7 +50,7 @@
     "//third_party/opus",
   ]
 
-  if (rtc_use_h264) {
+  if (media_use_openh264) {
     sources += [
       "h264_encoder.cc",
       "h264_encoder.h",
diff --git a/third_party/blink/renderer/modules/mediarecorder/h264_encoder.cc b/third_party/blink/renderer/modules/mediarecorder/h264_encoder.cc
index c19b954..985f071 100644
--- a/third_party/blink/renderer/modules/mediarecorder/h264_encoder.cc
+++ b/third_party/blink/renderer/modules/mediarecorder/h264_encoder.cc
@@ -90,11 +90,13 @@
     const VideoTrackRecorder::OnEncodedVideoCB& on_encoded_video_cb,
     VideoTrackRecorder::CodecProfile codec_profile,
     uint32_t bits_per_second,
+    bool is_screencast,
     const VideoTrackRecorder::OnErrorCB on_error_cb)
     : Encoder(std::move(encoding_task_runner),
               on_encoded_video_cb,
               bits_per_second),
       codec_profile_(codec_profile),
+      is_screencast_(is_screencast),
       on_error_cb_(on_error_cb) {
   DCHECK_EQ(codec_profile_.codec_id, VideoTrackRecorder::CodecId::kH264);
 }
@@ -207,7 +209,8 @@
 
   SEncParamExt init_params;
   openh264_encoder_->GetDefaultParams(&init_params);
-  init_params.iUsageType = CAMERA_VIDEO_REAL_TIME;
+  init_params.iUsageType =
+      is_screencast_ ? SCREEN_CONTENT_REAL_TIME : CAMERA_VIDEO_REAL_TIME;
 
   DCHECK_EQ(AUTO_REF_PIC_COUNT, init_params.iNumRefFrame);
   DCHECK(!init_params.bSimulcastAVC);
diff --git a/third_party/blink/renderer/modules/mediarecorder/h264_encoder.h b/third_party/blink/renderer/modules/mediarecorder/h264_encoder.h
index df18917..7bfb4e4 100644
--- a/third_party/blink/renderer/modules/mediarecorder/h264_encoder.h
+++ b/third_party/blink/renderer/modules/mediarecorder/h264_encoder.h
@@ -6,11 +6,11 @@
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIARECORDER_H264_ENCODER_H_
 
 #include "base/task/sequenced_task_runner.h"
-#include "third_party/blink/renderer/modules/mediarecorder/buildflags.h"
+#include "media/media_buildflags.h"
 
-#if !BUILDFLAG(RTC_USE_H264)
-#error RTC_USE_H264 should be defined.
-#endif  // #if BUILDFLAG(RTC_USE_H264)
+#if !BUILDFLAG(ENABLE_OPENH264)
+#error ENABLE_OPENH264 should be defined.
+#endif  // #if BUILDFLAG(ENABLE_OPENH264)
 
 #include "base/time/time.h"
 #include "third_party/blink/renderer/modules/mediarecorder/video_track_recorder.h"
@@ -30,6 +30,7 @@
               const VideoTrackRecorder::OnEncodedVideoCB& on_encoded_video_cb,
               VideoTrackRecorder::CodecProfile codec_profile,
               uint32_t bits_per_second,
+              bool is_screencast,
               const VideoTrackRecorder::OnErrorCB on_error_cb);
   ~H264Encoder() override;
 
@@ -54,6 +55,7 @@
 
   // TODO(inker): Move this field into VideoTrackRecorder::Encoder.
   const VideoTrackRecorder::CodecProfile codec_profile_;
+  const bool is_screencast_;
 
   const VideoTrackRecorder::OnErrorCB on_error_cb_;
 
diff --git a/third_party/blink/renderer/modules/mediarecorder/h264_encoder_unittest.cc b/third_party/blink/renderer/modules/mediarecorder/h264_encoder_unittest.cc
index 11417827..a51b503 100644
--- a/third_party/blink/renderer/modules/mediarecorder/h264_encoder_unittest.cc
+++ b/third_party/blink/renderer/modules/mediarecorder/h264_encoder_unittest.cc
@@ -72,6 +72,7 @@
                                              profile_,
                                              level_),
             bitrate_,
+            /*is_screencast=*/false,
             base::BindRepeating(&H264EncoderFixture::OnError,
                                 CrossThreadUnretained(this))) {
     auto metrics_provider =
diff --git a/third_party/blink/renderer/modules/mediarecorder/media_recorder_encoder_wrapper.cc b/third_party/blink/renderer/modules/mediarecorder/media_recorder_encoder_wrapper.cc
index e19eb62..ef77e94 100644
--- a/third_party/blink/renderer/modules/mediarecorder/media_recorder_encoder_wrapper.cc
+++ b/third_party/blink/renderer/modules/mediarecorder/media_recorder_encoder_wrapper.cc
@@ -11,7 +11,6 @@
 #include "media/media_buildflags.h"
 #include "media/video/alpha_video_encoder_wrapper.h"
 #include "media/video/gpu_video_accelerator_factories.h"
-#include "third_party/blink/renderer/modules/mediarecorder/buildflags.h"
 #include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
 
 namespace blink {
diff --git a/third_party/blink/renderer/modules/mediarecorder/media_recorder_handler.cc b/third_party/blink/renderer/modules/mediarecorder/media_recorder_handler.cc
index 42a0892c..8984356 100644
--- a/third_party/blink/renderer/modules/mediarecorder/media_recorder_handler.cc
+++ b/third_party/blink/renderer/modules/mediarecorder/media_recorder_handler.cc
@@ -25,6 +25,7 @@
 #include "media/base/video_codecs.h"
 #include "media/base/video_frame.h"
 #include "media/formats/mp4/mp4_status.h"
+#include "media/media_buildflags.h"
 #include "media/mojo/clients/mojo_video_encoder_metrics_provider.h"
 #include "media/muxers/live_webm_muxer_delegate.h"
 #include "media/muxers/mp4_muxer.h"
@@ -34,7 +35,6 @@
 #include "media/muxers/webm_muxer.h"
 #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/mediarecorder/buildflags.h"
 #include "third_party/blink/renderer/modules/mediarecorder/media_recorder.h"
 #include "third_party/blink/renderer/modules/mediastream/media_stream_video_track.h"
 #include "third_party/blink/renderer/platform/bindings/exception_code.h"
diff --git a/third_party/blink/renderer/modules/mediarecorder/media_recorder_handler_unittest.cc b/third_party/blink/renderer/modules/mediarecorder/media_recorder_handler_unittest.cc
index f4d7bc8..de5f611 100644
--- a/third_party/blink/renderer/modules/mediarecorder/media_recorder_handler_unittest.cc
+++ b/third_party/blink/renderer/modules/mediarecorder/media_recorder_handler_unittest.cc
@@ -296,7 +296,7 @@
   }
 
   bool IsCodecSupported() {
-#if !BUILDFLAG(RTC_USE_H264)
+#if !BUILDFLAG(ENABLE_OPENH264)
     // Test requires OpenH264 encoder. It can't use the VEA encoder.
     if (std::string(GetParam().codecs) == "avc1") {
       return false;
diff --git a/third_party/blink/renderer/modules/mediarecorder/video_track_recorder.cc b/third_party/blink/renderer/modules/mediarecorder/video_track_recorder.cc
index cf2c431..7d2496d 100644
--- a/third_party/blink/renderer/modules/mediarecorder/video_track_recorder.cc
+++ b/third_party/blink/renderer/modules/mediarecorder/video_track_recorder.cc
@@ -31,7 +31,6 @@
 #include "media/video/vpx_video_encoder.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/platform/web_graphics_context_3d_provider.h"
-#include "third_party/blink/renderer/modules/mediarecorder/buildflags.h"
 #include "third_party/blink/renderer/modules/mediarecorder/media_recorder_encoder_wrapper.h"
 #include "third_party/blink/renderer/modules/mediarecorder/vea_encoder.h"
 #include "third_party/blink/renderer/modules/mediarecorder/vpx_encoder.h"
@@ -49,10 +48,10 @@
 #include "third_party/libyuv/include/libyuv.h"
 #include "ui/gfx/geometry/size.h"
 
-#if BUILDFLAG(RTC_USE_H264)
+#if BUILDFLAG(ENABLE_OPENH264)
 #include "media/video/openh264_video_encoder.h"
 #include "third_party/blink/renderer/modules/mediarecorder/h264_encoder.h"
-#endif  // #if BUILDFLAG(RTC_USE_H264)
+#endif  // #if BUILDFLAG(ENABLE_OPENH264)
 
 #if BUILDFLAG(ENABLE_LIBAOM)
 #include "media/video/av1_video_encoder.h"
@@ -240,7 +239,7 @@
 }
 
 bool MustUseVEA(CodecId codec_id) {
-#if BUILDFLAG(USE_PROPRIETARY_CODECS) && !BUILDFLAG(RTC_USE_H264)
+#if BUILDFLAG(USE_PROPRIETARY_CODECS) && !BUILDFLAG(ENABLE_OPENH264)
   return codec_id == CodecId::kH264;
 #else
   return false;
@@ -263,14 +262,14 @@
 MediaRecorderEncoderWrapper::CreateEncoderCB
 GetCreateSoftwareVideoEncoderCallback(CodecId codec_id) {
   switch (codec_id) {
-#if BUILDFLAG(RTC_USE_H264)
+#if BUILDFLAG(ENABLE_OPENH264)
     case CodecId::kH264:
       return ConvertToBaseRepeatingCallback(WTF::CrossThreadBindRepeating(
           [](media::GpuVideoAcceleratorFactories* /*gpu_factories*/)
               -> std::unique_ptr<media::VideoEncoder> {
             return std::make_unique<media::OpenH264VideoEncoder>();
           }));
-#endif  // BUILDFLAG(RTC_USE_H264)
+#endif  // BUILDFLAG(ENABLE_OPENH264)
 #if BUILDFLAG(ENABLE_LIBVPX)
     case CodecId::kVp8:
     case CodecId::kVp9:
@@ -823,12 +822,12 @@
   media::VideoCodecProfile video_codec_profile =
       media::VIDEO_CODEC_PROFILE_UNKNOWN;
   switch (codec_profile.codec_id) {
-#if BUILDFLAG(RTC_USE_H264)
+#if BUILDFLAG(ENABLE_OPENH264)
     case CodecId::kH264:
       video_codec_profile =
           codec_profile.profile.value_or(media::H264PROFILE_BASELINE);
       break;
-#endif  // BUILDFLAG(RTC_USE_H264)
+#endif  // BUILDFLAG(ENABLE_OPENH264)
     case CodecId::kVp8:
       video_codec_profile =
           codec_profile.profile.value_or(media::VP8PROFILE_ANY);
@@ -887,11 +886,11 @@
     const OnEncodedVideoCB& on_encoded_video_cb) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(main_sequence_checker_);
   switch (codec_profile.codec_id) {
-#if BUILDFLAG(RTC_USE_H264)
+#if BUILDFLAG(ENABLE_OPENH264)
     case CodecId::kH264:
       return std::make_unique<H264Encoder>(
           std::move(encoding_task_runner), on_encoded_video_cb, codec_profile,
-          bits_per_second,
+          bits_per_second, is_screencast,
           base::BindPostTask(
               main_thread_task_runner_,
               WTF::BindRepeating(&CallbackInterface::OnVideoEncodingError,
@@ -902,7 +901,7 @@
       return std::make_unique<VpxEncoder>(
           std::move(encoding_task_runner),
           codec_profile.codec_id == CodecId::kVp9, on_encoded_video_cb,
-          bits_per_second,
+          bits_per_second, is_screencast,
           base::BindPostTask(
               main_thread_task_runner_,
               WTF::BindRepeating(&CallbackInterface::OnVideoEncodingError,
@@ -998,7 +997,7 @@
   const bool can_use_vea = CanUseAcceleratedEncoder(
       codec_profile.codec_id, input_size.width(), input_size.height());
 
-#if BUILDFLAG(USE_PROPRIETARY_CODECS) && !BUILDFLAG(RTC_USE_H264)
+#if BUILDFLAG(USE_PROPRIETARY_CODECS) && !BUILDFLAG(ENABLE_OPENH264)
   if (MustUseVEA(codec_profile.codec_id) &&
       (!allow_vea_encoder || !can_use_vea)) {
     // This should only happen if the H264 isn't supported by the VEA or an
diff --git a/third_party/blink/renderer/modules/mediarecorder/video_track_recorder.h b/third_party/blink/renderer/modules/mediarecorder/video_track_recorder.h
index 875115b71..0de196ad 100644
--- a/third_party/blink/renderer/modules/mediarecorder/video_track_recorder.h
+++ b/third_party/blink/renderer/modules/mediarecorder/video_track_recorder.h
@@ -18,6 +18,7 @@
 #include "media/base/video_encoder.h"
 #include "media/base/video_frame_converter.h"
 #include "media/base/video_frame_pool.h"
+#include "media/media_buildflags.h"
 #include "media/muxers/webm_muxer.h"
 #include "media/renderers/paint_canvas_video_renderer.h"
 #include "media/video/video_encode_accelerator.h"
@@ -26,7 +27,6 @@
 #include "third_party/blink/public/platform/web_graphics_context_3d_provider.h"
 #include "third_party/blink/public/web/modules/mediastream/encoded_video_frame.h"
 #include "third_party/blink/public/web/modules/mediastream/media_stream_video_sink.h"
-#include "third_party/blink/renderer/modules/mediarecorder/buildflags.h"
 #include "third_party/blink/renderer/modules/mediarecorder/key_frame_request_processor.h"
 #include "third_party/blink/renderer/modules/mediarecorder/track_recorder.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
diff --git a/third_party/blink/renderer/modules/mediarecorder/video_track_recorder_unittest.cc b/third_party/blink/renderer/modules/mediarecorder/video_track_recorder_unittest.cc
index 2337caf..a11e629 100644
--- a/third_party/blink/renderer/modules/mediarecorder/video_track_recorder_unittest.cc
+++ b/third_party/blink/renderer/modules/mediarecorder/video_track_recorder_unittest.cc
@@ -26,7 +26,6 @@
 #include "third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h"
 #include "third_party/blink/public/web/modules/mediastream/media_stream_video_source.h"
 #include "third_party/blink/public/web/web_heap.h"
-#include "third_party/blink/renderer/modules/mediarecorder/buildflags.h"
 #include "third_party/blink/renderer/modules/mediarecorder/fake_encoded_video_frame.h"
 #include "third_party/blink/renderer/modules/mediastream/media_stream_video_track.h"
 #include "third_party/blink/renderer/modules/mediastream/mock_media_stream_video_source.h"
@@ -74,7 +73,7 @@
 const VideoTrackRecorder::CodecId kTrackRecorderTestCodec[] = {
     VideoTrackRecorder::CodecId::kVp8,
     VideoTrackRecorder::CodecId::kVp9,
-#if BUILDFLAG(RTC_USE_H264)
+#if BUILDFLAG(ENABLE_OPENH264)
     VideoTrackRecorder::CodecId::kH264,
 #endif
 #if BUILDFLAG(ENABLE_LIBAOM)
@@ -96,7 +95,7 @@
       return media::VideoCodec::kVP9;
 // Note: The H264 tests in this file are written explicitly for OpenH264 and
 // will fail for hardware encoders that aren't 1 in 1 out.
-#if BUILDFLAG(RTC_USE_H264)
+#if BUILDFLAG(ENABLE_OPENH264)
     case VideoTrackRecorder::CodecId::kH264:
       return media::VideoCodec::kH264;
 #endif
@@ -120,7 +119,7 @@
       return media::VideoCodecProfile::VP9PROFILE_PROFILE0;
 // Note: The H264 tests in this file are written explicitly for OpenH264 and
 // will fail for hardware encoders that aren't 1 in 1 out.
-#if BUILDFLAG(RTC_USE_H264)
+#if BUILDFLAG(ENABLE_OPENH264)
     case VideoTrackRecorder::CodecId::kH264:
       return media::VideoCodecProfile::H264PROFILE_MIN;
 #endif
@@ -815,7 +814,7 @@
     case VideoTrackRecorder::CodecId::kVp9:
       ss << "vp9";
       break;
-#if BUILDFLAG(RTC_USE_H264)
+#if BUILDFLAG(ENABLE_OPENH264)
     case VideoTrackRecorder::CodecId::kH264:
       ss << "h264";
       break;
@@ -1366,7 +1365,7 @@
       emulator.GetFirstSupportedVideoCodecProfile(CodecId::kVp8));
 }
 
-#if BUILDFLAG(RTC_USE_H264)
+#if BUILDFLAG(ENABLE_OPENH264)
 TEST_F(CodecEnumeratorTest, FindSupportedVideoCodecProfileH264) {
   const CodecEnumerator emulator(MakeH264Profiles());
   EXPECT_EQ(std::make_pair(media::H264PROFILE_HIGH, /*vbr_support=*/false),
diff --git a/third_party/blink/renderer/modules/mediarecorder/vpx_encoder.cc b/third_party/blink/renderer/modules/mediarecorder/vpx_encoder.cc
index ce77c48..a49e432 100644
--- a/third_party/blink/renderer/modules/mediarecorder/vpx_encoder.cc
+++ b/third_party/blink/renderer/modules/mediarecorder/vpx_encoder.cc
@@ -41,11 +41,13 @@
     bool use_vp9,
     const VideoTrackRecorder::OnEncodedVideoCB& on_encoded_video_cb,
     uint32_t bits_per_second,
+    bool is_screencast,
     const VideoTrackRecorder::OnErrorCB on_error_cb)
     : Encoder(std::move(encoding_task_runner),
               on_encoded_video_cb,
               bits_per_second),
       use_vp9_(use_vp9),
+      is_screencast_(is_screencast),
       on_error_cb_(on_error_cb) {
   std::memset(&codec_config_, 0, sizeof(codec_config_));
   std::memset(&alpha_codec_config_, 0, sizeof(alpha_codec_config_));
@@ -331,6 +333,22 @@
     result = vpx_codec_control(encoder->get(), VP8E_SET_CPUUSED, kCpuUsed);
     DLOG_IF(WARNING, VPX_CODEC_OK != result) << "VP8E_SET_CPUUSED failed";
   }
+
+  // Tune configs for screen sharing. The values are the same as WebRTC
+  // https://source.chromium.org/chromium/chromium/src/+/main:third_party/webrtc/modules/video_coding/codecs/vp8/libvpx_vp8_encoder.cc
+  // https://source.chromium.org/chromium/chromium/src/+/main:third_party/webrtc/modules/video_coding/codecs/vp9/libvpx_vp9_encoder.cc
+  vpx_codec_control(encoder->get(), VP8E_SET_STATIC_THRESHOLD,
+                    (is_screencast_ && !use_vp9_) ? 100 : 1);
+  if (is_screencast_) {
+    if (use_vp9_) {
+      vpx_codec_control(encoder->get(), VP9E_SET_TUNE_CONTENT,
+                        VP9E_CONTENT_SCREEN);
+    } else {
+      // Setting 1, not 2, so the libvpx encoder doesn't drop a frame.
+      vpx_codec_control(encoder->get(), VP8E_SET_SCREEN_CONTENT_MODE, 1 /*On*/);
+    }
+  }
+
   return true;
 }
 
diff --git a/third_party/blink/renderer/modules/mediarecorder/vpx_encoder.h b/third_party/blink/renderer/modules/mediarecorder/vpx_encoder.h
index 03fae9a..76680ed 100644
--- a/third_party/blink/renderer/modules/mediarecorder/vpx_encoder.h
+++ b/third_party/blink/renderer/modules/mediarecorder/vpx_encoder.h
@@ -30,8 +30,8 @@
              bool use_vp9,
              const VideoTrackRecorder::OnEncodedVideoCB& on_encoded_video_cb,
              uint32_t bits_per_second,
+             bool is_screencast,
              const VideoTrackRecorder::OnErrorCB on_error_cb);
-
   VpxEncoder(const VpxEncoder&) = delete;
   VpxEncoder& operator=(const VpxEncoder&) = delete;
 
@@ -73,6 +73,7 @@
 
   // Force usage of VP9 for encoding, instead of VP8 which is the default.
   const bool use_vp9_;
+  const bool is_screencast_;
 
   const VideoTrackRecorder::OnErrorCB on_error_cb_;
 
diff --git a/third_party/blink/renderer/modules/mediastream/capture_controller_test.cc b/third_party/blink/renderer/modules/mediastream/capture_controller_test.cc
index ce0f7ca..4fd9c30 100644
--- a/third_party/blink/renderer/modules/mediastream/capture_controller_test.cc
+++ b/third_party/blink/renderer/modules/mediastream/capture_controller_test.cc
@@ -100,7 +100,8 @@
   device.display_media_info = media::mojom::DisplayMediaInformation::New(
       display_surface,
       /*logical_surface=*/true, media::mojom::CursorCaptureType::NEVER,
-      /*capture_handle=*/nullptr);
+      /*capture_handle=*/nullptr,
+      /*initial_zoom_level=*/100);
   media_stream_video_source->SetDevice(device);
 
   auto media_stream_video_track = std::make_unique<MediaStreamVideoTrack>(
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream_track_transfer_test.cc b/third_party/blink/renderer/modules/mediastream/media_stream_track_transfer_test.cc
index 7110fb6..ee92744 100644
--- a/third_party/blink/renderer/modules/mediastream/media_stream_track_transfer_test.cc
+++ b/third_party/blink/renderer/modules/mediastream/media_stream_track_transfer_test.cc
@@ -156,7 +156,8 @@
   device.display_media_info = media::mojom::DisplayMediaInformation::New(
       media::mojom::DisplayCaptureSurfaceType::BROWSER,
       /*logical_surface=*/true, media::mojom::CursorCaptureType::NEVER,
-      /*capture_handle=*/nullptr);
+      /*capture_handle=*/nullptr,
+      /*zoom_level=*/100);
   return {absl::nullopt, device};
 }
 
@@ -233,7 +234,8 @@
   device.display_media_info = media::mojom::DisplayMediaInformation::New(
       media::mojom::DisplayCaptureSurfaceType::BROWSER,
       /*logical_surface=*/true, media::mojom::CursorCaptureType::NEVER,
-      /*capture_handle=*/nullptr);
+      /*capture_handle=*/nullptr,
+      /*zoom_level=*/100);
   scoped_user_media_client.display_mock_media_stream_dispatcher_host
       .SetStreamDevices({absl::nullopt, device});
 
diff --git a/third_party/blink/renderer/modules/mediastream/test/transfer_test_utils.cc b/third_party/blink/renderer/modules/mediastream/test/transfer_test_utils.cc
index fdfd7f8..42fbaf9 100644
--- a/third_party/blink/renderer/modules/mediastream/test/transfer_test_utils.cc
+++ b/third_party/blink/renderer/modules/mediastream/test/transfer_test_utils.cc
@@ -57,7 +57,8 @@
   device.display_media_info = media::mojom::DisplayMediaInformation::New(
       media::mojom::DisplayCaptureSurfaceType::BROWSER,
       /*logical_surface=*/true, media::mojom::CursorCaptureType::NEVER,
-      /*capture_handle=*/nullptr);
+      /*capture_handle=*/nullptr,
+      /*initial_zoom_level=*/100);
   mock_source->SetDevice(device);
   MediaStreamSource* source = MakeGarbageCollected<MediaStreamSource>(
       "test_id", MediaStreamSource::StreamType::kTypeVideo, "test_name",
@@ -83,7 +84,8 @@
   device.display_media_info = media::mojom::DisplayMediaInformation::New(
       media::mojom::DisplayCaptureSurfaceType::BROWSER,
       /*logical_surface=*/true, media::mojom::CursorCaptureType::NEVER,
-      /*capture_handle=*/nullptr);
+      /*capture_handle=*/nullptr,
+      /*initial_zoom_level=*/100);
   mock_source->SetDevice(device);
   MediaStreamSource* source = MakeGarbageCollected<MediaStreamSource>(
       "test_id", MediaStreamSource::StreamType::kTypeAudio, "test_name",
diff --git a/third_party/blink/renderer/modules/ml/webnn/ml_graph_xnnpack.cc b/third_party/blink/renderer/modules/ml/webnn/ml_graph_xnnpack.cc
index 5b3b5593..7aa20d3e 100644
--- a/third_party/blink/renderer/modules/ml/webnn/ml_graph_xnnpack.cc
+++ b/third_party/blink/renderer/modules/ml/webnn/ml_graph_xnnpack.cc
@@ -1217,14 +1217,21 @@
 
   // Set window sizes of XNNPACK pooling 2d Node.
   uint32_t input_height, input_width;
+  uint32_t output_height, output_width;
   uint32_t filter_height, filter_width;
   bool global_pooling = false;
   switch (options->layout().AsEnum()) {
     case V8MLInputOperandLayout::Enum::kNhwc: {
       const auto* input = pool2d->Inputs()[0].Get();
-      DCHECK(input);
+      CHECK(input);
       input_height = input->Dimensions()[1];
       input_width = input->Dimensions()[2];
+
+      const auto* output = pool2d->Outputs()[0].Get();
+      CHECK(output);
+      output_height = output->Dimensions()[1];
+      output_width = output->Dimensions()[2];
+
       if (options->hasWindowDimensions()) {
         filter_height = options->windowDimensions()[0];
         filter_width = options->windowDimensions()[1];
@@ -1249,9 +1256,27 @@
   }
 
   // Set or calculate padding sizes of XNNPACK pooling 2d Node.
-  const auto padding = GetXnnPadding2D(
-      options, input_height, input_width, filter_height, filter_width,
-      stride_height, stride_width, dilation_height, dilation_width);
+  auto padding = GetXnnPadding2D(options, input_height, input_width,
+                                 filter_height, filter_width, stride_height,
+                                 stride_width, dilation_height, dilation_width);
+
+  // Since XNNPACK doesn't support ceil rounding, add bottom and right padding
+  // to bring the output to the sizes after ceil rounding.
+  if (options->roundingType().AsEnum() == V8MLRoundingType::Enum::kCeil &&
+      !options->hasOutputSizes()) {
+    auto checked_padding_bottom =
+        base::MakeCheckedNum<uint32_t>(output_height - 1) * stride_height +
+        (filter_height - 1) * dilation_height + 1 - input_height - padding.top;
+    auto checked_padding_right =
+        base::MakeCheckedNum<uint32_t>(output_width - 1) * stride_width +
+        (filter_width - 1) * dilation_width + 1 - input_width - padding.left;
+
+    if (!checked_padding_bottom.AssignIfValid(&padding.bottom) ||
+        !checked_padding_right.AssignIfValid(&padding.right)) {
+      error_message = "The padding size is too large.";
+      return xnn_status_invalid_parameter;
+    }
+  }
 
   // Define XNNPACK average or max pooling 2d Node for the Subgraph object.
   const float output_min = -std::numeric_limits<float>::infinity();
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_stats_report.cc b/third_party/blink/renderer/modules/peerconnection/rtc_stats_report.cc
index 5525607..a507b93 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_stats_report.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_stats_report.cc
@@ -960,7 +960,7 @@
     const auto& media_source =
         static_cast<const webrtc::RTCMediaSourceStats&>(stat);
     DCHECK(media_source.kind.has_value());
-    std::string kind = media_source.kind.ValueOrDefault("");
+    std::string kind = media_source.kind.value_or("");
     if (kind == "audio") {
       v8_stats =
           ToV8Stat(script_state, stat.cast_to<webrtc::RTCAudioSourceStats>());
diff --git a/third_party/blink/renderer/modules/permissions/permission_utils.cc b/third_party/blink/renderer/modules/permissions/permission_utils.cc
index 1180a78b..99492e09 100644
--- a/third_party/blink/renderer/modules/permissions/permission_utils.cc
+++ b/third_party/blink/renderer/modules/permissions/permission_utils.cc
@@ -372,7 +372,8 @@
     return CreatePermissionDescriptor(PermissionName::DISPLAY_CAPTURE);
   }
   if (name == V8PermissionName::Enum::kCapturedSurfaceControl) {
-    if (!RuntimeEnabledFeatures::CapturedSurfaceControlEnabled()) {
+    if (!RuntimeEnabledFeatures::CapturedSurfaceControlEnabled(
+            ExecutionContext::From(script_state))) {
       exception_state.ThrowTypeError(
           "The Captured Surface Control API is not enabled.");
       return nullptr;
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc
index a56f1b0b..26b1df2 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc
@@ -1252,7 +1252,6 @@
   ImageLoadBlockingPolicy load_blocking_policy =
       ImageLoadBlockingPolicy::kDefault;
   if (resource->GetType() == ResourceType::kImage) {
-    image_resources_.insert(resource);
     not_loaded_image_resources_.insert(resource);
     if (params.GetImageRequestBehavior() ==
         FetchParameters::ImageRequestBehavior::kNonBlockingImage) {
@@ -2806,7 +2805,6 @@
   visitor->Trace(non_blocking_loaders_);
   visitor->Trace(cached_resources_map_);
   visitor->Trace(emulated_load_started_for_inspector_resources_map_);
-  visitor->Trace(image_resources_);
   visitor->Trace(not_loaded_image_resources_);
   visitor->Trace(preloads_);
   visitor->Trace(matched_preloads_);
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h
index 4d5dc5e..1c7744e6 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h
@@ -582,12 +582,10 @@
   HeapHashSet<Member<Resource>> document_resource_strong_refs_;
   size_t document_resource_strong_refs_total_size_ = 0;
 
-  // |image_resources_| is the subset of all image resources for the document.
-  HeapHashSet<WeakMember<Resource>> image_resources_;
-
-  // |not_loaded_image_resources_| is a subset of |image_resources_| where
-  // |Resource::IsLoaded| might be false. The is used for performance
-  // optimizations and might still contain images which are actually loaded.
+  // |not_loaded_image_resources_| is a subset of all image resources for the
+  // document where |Resource::IsLoaded| might be false. The is used for
+  // performance optimizations and might still contain images which are actually
+  // loaded.
   HeapHashSet<WeakMember<Resource>> not_loaded_image_resources_;
 
   HeapHashMap<PreloadKey, Member<Resource>> preloads_;
diff --git a/third_party/blink/renderer/platform/peerconnection/rtc_video_encoder.cc b/third_party/blink/renderer/platform/peerconnection/rtc_video_encoder.cc
index 97284d6..a60b640 100644
--- a/third_party/blink/renderer/platform/peerconnection/rtc_video_encoder.cc
+++ b/third_party/blink/renderer/platform/peerconnection/rtc_video_encoder.cc
@@ -865,14 +865,6 @@
     preferred_pixel_formats_ = {webrtc::VideoFrameBuffer::Type::kNV12};
   }
 
-  // When we don't have built in H264 software encoding, allow usage of any
-  // software encoders provided by the platform.
-#if !BUILDFLAG(ENABLE_OPENH264) && BUILDFLAG(RTC_USE_H264)
-  if (profile >= media::H264PROFILE_MIN && profile <= media::H264PROFILE_MAX) {
-    vea_config.required_encoder_type =
-        media::VideoEncodeAccelerator::Config::EncoderType::kNoPreference;
-  }
-#endif
   encoder_metrics_provider_ =
       encoder_metrics_provider_factory_->CreateVideoEncoderMetricsProvider();
   encoder_metrics_provider_->Initialize(
@@ -1971,6 +1963,15 @@
   vea_config_->inter_layer_pred = inter_layer_pred;
   vea_config_->drop_frame_thresh_percentage =
       GetDropFrameThreshold(*codec_settings);
+  // When we don't have built in H264 software encoding, allow usage of any
+  // software encoders provided by the platform.
+#if !BUILDFLAG(ENABLE_OPENH264) && BUILDFLAG(RTC_USE_H264)
+  if (profile_ >= media::H264PROFILE_MIN &&
+      profile_ <= media::H264PROFILE_MAX) {
+    vea_config_->required_encoder_type =
+        media::VideoEncodeAccelerator::Config::EncoderType::kNoPreference;
+  }
+#endif
 
   if (!base::FeatureList::IsEnabled(
           features::kWebRtcInitializeEncoderOnFirstFrame)) {
diff --git a/third_party/blink/renderer/platform/peerconnection/rtc_video_encoder.h b/third_party/blink/renderer/platform/peerconnection/rtc_video_encoder.h
index 378829e..463fb6d5 100644
--- a/third_party/blink/renderer/platform/peerconnection/rtc_video_encoder.h
+++ b/third_party/blink/renderer/platform/peerconnection/rtc_video_encoder.h
@@ -17,6 +17,7 @@
 #include "base/memory/weak_ptr.h"
 #include "base/synchronization/lock.h"
 #include "media/base/video_decoder_config.h"
+#include "media/media_buildflags.h"
 #include "media/video/video_encode_accelerator.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
 #include "third_party/blink/renderer/platform/wtf/functional.h"
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index 40eba3f..adddec26 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -607,10 +607,8 @@
     },
     {
       name: "CapturedSurfaceControl",
-      // TODO(crbug.com/1466247): Once implementation is further along,
-      // set "default" to "experimental". (Note that Android support
-      // is not planned.)
-      status: {"Android": ""},
+      origin_trial_feature_name: "CapturedSurfaceControl",
+      status: {"Android": "", "default": "experimental"},
     },
     {
       name: "CaptureHandle",
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index a23ca02..39d4fcf 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -6738,7 +6738,7 @@
 crbug.com/1519053 external/wpt/webnn/softmax.https.any.html [ Crash Pass ]
 crbug.com/1519053 external/wpt/webnn/softmax.https.any.worker.html [ Crash Pass ]
 
-# Garderer: 2024-01-19
+# Gardener: 2024-01-19
 crbug.com/1511285 virtual/force-eager/external/wpt/measure-memory/redirect.client.https.window.html [ Skip Timeout ]
 crbug.com/1511285 virtual/force-eager/external/wpt/measure-memory/redirect.server.https.window.html [ Skip Timeout ]
 crbug.com/1518938 [ Linux ] http/tests/devtools/resource-tree/resource-tree-non-unique-url.js [ Failure Pass ]
@@ -6750,3 +6750,7 @@
 crbug.com/1519124 [ Mac12 ] external/wpt/webrtc-encoded-transform/tentative/RTCPeerConnection-insertable-streams-simulcast.https.html [ Failure Pass Timeout ]
 crbug.com/1519124 [ Mac12-arm64 ] external/wpt/webrtc-encoded-transform/tentative/RTCPeerConnection-insertable-streams-simulcast.https.html [ Failure Pass Timeout ]
 crbug.com/1519124 [ Linux ] external/wpt/webrtc-encoded-transform/tentative/RTCPeerConnection-insertable-streams-simulcast.https.html [ Failure Pass Timeout ]
+
+# Gardener: 2024-01-22
+crbug.com/1518453 inspector-protocol/input/dispatchTouchEvent.js [ Failure Pass Timeout ]
+crbug.com/1520409 [ Linux ] virtual/shared-storage-fenced-frame-mparch/external/wpt/shared-storage/shared-storage-writable-service-worker-fetch.tentative.https.sub.html [ Failure Pass ]
diff --git a/third_party/blink/web_tests/external/wpt/compute-pressure/compute_pressure_rate_obfuscation_mitigation_not_triggered.tentative.https.window.js b/third_party/blink/web_tests/external/wpt/compute-pressure/compute_pressure_rate_obfuscation_mitigation_not_triggered.tentative.https.window.js
index 9fd549d2..cb1aa43 100644
--- a/third_party/blink/web_tests/external/wpt/compute-pressure/compute_pressure_rate_obfuscation_mitigation_not_triggered.tentative.https.window.js
+++ b/third_party/blink/web_tests/external/wpt/compute-pressure/compute_pressure_rate_obfuscation_mitigation_not_triggered.tentative.https.window.js
@@ -29,6 +29,8 @@
     while (observerChanges.length < minChangesThreshold) {
       mockPressureService.setPressureUpdate(
           'cpu', readings[i++ % readings.length]);
+      // Allow tasks to run (avoid a micro-task loop).
+      await new Promise((resolve) => t.step_timeout(resolve, 0));
       await t.step_wait(
           () => mockPressureService.updatesDelivered() >= i,
           `At least ${i} readings have been delivered`);
diff --git a/third_party/blink/web_tests/external/wpt/compute-pressure/compute_pressure_rate_obfuscation_mitigation_triggered.tentative.https.window.js b/third_party/blink/web_tests/external/wpt/compute-pressure/compute_pressure_rate_obfuscation_mitigation_triggered.tentative.https.window.js
index 0ae20fd..11dcc3c 100644
--- a/third_party/blink/web_tests/external/wpt/compute-pressure/compute_pressure_rate_obfuscation_mitigation_triggered.tentative.https.window.js
+++ b/third_party/blink/web_tests/external/wpt/compute-pressure/compute_pressure_rate_obfuscation_mitigation_triggered.tentative.https.window.js
@@ -17,18 +17,12 @@
   await new Promise(async resolve => {
     const observerChanges = [];
     const observer = new PressureObserver(changes => {
-      if (observerChanges.length >= (minChangesThreshold - 1) && !gotPenalty) {
-        // Add an assert to the maximum threshold possible.
-        t.step(() => {
-          assert_less_than_equal(observerChanges.length, maxChangesThreshold,
-                                 "Sample count reaching maxChangesThreshold.");
-        });
-
+      if (observerChanges.length >= (minChangesThreshold - 1)) {
         const lastSample = observerChanges.at(-1);
         if ((changes[0].time - lastSample[0].time) >= minPenaltyTimeInMs) {
           // The update delivery might still be working even if
           // maxChangesThreshold have been reached and before disconnect() is
-          // processed. This will corrupt the result for the above t.step().
+          // processed.
           // Therefore we are adding a flag to dismiss any updates after the
           // penalty is detected, which is the condition for the test to pass.
           gotPenalty = true;
@@ -46,12 +40,17 @@
     // pressureChanges.length, as system load and browser optimizations can
     // cause the actual timer used by mockPressureService to deliver readings
     // to be a bit slower or faster than requested.
-    while (true) {
+    while (observerChanges.length <= maxChangesThreshold || !gotPenalty) {
       mockPressureService.setPressureUpdate(
           'cpu', readings[i++ % readings.length]);
+      // Allow tasks to run (avoid a micro-task loop).
+      await new Promise((resolve) => t.step_timeout(resolve, 0));
       await t.step_wait(
           () => mockPressureService.updatesDelivered() >= i,
           `At least ${i} readings have been delivered`);
     }
+
+    assert_true(gotPenalty, 'Penalty not triggered');
+
   });
 }, 'Rate obfuscation mitigation should have been triggered, when changes is higher than minimum changes before penalty');
diff --git a/third_party/blink/web_tests/external/wpt/webnn/pooling.https.any-expected.txt b/third_party/blink/web_tests/external/wpt/webnn/pooling.https.any-expected.txt
index 570e340..b2074d66 100644
--- a/third_party/blink/web_tests/external/wpt/webnn/pooling.https.any-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/webnn/pooling.https.any-expected.txt
@@ -29,8 +29,6 @@
   promise_test: Unhandled rejection with value: object "NotSupportedError: The nchw input layout is not supported."
 [FAIL] averagePool2d float32 4D tensor options.roundingType=ceil / async
   promise_test: Unhandled rejection with value: object "NotSupportedError: The nchw input layout is not supported."
-[FAIL] averagePool2d float32 4D tensor options.layout=nhwc and options.roundingType=ceil / async
-  assert_true: assert_array_approx_equals_ulp: test averagePool2d float32 actual 50.03894805908203 should be close enough to expected 21.206613540649414 by the acceptable 11 ULP distance, but they have 10387645 ULP distance expected true got false
 [FAIL] averagePool2d float32 4D tensor options.outputSizes ignores options.roundingType=floor / async
   promise_test: Unhandled rejection with value: object "NotSupportedError: The nchw input layout is not supported."
 [FAIL] averagePool2d float32 4D tensor options.outputSizes ignores options.roundingType=ceil / async
diff --git a/third_party/blink/web_tests/external/wpt/webnn/pooling.https.any.worker-expected.txt b/third_party/blink/web_tests/external/wpt/webnn/pooling.https.any.worker-expected.txt
index c6138a9..8db7404a 100644
--- a/third_party/blink/web_tests/external/wpt/webnn/pooling.https.any.worker-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/webnn/pooling.https.any.worker-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 73 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 72 FAIL, 0 TIMEOUT, 0 NOTRUN.
 [FAIL] averagePool2d float32 4D constant tensor all positive default options / sync
   Failed to execute 'buildSync' on 'MLGraphBuilder': The nchw input layout is not supported.
 [FAIL] averagePool2d float32 4D tensor all positive default options / sync
@@ -30,8 +30,6 @@
   Failed to execute 'buildSync' on 'MLGraphBuilder': The nchw input layout is not supported.
 [FAIL] averagePool2d float32 4D tensor options.roundingType=ceil / sync
   Failed to execute 'buildSync' on 'MLGraphBuilder': The nchw input layout is not supported.
-[FAIL] averagePool2d float32 4D tensor options.layout=nhwc and options.roundingType=ceil / sync
-  assert_true: assert_array_approx_equals_ulp: test averagePool2d float32 actual 50.03894805908203 should be close enough to expected 21.206613540649414 by the acceptable 11 ULP distance, but they have 10387645 ULP distance expected true got false
 [FAIL] averagePool2d float32 4D tensor options.outputSizes ignores options.roundingType=floor / sync
   Failed to execute 'buildSync' on 'MLGraphBuilder': The nchw input layout is not supported.
 [FAIL] averagePool2d float32 4D tensor options.outputSizes ignores options.roundingType=ceil / sync
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/back-forward-cache-with-open-webrtc-connection.https.window.js b/third_party/blink/web_tests/external/wpt/webrtc/back-forward-cache-with-open-webrtc-connection.https.window.js
index 4a4807d..a516aa4 100644
--- a/third_party/blink/web_tests/external/wpt/webrtc/back-forward-cache-with-open-webrtc-connection.https.window.js
+++ b/third_party/blink/web_tests/external/wpt/webrtc/back-forward-cache-with-open-webrtc-connection.https.window.js
@@ -1,4 +1,4 @@
-// META: title=Testing BFCache support for page with open WebRTC connection.
+// META: title=Testing BFCache support for page with open WebRTC connection and live MediaStreamTrack.
 // META: script=/common/dispatcher/dispatcher.js
 // META: script=/common/utils.js
 // META: script=/html/browsers/browsing-the-web/back-forward-cache/resources/rc-helper.js
@@ -14,7 +14,7 @@
   const rc1 = await rcHelper.addWindow(
       /*config=*/ null, /*options=*/ { features: 'noopener' });
   await openWebRTC(rc1);
-  // The page should not be eligible for BFCache because of open WebRTC connection.
+  // The page should not be eligible for BFCache because of open WebRTC connection and live MediaStreamTrack.
   await assertBFCacheEligibility(rc1, /*shouldRestoreFromBFCache=*/ false);
-  await assertNotRestoredFromBFCache(rc1, ['WebRTC']);
+  await assertNotRestoredFromBFCache(rc1, ['WebRTC', 'LiveMediaStreamTrack']);
 });
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/resources/inspector-protocol-test.js b/third_party/blink/web_tests/http/tests/inspector-protocol/resources/inspector-protocol-test.js
index b93fb8a..65e8920 100644
--- a/third_party/blink/web_tests/http/tests/inspector-protocol/resources/inspector-protocol-test.js
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/resources/inspector-protocol-test.js
@@ -488,7 +488,7 @@
   }
 
   /**
-   * @returns {import("devtools-protocol/types/protocol-proxy-api.d.ts").ProtocolProxyApi.ProtocolApi}
+   * @returns {import("devtools-protocol/types/protocol-tests-proxy-api").ProtocolTestsProxyApi.ProtocolApi}
    */
   _setupProtocol() {
     return new Proxy({}, {
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/storage/interest-groups-expected.txt b/third_party/blink/web_tests/http/tests/inspector-protocol/storage/interest-groups-expected.txt
index e8cfeb5..44b0017 100644
--- a/third_party/blink/web_tests/http/tests/inspector-protocol/storage/interest-groups-expected.txt
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/storage/interest-groups-expected.txt
@@ -13,22 +13,6 @@
     type : started
     uniqueAuctionId : 1
 }
-interestGroupAuctionNetworkRequestCreated {
-    auctions : [
-        [0] : 1
-    ]
-    requestId : <string>
-    type : bidderJs
-    url : https://a.test:8443/inspector-protocol/resources/fledge_bidding_logic.js.php
-}
-interestGroupAuctionNetworkRequestCreated {
-    auctions : [
-        [0] : 1
-    ]
-    requestId : <string>
-    type : sellerJs
-    url : https://a.test:8443/inspector-protocol/resources/fledge_decision_logic.js.php
-}
 interestGroupAccessed {
     accessTime : <number>
     name : 0
@@ -213,22 +197,6 @@
     type : started
     uniqueAuctionId : 2
 }
-interestGroupAuctionNetworkRequestCreated {
-    auctions : [
-        [0] : 2
-    ]
-    requestId : <string>
-    type : bidderJs
-    url : https://a.test:8443/inspector-protocol/resources/fledge_bidding_logic.js.php
-}
-interestGroupAuctionNetworkRequestCreated {
-    auctions : [
-        [0] : 2
-    ]
-    requestId : <string>
-    type : sellerJs
-    url : https://a.test:8443/inspector-protocol/resources/fledge_decision_logic.js.php
-}
 Stop Tracking Auction Events
 Logged IG events:
 Test Done
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/storage/interest-groups.js b/third_party/blink/web_tests/http/tests/inspector-protocol/storage/interest-groups.js
index ba3220a..1464378f 100644
--- a/third_party/blink/web_tests/http/tests/inspector-protocol/storage/interest-groups.js
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/storage/interest-groups.js
@@ -22,13 +22,13 @@
 
   let nextAuctionId = 1;
   let auctionIdMap = new Map();
-  function normalizeAuctionId(uniqueAuctionId) {
-    if (uniqueAuctionId) {
-      if (!auctionIdMap.has(uniqueAuctionId)) {
-        auctionIdMap.set(uniqueAuctionId, nextAuctionId);
+  function normalizeAuctionId(event) {
+    if ('uniqueAuctionId' in event) {
+      if (!auctionIdMap.has(event.uniqueAuctionId)) {
+        auctionIdMap.set(event.uniqueAuctionId, nextAuctionId);
         ++nextAuctionId;
       }
-      return auctionIdMap.get(uniqueAuctionId);
+      return auctionIdMap.get(event.uniqueAuctionId);
     } else {
       return 'global';
     }
@@ -43,13 +43,6 @@
         a.name.localeCompare(b.name, 'en');
   }
 
-  // Helper for sorting auction <-> network events. Only cares about types
-  // since it's good enough for this application, and everything else is
-  // a random ID.
-  function compareNetEvents(a, b) {
-    return a.type.localeCompare(b.type, 'en');
-  }
-
   async function joinInterestGroups(id) {
     const joinJs = `
     navigator.joinAdInterestGroup({
@@ -80,16 +73,13 @@
     return session.evaluateAsync(auctionJs);
   }
 
-  let networkRequestUrls = new Map();
-
-  let events = [];
-  let auctionEvents = [];
-  let auctionNetworkEvents = [];
+  events = [];
+  auctionEvents = [];
   async function logAndClearEvents() {
     testRunner.log('Logged IG events:');
     // We expect only one auction event, so no ordering issue to worry about.
     for (let event of auctionEvents) {
-      event.uniqueAuctionId = normalizeAuctionId(event.uniqueAuctionId);
+      event.uniqueAuctionId = normalizeAuctionId(event);
 
       // Only some of auctionConfig fields are kept so this doesn't have to be
       // changed every time something new is added that shows up by default.
@@ -104,18 +94,11 @@
           event, 'interestGroupAuctionEventOccurred ', ['eventTime']);
     }
 
-    auctionNetworkEvents.sort(compareNetEvents);
-    for (let event of auctionNetworkEvents) {
-      event.auctions = event.auctions.map((a) => normalizeAuctionId(a));
-      event.url = networkRequestUrls.get(event.requestId);
-      testRunner.log(event, 'interestGroupAuctionNetworkRequestCreated ');
-    }
-
     // We need to sort IG events before dumping since ordering of bids is not
     // deterministic.
     events.sort(compareEvents);
     for (let event of events) {
-      event.uniqueAuctionId = normalizeAuctionId(event.uniqueAuctionId);
+      event.uniqueAuctionId = normalizeAuctionId(event);
       testRunner.log(event, 'interestGroupAccessed ', ['accessTime']);
       data = await dp.Storage.getInterestGroupDetails(
         {ownerOrigin: event.ownerOrigin, name: event.name});
@@ -123,9 +106,8 @@
       details.expirationTime = 0;
       testRunner.log(details, 'interestGroupDetails ');
     }
-    events = [];
     auctionEvents = [];
-    auctionNetworkEvents = [];
+    events = [];
   }
 
   let resolveWaitForWinPromise;
@@ -137,27 +119,14 @@
     auctionEvents.push(messageObject.params);
   });
 
-  dp.Storage.onInterestGroupAuctionNetworkRequestCreated(messageObject => {
-    auctionNetworkEvents.push(messageObject.params);
-  });
-
   dp.Storage.onInterestGroupAccessed(messageObject => {
     events.push(messageObject.params);
     if (messageObject.params.type == 'win') {
       resolveWaitForWinPromise();
     }
   });
-
-  dp.Network.onRequestWillBeSent(messageObject => {
-    networkRequestUrls.set(
-        messageObject.params.requestId, messageObject.params.request.url);
-  });
-
   await page.navigate(base + 'empty.html');
 
-  // Enable network events, to check cross-referencing of them.
-  await dp.Network.enable();
-
   // Start tracking, join interest groups, and run an auction.
   await dp.Storage.setInterestGroupTracking({enable: true});
   await dp.Storage.setInterestGroupAuctionTracking({enable: true});
diff --git a/third_party/blink/web_tests/webexposed/feature-policy-features-expected.txt b/third_party/blink/web_tests/webexposed/feature-policy-features-expected.txt
index 0459c7c..af6916a5 100644
--- a/third_party/blink/web_tests/webexposed/feature-policy-features-expected.txt
+++ b/third_party/blink/web_tests/webexposed/feature-policy-features-expected.txt
@@ -7,6 +7,7 @@
 autoplay
 browsing-topics
 camera
+captured-surface-control
 ch-device-memory
 ch-downlink
 ch-dpr
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
index 21bdb58..b2996af 100644
--- a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
+++ b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
@@ -1206,11 +1206,17 @@
     setter textRendering
     setter wordSpacing
 interface CaptureController : EventTarget
+    static method getSupportedZoomLevels
     attribute @@toStringTag
     getter oncapturedmousechange
+    getter oncapturedzoomlevelchange
     method constructor
+    method getZoomLevel
+    method sendWheel
     method setFocusBehavior
+    method setZoomLevel
     setter oncapturedmousechange
+    setter oncapturedzoomlevelchange
 interface CapturedMouseEvent : Event
     attribute @@toStringTag
     getter surfaceX
diff --git a/third_party/chromium-variations b/third_party/chromium-variations
index f9a3242..901d184 160000
--- a/third_party/chromium-variations
+++ b/third_party/chromium-variations
@@ -1 +1 @@
-Subproject commit f9a3242f9bf85b3e41e25bd57d4a0a85a2a8ff2d
+Subproject commit 901d1840715d46381809bb62cbe1c6d1b7724660
diff --git a/third_party/cros_system_api b/third_party/cros_system_api
index e5100d6..12d5e38 160000
--- a/third_party/cros_system_api
+++ b/third_party/cros_system_api
@@ -1 +1 @@
-Subproject commit e5100d618713aadf930da88e2e8458a6432f15c6
+Subproject commit 12d5e386005a211570cfdf9849d2fa6a3b38594b
diff --git a/third_party/dawn b/third_party/dawn
index c62e9d9..3e96865 160000
--- a/third_party/dawn
+++ b/third_party/dawn
@@ -1 +1 @@
-Subproject commit c62e9d9be33be8b9d1b437840cfb02b03d8c35a5
+Subproject commit 3e968653f537c5f38f1b45d07efe8051c6021779
diff --git a/third_party/devtools-frontend-internal b/third_party/devtools-frontend-internal
index 9c8974e..4e0cc80 160000
--- a/third_party/devtools-frontend-internal
+++ b/third_party/devtools-frontend-internal
@@ -1 +1 @@
-Subproject commit 9c8974e12b5baa4671db8afa6675bf191ff97e99
+Subproject commit 4e0cc80c5875ea35501cf9ff3b859cc2c8565ccd
diff --git a/third_party/devtools-frontend/src b/third_party/devtools-frontend/src
index 97f497e..7f28655 160000
--- a/third_party/devtools-frontend/src
+++ b/third_party/devtools-frontend/src
@@ -1 +1 @@
-Subproject commit 97f497e6be3a25ee3d489c31a183cafcb441ba15
+Subproject commit 7f28655bc9e25129032852b084aa07a171d1cca2
diff --git a/third_party/perfetto b/third_party/perfetto
index 28eadcf..d6af17f 160000
--- a/third_party/perfetto
+++ b/third_party/perfetto
@@ -1 +1 @@
-Subproject commit 28eadcf5efccc550c5317bdccaca0e64d9698f65
+Subproject commit d6af17fef257af28ee2417216ef87d5c5b743a1b
diff --git a/third_party/skia b/third_party/skia
index c42ac52..f4b7996 160000
--- a/third_party/skia
+++ b/third_party/skia
@@ -1 +1 @@
-Subproject commit c42ac527ff0e66875adf1e849e6211e3bbfa51dd
+Subproject commit f4b79968de2a319bd8e0e1b958a4b70aa734482f
diff --git a/third_party/webrtc b/third_party/webrtc
index 1768705..3484381 160000
--- a/third_party/webrtc
+++ b/third_party/webrtc
@@ -1 +1 @@
-Subproject commit 1768705d997b466f528624ba143dbe55fba94a36
+Subproject commit 348438154abf25bfda544c7e4460ef2258eb1eab
diff --git a/third_party/xnnpack/BUILD.gn b/third_party/xnnpack/BUILD.gn
index 85d9c10..6286ed41 100644
--- a/third_party/xnnpack/BUILD.gn
+++ b/third_party/xnnpack/BUILD.gn
@@ -55,12 +55,11 @@
 if (current_cpu == "x64" || current_cpu == "x86") {
   xnnpack_deps = [
     ":amalgam_avx-no-avx2-no-f16c-no-fma",
-    ":amalgam_avx2-avxvnni-fma",
+    ":amalgam_avx2-avxvnni",
     ":amalgam_avx512f",
     ":amalgam_avx512f-avx512cd-avx512bw-avx512dq-avx512vl",
     ":amalgam_avx512f-avx512cd-avx512bw-avx512dq-avx512vl-avx512vbmi",
-    ":amalgam_avx512f-avx512cd-avx512bw-avx512dq-avx512vl-avx512vnni-fma",
-    ":amalgam_avx512f-avx512cd-avx512bw-avx512dq-avx512vl-avx512vnni-gfni-fma",
+    ":amalgam_avx512f-avx512cd-avx512bw-avx512dq-avx512vl-avx512vnni",
     ":amalgam_f16c-fma-avx2",
     ":amalgam_f16c-fma-no-avx2",
     ":amalgam_f16c-no-avx2-no-fma",
@@ -78,10 +77,9 @@
 
   xnnpack_standalone_deps = [
     ":amalgam_avx-no-avx2-no-f16c-no-fma_standalone",
-    ":amalgam_avx2-avxvnni-fma_standalone",
+    ":amalgam_avx2-avxvnni_standalone",
     ":amalgam_avx512f-avx512cd-avx512bw-avx512dq-avx512vl-avx512vbmi_standalone",
-    ":amalgam_avx512f-avx512cd-avx512bw-avx512dq-avx512vl-avx512vnni-fma_standalone",
-    ":amalgam_avx512f-avx512cd-avx512bw-avx512dq-avx512vl-avx512vnni-gfni-fma_standalone",
+    ":amalgam_avx512f-avx512cd-avx512bw-avx512dq-avx512vl-avx512vnni_standalone",
     ":amalgam_avx512f-avx512cd-avx512bw-avx512dq-avx512vl_standalone",
     ":amalgam_avx512f_standalone",
     ":amalgam_f16c-fma-avx2_standalone",
@@ -118,9 +116,10 @@
     ":f32-qc8w-gemm_arch=armv8.2-a+fp16+dotprod",
     ":operators_arm64",
     ":qd8-f16-qc8w-gemm_arch=armv8.2-a+fp16+dotprod",
-    ":qd8-f16-qc8w-igemm_arch=armv8.2-a+fp16+dotprod",
     ":qd8-f32-qc8w-gemm_arch=armv8.2-a+fp16+dotprod",
     ":qd8-f32-qc8w-igemm_arch=armv8.2-a+fp16+dotprod",
+    ":qs8-gemm_arch=armv8.2-a+fp16+dotprod",
+    ":qs8-igemm_arch=armv8.2-a+fp16+dotprod",
     ":qs8-qc8w-gemm_arch=armv8.2-a+fp16+dotprod",
     ":qs8-qc8w-igemm_arch=armv8.2-a+fp16+dotprod",
     ":qu8-gemm_arch=armv8.2-a+fp16+dotprod",
@@ -148,9 +147,10 @@
     ":f32-qc8w-gemm_arch=armv8.2-a+fp16+dotprod_standalone",
     ":operators_arm64_standalone",
     ":qd8-f16-qc8w-gemm_arch=armv8.2-a+fp16+dotprod_standalone",
-    ":qd8-f16-qc8w-igemm_arch=armv8.2-a+fp16+dotprod_standalone",
     ":qd8-f32-qc8w-gemm_arch=armv8.2-a+fp16+dotprod_standalone",
     ":qd8-f32-qc8w-igemm_arch=armv8.2-a+fp16+dotprod_standalone",
+    ":qs8-gemm_arch=armv8.2-a+fp16+dotprod_standalone",
+    ":qs8-igemm_arch=armv8.2-a+fp16+dotprod_standalone",
     ":qs8-qc8w-gemm_arch=armv8.2-a+fp16+dotprod_standalone",
     ":qs8-qc8w-igemm_arch=armv8.2-a+fp16+dotprod_standalone",
     ":qu8-gemm_arch=armv8.2-a+fp16+dotprod_standalone",
@@ -300,11 +300,10 @@
     }
   }
 
-  source_set("amalgam_avx2-avxvnni-fma") {
+  source_set("amalgam_avx2-avxvnni") {
     cflags = [
       "-mavx2",
       "-mavxvnni",
-      "-mfma",
     ]
 
     sources = [ "src/src/amalgam/gen/avxvnni.c" ]
@@ -324,11 +323,10 @@
   }
 
   # This is a target that cannot depend on //base.
-  source_set("amalgam_avx2-avxvnni-fma_standalone") {
+  source_set("amalgam_avx2-avxvnni_standalone") {
     cflags = [
       "-mavx2",
       "-mavxvnni",
-      "-mfma",
     ]
 
     sources = [ "src/src/amalgam/gen/avxvnni.c" ]
@@ -507,8 +505,7 @@
     }
   }
 
-  source_set(
-      "amalgam_avx512f-avx512cd-avx512bw-avx512dq-avx512vl-avx512vnni-fma") {
+  source_set("amalgam_avx512f-avx512cd-avx512bw-avx512dq-avx512vl-avx512vnni") {
     cflags = [
       "-mavx512bw",
       "-mavx512cd",
@@ -516,7 +513,6 @@
       "-mavx512f",
       "-mavx512vl",
       "-mavx512vnni",
-      "-mfma",
     ]
 
     sources = [ "src/src/amalgam/gen/avx512vnni.c" ]
@@ -537,7 +533,7 @@
 
   # This is a target that cannot depend on //base.
   source_set(
-      "amalgam_avx512f-avx512cd-avx512bw-avx512dq-avx512vl-avx512vnni-fma_standalone") {
+      "amalgam_avx512f-avx512cd-avx512bw-avx512dq-avx512vl-avx512vnni_standalone") {
     cflags = [
       "-mavx512bw",
       "-mavx512cd",
@@ -545,7 +541,6 @@
       "-mavx512f",
       "-mavx512vl",
       "-mavx512vnni",
-      "-mfma",
     ]
 
     sources = [ "src/src/amalgam/gen/avx512vnni.c" ]
@@ -568,69 +563,6 @@
     }
   }
 
-  source_set(
-      "amalgam_avx512f-avx512cd-avx512bw-avx512dq-avx512vl-avx512vnni-gfni-fma") {
-    cflags = [
-      "-mavx512bw",
-      "-mavx512cd",
-      "-mavx512dq",
-      "-mavx512f",
-      "-mavx512vl",
-      "-mavx512vnni",
-      "-mfma",
-      "-mgfni",
-    ]
-
-    sources = [ "src/src/amalgam/gen/avx512vnnigfni.c" ]
-
-    configs -= [ "//build/config/compiler:chromium_code" ]
-    configs += [ "//build/config/compiler:no_chromium_code" ]
-    configs += [ "//build/config/sanitizers:cfi_icall_generalize_pointers" ]
-
-    deps = [
-      "//third_party/cpuinfo",
-      "//third_party/fp16",
-      "//third_party/fxdiv",
-      "//third_party/pthreadpool",
-    ]
-
-    public_configs = [ ":xnnpack_config" ]
-  }
-
-  # This is a target that cannot depend on //base.
-  source_set(
-      "amalgam_avx512f-avx512cd-avx512bw-avx512dq-avx512vl-avx512vnni-gfni-fma_standalone") {
-    cflags = [
-      "-mavx512bw",
-      "-mavx512cd",
-      "-mavx512dq",
-      "-mavx512f",
-      "-mavx512vl",
-      "-mavx512vnni",
-      "-mfma",
-      "-mgfni",
-    ]
-
-    sources = [ "src/src/amalgam/gen/avx512vnnigfni.c" ]
-
-    configs -= [ "//build/config/compiler:chromium_code" ]
-    configs += [ "//build/config/compiler:no_chromium_code" ]
-    configs += [ "//build/config/sanitizers:cfi_icall_generalize_pointers" ]
-
-    deps = [
-      "//third_party/cpuinfo",
-      "//third_party/fp16",
-      "//third_party/fxdiv",
-      "//third_party/pthreadpool:pthreadpool_standalone",
-    ]
-
-    public_configs = [ ":xnnpack_config" ]
-
-    if (!(is_android && use_order_profiling)) {
-      assert_no_deps = [ "//base" ]
-    }
-  }
-
   source_set("amalgam_f16c-fma-avx2") {
     cflags = [
       "-mavx2",
@@ -1321,7 +1253,6 @@
       "src/src/subgraph/multiply2.c",
       "src/src/subgraph/negate.c",
       "src/src/subgraph/prelu.c",
-      "src/src/subgraph/reshape-helpers.c",
       "src/src/subgraph/rope.c",
       "src/src/subgraph/scaled-dot-product-attention.c",
       "src/src/subgraph/sigmoid.c",
@@ -1392,7 +1323,6 @@
       "src/src/subgraph/multiply2.c",
       "src/src/subgraph/negate.c",
       "src/src/subgraph/prelu.c",
-      "src/src/subgraph/reshape-helpers.c",
       "src/src/subgraph/rope.c",
       "src/src/subgraph/scaled-dot-product-attention.c",
       "src/src/subgraph/sigmoid.c",
@@ -2850,59 +2780,6 @@
     }
   }
 
-  source_set("qd8-f16-qc8w-igemm_arch=armv8.2-a+fp16+dotprod") {
-    cflags = [ "-march=armv8.2-a+fp16+dotprod" ]
-
-    asmflags = cflags
-
-    sources = [
-      "src/src/qd8-f16-qc8w-igemm/gen/qd8-f16-qc8w-igemm-4x16c4-minmax-asm-aarch64-neondot-cortex-a55.S",
-      "src/src/qd8-f16-qc8w-igemm/gen/qd8-f16-qc8w-igemm-4x16c4-minmax-asm-aarch64-neondot-ld128.S",
-    ]
-
-    configs -= [ "//build/config/compiler:chromium_code" ]
-    configs += [ "//build/config/compiler:no_chromium_code" ]
-    configs += [ "//build/config/sanitizers:cfi_icall_generalize_pointers" ]
-
-    deps = [
-      "//third_party/cpuinfo",
-      "//third_party/fp16",
-      "//third_party/fxdiv",
-      "//third_party/pthreadpool",
-    ]
-
-    public_configs = [ ":xnnpack_config" ]
-  }
-
-  # This is a target that cannot depend on //base.
-  source_set("qd8-f16-qc8w-igemm_arch=armv8.2-a+fp16+dotprod_standalone") {
-    cflags = [ "-march=armv8.2-a+fp16+dotprod" ]
-
-    asmflags = cflags
-
-    sources = [
-      "src/src/qd8-f16-qc8w-igemm/gen/qd8-f16-qc8w-igemm-4x16c4-minmax-asm-aarch64-neondot-cortex-a55.S",
-      "src/src/qd8-f16-qc8w-igemm/gen/qd8-f16-qc8w-igemm-4x16c4-minmax-asm-aarch64-neondot-ld128.S",
-    ]
-
-    configs -= [ "//build/config/compiler:chromium_code" ]
-    configs += [ "//build/config/compiler:no_chromium_code" ]
-    configs += [ "//build/config/sanitizers:cfi_icall_generalize_pointers" ]
-
-    deps = [
-      "//third_party/cpuinfo",
-      "//third_party/fp16",
-      "//third_party/fxdiv",
-      "//third_party/pthreadpool:pthreadpool_standalone",
-    ]
-
-    public_configs = [ ":xnnpack_config" ]
-
-    if (!(is_android && use_order_profiling)) {
-      assert_no_deps = [ "//base" ]
-    }
-  }
-
   source_set("qd8-f32-qc8w-gemm_arch=armv8.2-a+fp16+dotprod") {
     cflags = [ "-march=armv8.2-a+fp16+dotprod" ]
 
@@ -3011,6 +2888,256 @@
     }
   }
 
+  source_set("qs8-gemm_arch=armv8.2-a+fp16+dotprod") {
+    cflags = [ "-march=armv8.2-a+fp16+dotprod" ]
+
+    asmflags = cflags
+
+    sources = [
+      "src/src/qs8-gemm/gen/qs8-gemm-1x16c4-minmax-fp32-asm-aarch64-neondot-ld32.S",
+      "src/src/qs8-gemm/gen/qs8-gemm-1x16c4-minmax-fp32-asm-aarch64-neondot-ld64.S",
+      "src/src/qs8-gemm/gen/qs8-gemm-1x16c4-minmax-rndnu-asm-aarch64-neondot-ld32.S",
+      "src/src/qs8-gemm/gen/qs8-gemm-1x16c4-minmax-rndnu-asm-aarch64-neondot-ld64.S",
+      "src/src/qs8-gemm/gen/qs8-gemm-1x8c8-minmax-fp32-asm-aarch64-neon-mlal-cortex-a53-prfm.S",
+      "src/src/qs8-gemm/gen/qs8-gemm-1x8c8-minmax-fp32-asm-aarch64-neon-mlal-cortex-a53.S",
+      "src/src/qs8-gemm/gen/qs8-gemm-1x8c8-minmax-fp32-asm-aarch64-neon-mlal-prfm.S",
+      "src/src/qs8-gemm/gen/qs8-gemm-1x8c8-minmax-fp32-asm-aarch64-neon-mlal.S",
+      "src/src/qs8-gemm/gen/qs8-gemm-1x8c8-minmax-rndnu-asm-aarch64-neon-mlal-cortex-a53-prfm.S",
+      "src/src/qs8-gemm/gen/qs8-gemm-1x8c8-minmax-rndnu-asm-aarch64-neon-mlal-cortex-a53.S",
+      "src/src/qs8-gemm/gen/qs8-gemm-1x8c8-minmax-rndnu-asm-aarch64-neon-mlal-prfm.S",
+      "src/src/qs8-gemm/gen/qs8-gemm-1x8c8-minmax-rndnu-asm-aarch64-neon-mlal.S",
+      "src/src/qs8-gemm/gen/qs8-gemm-2x8c16-minmax-fp32-asm-aarch64-neon-mlal.S",
+      "src/src/qs8-gemm/gen/qs8-gemm-2x8c16-minmax-rndnu-asm-aarch64-neon-mlal.S",
+      "src/src/qs8-gemm/gen/qs8-gemm-2x8c8-minmax-fp32-asm-aarch64-neon-mlal-cortex-a53-prfm.S",
+      "src/src/qs8-gemm/gen/qs8-gemm-2x8c8-minmax-fp32-asm-aarch64-neon-mlal-cortex-a53.S",
+      "src/src/qs8-gemm/gen/qs8-gemm-2x8c8-minmax-fp32-asm-aarch64-neon-mlal-prfm.S",
+      "src/src/qs8-gemm/gen/qs8-gemm-2x8c8-minmax-fp32-asm-aarch64-neon-mlal.S",
+      "src/src/qs8-gemm/gen/qs8-gemm-2x8c8-minmax-fp32-asm-aarch64-neon-mull.S",
+      "src/src/qs8-gemm/gen/qs8-gemm-2x8c8-minmax-rndnu-asm-aarch64-neon-mlal-cortex-a53-prfm.S",
+      "src/src/qs8-gemm/gen/qs8-gemm-2x8c8-minmax-rndnu-asm-aarch64-neon-mlal-cortex-a53.S",
+      "src/src/qs8-gemm/gen/qs8-gemm-2x8c8-minmax-rndnu-asm-aarch64-neon-mlal-prfm.S",
+      "src/src/qs8-gemm/gen/qs8-gemm-2x8c8-minmax-rndnu-asm-aarch64-neon-mlal.S",
+      "src/src/qs8-gemm/gen/qs8-gemm-2x8c8-minmax-rndnu-asm-aarch64-neon-mull.S",
+      "src/src/qs8-gemm/gen/qs8-gemm-4x16-minmax-fp32-asm-aarch64-neon-mlal-lane-cortex-a53-prfm.S",
+      "src/src/qs8-gemm/gen/qs8-gemm-4x16-minmax-fp32-asm-aarch64-neon-mlal-lane-cortex-a53.S",
+      "src/src/qs8-gemm/gen/qs8-gemm-4x16-minmax-fp32-asm-aarch64-neon-mlal-lane-ld64-prfm.S",
+      "src/src/qs8-gemm/gen/qs8-gemm-4x16-minmax-fp32-asm-aarch64-neon-mlal-lane-ld64.S",
+      "src/src/qs8-gemm/gen/qs8-gemm-4x16-minmax-rndnu-asm-aarch64-neon-mlal-lane-cortex-a53-prfm.S",
+      "src/src/qs8-gemm/gen/qs8-gemm-4x16-minmax-rndnu-asm-aarch64-neon-mlal-lane-cortex-a53.S",
+      "src/src/qs8-gemm/gen/qs8-gemm-4x16-minmax-rndnu-asm-aarch64-neon-mlal-lane-ld64-prfm.S",
+      "src/src/qs8-gemm/gen/qs8-gemm-4x16-minmax-rndnu-asm-aarch64-neon-mlal-lane-ld64.S",
+      "src/src/qs8-gemm/gen/qs8-gemm-4x16c4-minmax-fp32-asm-aarch64-neondot-cortex-a55.S",
+      "src/src/qs8-gemm/gen/qs8-gemm-4x16c4-minmax-fp32-asm-aarch64-neondot-ld128.S",
+      "src/src/qs8-gemm/gen/qs8-gemm-4x16c4-minmax-fp32-asm-aarch64-neondot-ld32.S",
+      "src/src/qs8-gemm/gen/qs8-gemm-4x16c4-minmax-fp32-asm-aarch64-neondot-ld64.S",
+      "src/src/qs8-gemm/gen/qs8-gemm-4x16c4-minmax-rndnu-asm-aarch64-neondot-cortex-a55.S",
+      "src/src/qs8-gemm/gen/qs8-gemm-4x16c4-minmax-rndnu-asm-aarch64-neondot-ld128.S",
+      "src/src/qs8-gemm/gen/qs8-gemm-4x16c4-minmax-rndnu-asm-aarch64-neondot-ld32.S",
+      "src/src/qs8-gemm/gen/qs8-gemm-4x16c4-minmax-rndnu-asm-aarch64-neondot-ld64.S",
+      "src/src/qs8-gemm/gen/qs8-gemm-4x8-minmax-rndnu-asm-aarch64-neon-mlal-lane-ld64-prfm.S",
+      "src/src/qs8-gemm/gen/qs8-gemm-4x8-minmax-rndnu-asm-aarch64-neon-mlal-lane-ld64.S",
+    ]
+
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+    configs += [ "//build/config/sanitizers:cfi_icall_generalize_pointers" ]
+
+    deps = [
+      "//third_party/cpuinfo",
+      "//third_party/fp16",
+      "//third_party/fxdiv",
+      "//third_party/pthreadpool",
+    ]
+
+    public_configs = [ ":xnnpack_config" ]
+  }
+
+  # This is a target that cannot depend on //base.
+  source_set("qs8-gemm_arch=armv8.2-a+fp16+dotprod_standalone") {
+    cflags = [ "-march=armv8.2-a+fp16+dotprod" ]
+
+    asmflags = cflags
+
+    sources = [
+      "src/src/qs8-gemm/gen/qs8-gemm-1x16c4-minmax-fp32-asm-aarch64-neondot-ld32.S",
+      "src/src/qs8-gemm/gen/qs8-gemm-1x16c4-minmax-fp32-asm-aarch64-neondot-ld64.S",
+      "src/src/qs8-gemm/gen/qs8-gemm-1x16c4-minmax-rndnu-asm-aarch64-neondot-ld32.S",
+      "src/src/qs8-gemm/gen/qs8-gemm-1x16c4-minmax-rndnu-asm-aarch64-neondot-ld64.S",
+      "src/src/qs8-gemm/gen/qs8-gemm-1x8c8-minmax-fp32-asm-aarch64-neon-mlal-cortex-a53-prfm.S",
+      "src/src/qs8-gemm/gen/qs8-gemm-1x8c8-minmax-fp32-asm-aarch64-neon-mlal-cortex-a53.S",
+      "src/src/qs8-gemm/gen/qs8-gemm-1x8c8-minmax-fp32-asm-aarch64-neon-mlal-prfm.S",
+      "src/src/qs8-gemm/gen/qs8-gemm-1x8c8-minmax-fp32-asm-aarch64-neon-mlal.S",
+      "src/src/qs8-gemm/gen/qs8-gemm-1x8c8-minmax-rndnu-asm-aarch64-neon-mlal-cortex-a53-prfm.S",
+      "src/src/qs8-gemm/gen/qs8-gemm-1x8c8-minmax-rndnu-asm-aarch64-neon-mlal-cortex-a53.S",
+      "src/src/qs8-gemm/gen/qs8-gemm-1x8c8-minmax-rndnu-asm-aarch64-neon-mlal-prfm.S",
+      "src/src/qs8-gemm/gen/qs8-gemm-1x8c8-minmax-rndnu-asm-aarch64-neon-mlal.S",
+      "src/src/qs8-gemm/gen/qs8-gemm-2x8c16-minmax-fp32-asm-aarch64-neon-mlal.S",
+      "src/src/qs8-gemm/gen/qs8-gemm-2x8c16-minmax-rndnu-asm-aarch64-neon-mlal.S",
+      "src/src/qs8-gemm/gen/qs8-gemm-2x8c8-minmax-fp32-asm-aarch64-neon-mlal-cortex-a53-prfm.S",
+      "src/src/qs8-gemm/gen/qs8-gemm-2x8c8-minmax-fp32-asm-aarch64-neon-mlal-cortex-a53.S",
+      "src/src/qs8-gemm/gen/qs8-gemm-2x8c8-minmax-fp32-asm-aarch64-neon-mlal-prfm.S",
+      "src/src/qs8-gemm/gen/qs8-gemm-2x8c8-minmax-fp32-asm-aarch64-neon-mlal.S",
+      "src/src/qs8-gemm/gen/qs8-gemm-2x8c8-minmax-fp32-asm-aarch64-neon-mull.S",
+      "src/src/qs8-gemm/gen/qs8-gemm-2x8c8-minmax-rndnu-asm-aarch64-neon-mlal-cortex-a53-prfm.S",
+      "src/src/qs8-gemm/gen/qs8-gemm-2x8c8-minmax-rndnu-asm-aarch64-neon-mlal-cortex-a53.S",
+      "src/src/qs8-gemm/gen/qs8-gemm-2x8c8-minmax-rndnu-asm-aarch64-neon-mlal-prfm.S",
+      "src/src/qs8-gemm/gen/qs8-gemm-2x8c8-minmax-rndnu-asm-aarch64-neon-mlal.S",
+      "src/src/qs8-gemm/gen/qs8-gemm-2x8c8-minmax-rndnu-asm-aarch64-neon-mull.S",
+      "src/src/qs8-gemm/gen/qs8-gemm-4x16-minmax-fp32-asm-aarch64-neon-mlal-lane-cortex-a53-prfm.S",
+      "src/src/qs8-gemm/gen/qs8-gemm-4x16-minmax-fp32-asm-aarch64-neon-mlal-lane-cortex-a53.S",
+      "src/src/qs8-gemm/gen/qs8-gemm-4x16-minmax-fp32-asm-aarch64-neon-mlal-lane-ld64-prfm.S",
+      "src/src/qs8-gemm/gen/qs8-gemm-4x16-minmax-fp32-asm-aarch64-neon-mlal-lane-ld64.S",
+      "src/src/qs8-gemm/gen/qs8-gemm-4x16-minmax-rndnu-asm-aarch64-neon-mlal-lane-cortex-a53-prfm.S",
+      "src/src/qs8-gemm/gen/qs8-gemm-4x16-minmax-rndnu-asm-aarch64-neon-mlal-lane-cortex-a53.S",
+      "src/src/qs8-gemm/gen/qs8-gemm-4x16-minmax-rndnu-asm-aarch64-neon-mlal-lane-ld64-prfm.S",
+      "src/src/qs8-gemm/gen/qs8-gemm-4x16-minmax-rndnu-asm-aarch64-neon-mlal-lane-ld64.S",
+      "src/src/qs8-gemm/gen/qs8-gemm-4x16c4-minmax-fp32-asm-aarch64-neondot-cortex-a55.S",
+      "src/src/qs8-gemm/gen/qs8-gemm-4x16c4-minmax-fp32-asm-aarch64-neondot-ld128.S",
+      "src/src/qs8-gemm/gen/qs8-gemm-4x16c4-minmax-fp32-asm-aarch64-neondot-ld32.S",
+      "src/src/qs8-gemm/gen/qs8-gemm-4x16c4-minmax-fp32-asm-aarch64-neondot-ld64.S",
+      "src/src/qs8-gemm/gen/qs8-gemm-4x16c4-minmax-rndnu-asm-aarch64-neondot-cortex-a55.S",
+      "src/src/qs8-gemm/gen/qs8-gemm-4x16c4-minmax-rndnu-asm-aarch64-neondot-ld128.S",
+      "src/src/qs8-gemm/gen/qs8-gemm-4x16c4-minmax-rndnu-asm-aarch64-neondot-ld32.S",
+      "src/src/qs8-gemm/gen/qs8-gemm-4x16c4-minmax-rndnu-asm-aarch64-neondot-ld64.S",
+      "src/src/qs8-gemm/gen/qs8-gemm-4x8-minmax-rndnu-asm-aarch64-neon-mlal-lane-ld64-prfm.S",
+      "src/src/qs8-gemm/gen/qs8-gemm-4x8-minmax-rndnu-asm-aarch64-neon-mlal-lane-ld64.S",
+    ]
+
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+    configs += [ "//build/config/sanitizers:cfi_icall_generalize_pointers" ]
+
+    deps = [
+      "//third_party/cpuinfo",
+      "//third_party/fp16",
+      "//third_party/fxdiv",
+      "//third_party/pthreadpool:pthreadpool_standalone",
+    ]
+
+    public_configs = [ ":xnnpack_config" ]
+
+    if (!(is_android && use_order_profiling)) {
+      assert_no_deps = [ "//base" ]
+    }
+  }
+
+  source_set("qs8-igemm_arch=armv8.2-a+fp16+dotprod") {
+    cflags = [ "-march=armv8.2-a+fp16+dotprod" ]
+
+    asmflags = cflags
+
+    sources = [
+      "src/src/qs8-igemm/gen/qs8-igemm-1x8c8-minmax-fp32-asm-aarch64-neon-mlal-cortex-a53-prfm.S",
+      "src/src/qs8-igemm/gen/qs8-igemm-1x8c8-minmax-fp32-asm-aarch64-neon-mlal-cortex-a53.S",
+      "src/src/qs8-igemm/gen/qs8-igemm-1x8c8-minmax-fp32-asm-aarch64-neon-mlal-prfm.S",
+      "src/src/qs8-igemm/gen/qs8-igemm-1x8c8-minmax-fp32-asm-aarch64-neon-mlal.S",
+      "src/src/qs8-igemm/gen/qs8-igemm-1x8c8-minmax-rndnu-asm-aarch64-neon-mlal-cortex-a53-prfm.S",
+      "src/src/qs8-igemm/gen/qs8-igemm-1x8c8-minmax-rndnu-asm-aarch64-neon-mlal-cortex-a53.S",
+      "src/src/qs8-igemm/gen/qs8-igemm-1x8c8-minmax-rndnu-asm-aarch64-neon-mlal-prfm.S",
+      "src/src/qs8-igemm/gen/qs8-igemm-1x8c8-minmax-rndnu-asm-aarch64-neon-mlal.S",
+      "src/src/qs8-igemm/gen/qs8-igemm-2x8c16-minmax-fp32-asm-aarch64-neon-mlal.S",
+      "src/src/qs8-igemm/gen/qs8-igemm-2x8c16-minmax-rndnu-asm-aarch64-neon-mlal.S",
+      "src/src/qs8-igemm/gen/qs8-igemm-2x8c8-minmax-fp32-asm-aarch64-neon-mlal-cortex-a53-prfm.S",
+      "src/src/qs8-igemm/gen/qs8-igemm-2x8c8-minmax-fp32-asm-aarch64-neon-mlal-cortex-a53.S",
+      "src/src/qs8-igemm/gen/qs8-igemm-2x8c8-minmax-fp32-asm-aarch64-neon-mlal-prfm.S",
+      "src/src/qs8-igemm/gen/qs8-igemm-2x8c8-minmax-fp32-asm-aarch64-neon-mlal.S",
+      "src/src/qs8-igemm/gen/qs8-igemm-2x8c8-minmax-rndnu-asm-aarch64-neon-mlal-cortex-a53-prfm.S",
+      "src/src/qs8-igemm/gen/qs8-igemm-2x8c8-minmax-rndnu-asm-aarch64-neon-mlal-cortex-a53.S",
+      "src/src/qs8-igemm/gen/qs8-igemm-2x8c8-minmax-rndnu-asm-aarch64-neon-mlal-prfm.S",
+      "src/src/qs8-igemm/gen/qs8-igemm-2x8c8-minmax-rndnu-asm-aarch64-neon-mlal.S",
+      "src/src/qs8-igemm/gen/qs8-igemm-4x16-minmax-fp32-asm-aarch64-neon-mlal-lane-cortex-a53-prfm.S",
+      "src/src/qs8-igemm/gen/qs8-igemm-4x16-minmax-fp32-asm-aarch64-neon-mlal-lane-cortex-a53.S",
+      "src/src/qs8-igemm/gen/qs8-igemm-4x16-minmax-fp32-asm-aarch64-neon-mlal-lane-ld64-prfm.S",
+      "src/src/qs8-igemm/gen/qs8-igemm-4x16-minmax-fp32-asm-aarch64-neon-mlal-lane-ld64.S",
+      "src/src/qs8-igemm/gen/qs8-igemm-4x16-minmax-rndnu-asm-aarch64-neon-mlal-lane-cortex-a53-prfm.S",
+      "src/src/qs8-igemm/gen/qs8-igemm-4x16-minmax-rndnu-asm-aarch64-neon-mlal-lane-cortex-a53.S",
+      "src/src/qs8-igemm/gen/qs8-igemm-4x16-minmax-rndnu-asm-aarch64-neon-mlal-lane-ld64-prfm.S",
+      "src/src/qs8-igemm/gen/qs8-igemm-4x16-minmax-rndnu-asm-aarch64-neon-mlal-lane-ld64.S",
+      "src/src/qs8-igemm/gen/qs8-igemm-4x16c4-minmax-fp32-asm-aarch64-neondot-cortex-a55.S",
+      "src/src/qs8-igemm/gen/qs8-igemm-4x16c4-minmax-fp32-asm-aarch64-neondot-ld128.S",
+      "src/src/qs8-igemm/gen/qs8-igemm-4x16c4-minmax-fp32-asm-aarch64-neondot-ld64.S",
+      "src/src/qs8-igemm/gen/qs8-igemm-4x16c4-minmax-rndnu-asm-aarch64-neondot-cortex-a55.S",
+      "src/src/qs8-igemm/gen/qs8-igemm-4x16c4-minmax-rndnu-asm-aarch64-neondot-ld128.S",
+      "src/src/qs8-igemm/gen/qs8-igemm-4x16c4-minmax-rndnu-asm-aarch64-neondot-ld64.S",
+      "src/src/qs8-igemm/gen/qs8-igemm-4x8-minmax-rndnu-asm-aarch64-neon-mlal-lane-ld64-prfm.S",
+      "src/src/qs8-igemm/gen/qs8-igemm-4x8-minmax-rndnu-asm-aarch64-neon-mlal-lane-ld64.S",
+    ]
+
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+    configs += [ "//build/config/sanitizers:cfi_icall_generalize_pointers" ]
+
+    deps = [
+      "//third_party/cpuinfo",
+      "//third_party/fp16",
+      "//third_party/fxdiv",
+      "//third_party/pthreadpool",
+    ]
+
+    public_configs = [ ":xnnpack_config" ]
+  }
+
+  # This is a target that cannot depend on //base.
+  source_set("qs8-igemm_arch=armv8.2-a+fp16+dotprod_standalone") {
+    cflags = [ "-march=armv8.2-a+fp16+dotprod" ]
+
+    asmflags = cflags
+
+    sources = [
+      "src/src/qs8-igemm/gen/qs8-igemm-1x8c8-minmax-fp32-asm-aarch64-neon-mlal-cortex-a53-prfm.S",
+      "src/src/qs8-igemm/gen/qs8-igemm-1x8c8-minmax-fp32-asm-aarch64-neon-mlal-cortex-a53.S",
+      "src/src/qs8-igemm/gen/qs8-igemm-1x8c8-minmax-fp32-asm-aarch64-neon-mlal-prfm.S",
+      "src/src/qs8-igemm/gen/qs8-igemm-1x8c8-minmax-fp32-asm-aarch64-neon-mlal.S",
+      "src/src/qs8-igemm/gen/qs8-igemm-1x8c8-minmax-rndnu-asm-aarch64-neon-mlal-cortex-a53-prfm.S",
+      "src/src/qs8-igemm/gen/qs8-igemm-1x8c8-minmax-rndnu-asm-aarch64-neon-mlal-cortex-a53.S",
+      "src/src/qs8-igemm/gen/qs8-igemm-1x8c8-minmax-rndnu-asm-aarch64-neon-mlal-prfm.S",
+      "src/src/qs8-igemm/gen/qs8-igemm-1x8c8-minmax-rndnu-asm-aarch64-neon-mlal.S",
+      "src/src/qs8-igemm/gen/qs8-igemm-2x8c16-minmax-fp32-asm-aarch64-neon-mlal.S",
+      "src/src/qs8-igemm/gen/qs8-igemm-2x8c16-minmax-rndnu-asm-aarch64-neon-mlal.S",
+      "src/src/qs8-igemm/gen/qs8-igemm-2x8c8-minmax-fp32-asm-aarch64-neon-mlal-cortex-a53-prfm.S",
+      "src/src/qs8-igemm/gen/qs8-igemm-2x8c8-minmax-fp32-asm-aarch64-neon-mlal-cortex-a53.S",
+      "src/src/qs8-igemm/gen/qs8-igemm-2x8c8-minmax-fp32-asm-aarch64-neon-mlal-prfm.S",
+      "src/src/qs8-igemm/gen/qs8-igemm-2x8c8-minmax-fp32-asm-aarch64-neon-mlal.S",
+      "src/src/qs8-igemm/gen/qs8-igemm-2x8c8-minmax-rndnu-asm-aarch64-neon-mlal-cortex-a53-prfm.S",
+      "src/src/qs8-igemm/gen/qs8-igemm-2x8c8-minmax-rndnu-asm-aarch64-neon-mlal-cortex-a53.S",
+      "src/src/qs8-igemm/gen/qs8-igemm-2x8c8-minmax-rndnu-asm-aarch64-neon-mlal-prfm.S",
+      "src/src/qs8-igemm/gen/qs8-igemm-2x8c8-minmax-rndnu-asm-aarch64-neon-mlal.S",
+      "src/src/qs8-igemm/gen/qs8-igemm-4x16-minmax-fp32-asm-aarch64-neon-mlal-lane-cortex-a53-prfm.S",
+      "src/src/qs8-igemm/gen/qs8-igemm-4x16-minmax-fp32-asm-aarch64-neon-mlal-lane-cortex-a53.S",
+      "src/src/qs8-igemm/gen/qs8-igemm-4x16-minmax-fp32-asm-aarch64-neon-mlal-lane-ld64-prfm.S",
+      "src/src/qs8-igemm/gen/qs8-igemm-4x16-minmax-fp32-asm-aarch64-neon-mlal-lane-ld64.S",
+      "src/src/qs8-igemm/gen/qs8-igemm-4x16-minmax-rndnu-asm-aarch64-neon-mlal-lane-cortex-a53-prfm.S",
+      "src/src/qs8-igemm/gen/qs8-igemm-4x16-minmax-rndnu-asm-aarch64-neon-mlal-lane-cortex-a53.S",
+      "src/src/qs8-igemm/gen/qs8-igemm-4x16-minmax-rndnu-asm-aarch64-neon-mlal-lane-ld64-prfm.S",
+      "src/src/qs8-igemm/gen/qs8-igemm-4x16-minmax-rndnu-asm-aarch64-neon-mlal-lane-ld64.S",
+      "src/src/qs8-igemm/gen/qs8-igemm-4x16c4-minmax-fp32-asm-aarch64-neondot-cortex-a55.S",
+      "src/src/qs8-igemm/gen/qs8-igemm-4x16c4-minmax-fp32-asm-aarch64-neondot-ld128.S",
+      "src/src/qs8-igemm/gen/qs8-igemm-4x16c4-minmax-fp32-asm-aarch64-neondot-ld64.S",
+      "src/src/qs8-igemm/gen/qs8-igemm-4x16c4-minmax-rndnu-asm-aarch64-neondot-cortex-a55.S",
+      "src/src/qs8-igemm/gen/qs8-igemm-4x16c4-minmax-rndnu-asm-aarch64-neondot-ld128.S",
+      "src/src/qs8-igemm/gen/qs8-igemm-4x16c4-minmax-rndnu-asm-aarch64-neondot-ld64.S",
+      "src/src/qs8-igemm/gen/qs8-igemm-4x8-minmax-rndnu-asm-aarch64-neon-mlal-lane-ld64-prfm.S",
+      "src/src/qs8-igemm/gen/qs8-igemm-4x8-minmax-rndnu-asm-aarch64-neon-mlal-lane-ld64.S",
+    ]
+
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+    configs += [ "//build/config/sanitizers:cfi_icall_generalize_pointers" ]
+
+    deps = [
+      "//third_party/cpuinfo",
+      "//third_party/fp16",
+      "//third_party/fxdiv",
+      "//third_party/pthreadpool:pthreadpool_standalone",
+    ]
+
+    public_configs = [ ":xnnpack_config" ]
+
+    if (!(is_android && use_order_profiling)) {
+      assert_no_deps = [ "//base" ]
+    }
+  }
+
   source_set("qs8-qc8w-gemm_arch=armv8.2-a+fp16+dotprod") {
     cflags = [ "-march=armv8.2-a+fp16+dotprod" ]
 
@@ -3362,7 +3489,6 @@
       "src/src/subgraph/multiply2.c",
       "src/src/subgraph/negate.c",
       "src/src/subgraph/prelu.c",
-      "src/src/subgraph/reshape-helpers.c",
       "src/src/subgraph/rope.c",
       "src/src/subgraph/scaled-dot-product-attention.c",
       "src/src/subgraph/sigmoid.c",
@@ -3433,7 +3559,6 @@
       "src/src/subgraph/multiply2.c",
       "src/src/subgraph/negate.c",
       "src/src/subgraph/prelu.c",
-      "src/src/subgraph/reshape-helpers.c",
       "src/src/subgraph/rope.c",
       "src/src/subgraph/scaled-dot-product-attention.c",
       "src/src/subgraph/sigmoid.c",
diff --git a/third_party/xnnpack/README.chromium b/third_party/xnnpack/README.chromium
index e8b7eff..6f945e6 100644
--- a/third_party/xnnpack/README.chromium
+++ b/third_party/xnnpack/README.chromium
@@ -1,8 +1,8 @@
 Name: XNNPACK
 Short Name: xnnpack
 URL: https://github.com/google/xnnpack
-Version: 8951decff5114f70bae7cc2e23b732812e73acc7
-Date: 2024-01-19
+Version: a68aa0a24b0d3e1c75f2f7c0915b70121cee0470
+Date: 2024-01-09
 License: BSD
 License File: src/LICENSE
 Security Critical: Yes
diff --git a/third_party/xnnpack/src b/third_party/xnnpack/src
index 8951dec..a68aa0a 160000
--- a/third_party/xnnpack/src
+++ b/third_party/xnnpack/src
@@ -1 +1 @@
-Subproject commit 8951decff5114f70bae7cc2e23b732812e73acc7
+Subproject commit a68aa0a24b0d3e1c75f2f7c0915b70121cee0470
diff --git a/tools/clang/rewrite_raw_ptr_fields/RewriteRawPtrFields.cpp b/tools/clang/rewrite_raw_ptr_fields/RewriteRawPtrFields.cpp
index 170c59a..d9fb605 100644
--- a/tools/clang/rewrite_raw_ptr_fields/RewriteRawPtrFields.cpp
+++ b/tools/clang/rewrite_raw_ptr_fields/RewriteRawPtrFields.cpp
@@ -740,13 +740,23 @@
 
     match_finder.addMatcher(global_scope_matcher, &global_scope_rewriter);
 
+    // This is used to exclude unions from certain files that are known to have
+    // safe usage of union (i.e. doesn't cause ref count mismatch), such as
+    // std::optional and absl::variant.
+    files_with_audited_unions =
+        std::make_unique<FilterFile>(std::vector<std::string>{
+            "third_party/libc++/src/include/optional",
+            "third_party/abseil-cpp/absl/types/internal/variant.h",
+        });
     // Matches fields in unions (both directly rewritable fields as well as
     // union fields that embed a struct that contains a rewritable field).  See
     // also the testcases in tests/gen-unions-test.cc.
     auto union_field_decl_matcher = recordDecl(allOf(
-        isUnion(), forEach(fieldDecl(anyOf(field_decl_matcher,
-                                           hasType(typeWithEmbeddedFieldDecl(
-                                               field_decl_matcher)))))));
+        isUnion(),
+        unless(isInLocationListedInFilterFile(files_with_audited_unions.get())),
+        forEach(fieldDecl(
+            anyOf(field_decl_matcher,
+                  hasType(typeWithEmbeddedFieldDecl(field_decl_matcher)))))));
 
     match_finder.addMatcher(union_field_decl_matcher, &union_field_decl_writer);
 
@@ -806,6 +816,7 @@
   FilteredExprWriter global_scope_rewriter;
   FilteredExprWriter union_field_decl_writer;
   FilteredExprWriter reinterpret_cast_struct_writer;
+  std::unique_ptr<FilterFile> files_with_audited_unions;
   const RawPtrAndRefExclusionsOptions exclusion_options_;
 };
 
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 20f55af3..8800e0e 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -7728,6 +7728,141 @@
   <int value="67" label="Drawer - Autofill"/>
 </enum>
 
+<enum name="DevToolsPanelWithLocation">
+  <int value="1" label="Elements - Main"/>
+  <int value="2" label="Elements - Drawer"/>
+  <int value="3" label="Resources - Main"/>
+  <int value="4" label="Resources - Drawer"/>
+  <int value="5" label="Network - Main"/>
+  <int value="6" label="Network - Drawer"/>
+  <int value="7" label="Sources - Main"/>
+  <int value="8" label="Sources - Drawer"/>
+  <int value="9" label="Performance - Main"/>
+  <int value="10" label="Performance - Drawer"/>
+  <int value="11" label="Memory - Main"/>
+  <int value="12" label="Memory - Drawer"/>
+  <int value="13" label="Console - Main"/>
+  <int value="14" label="Console - Drawer"/>
+  <int value="15" label="Layers - Main"/>
+  <int value="16" label="Layers - Drawer"/>
+  <int value="17" label="Console View - Main"/>
+  <int value="18" label="Console View - Drawer"/>
+  <int value="19" label="Animations - Main"/>
+  <int value="20" label="Animations - Drawer"/>
+  <int value="21" label="Network config - Main"/>
+  <int value="22" label="Network config - Drawer"/>
+  <int value="23" label="Rendering - Main"/>
+  <int value="24" label="Rendering - Drawer"/>
+  <int value="25" label="Sensors - Main"/>
+  <int value="26" label="Sensors - Drawer"/>
+  <int value="27" label="Search - Main"/>
+  <int value="28" label="Search - Drawer"/>
+  <int value="29" label="Security - Main"/>
+  <int value="30" label="Security - Drawer"/>
+  <int value="31" label="JavaScript Profiler - Main"/>
+  <int value="32" label="JavaScript Profiler - Drawer"/>
+  <int value="33" label="Lighthouse - Main"/>
+  <int value="34" label="Lighthouse - Drawer"/>
+  <int value="35" label="Coverage - Main"/>
+  <int value="36" label="Coverage - Drawer"/>
+  <int value="37" label="Protocol monitor - Main"/>
+  <int value="38" label="Protocol monitor - Drawer"/>
+  <int value="39" label="Remote devices - Main"/>
+  <int value="40" label="Remote devices - Drawer"/>
+  <int value="41" label="WebAudio - Main"/>
+  <int value="42" label="WebAudio - Drawer"/>
+  <int value="43" label="Changes - Main"/>
+  <int value="44" label="Changes - Drawer"/>
+  <int value="45" label="Performance monitor - Main"/>
+  <int value="46" label="Performance monitor - Drawer"/>
+  <int value="47" label="What's New - Main"/>
+  <int value="48" label="What's New - Drawer"/>
+  <int value="49" label="Live Heap Profile - Main"/>
+  <int value="50" label="Live Heap Profile - Drawer"/>
+  <int value="51" label="Quick source - Main"/>
+  <int value="52" label="Quick source - Drawer"/>
+  <int value="53" label="Request blocking - Main"/>
+  <int value="54" label="Request blocking - Drawer"/>
+  <int value="55" label="Settings - Preferences - Main"/>
+  <int value="56" label="Settings - Preferences - Drawer"/>
+  <int value="57" label="Settings - Workspace - Main"/>
+  <int value="58" label="Settings - Workspace - Drawer"/>
+  <int value="59" label="Settings - Experiments - Main"/>
+  <int value="60" label="Settings - Experiments - Drawer"/>
+  <int value="61" label="Settings - Blackbox - Main"/>
+  <int value="62" label="Settings - Blackbox - Drawer"/>
+  <int value="63" label="Settings - Devices - Main"/>
+  <int value="64" label="Settings - Devices - Drawer"/>
+  <int value="65" label="Settings - Throttling Conditions - Main"/>
+  <int value="66" label="Settings - Throttling Conditions - Drawer"/>
+  <int value="67" label="Settings - Emulation Geolocations - Main"/>
+  <int value="68" label="Settings - Emulation Geolocations - Drawer"/>
+  <int value="69" label="Settings - Shortcuts - Main"/>
+  <int value="70" label="Settings - Shortcuts - Drawer"/>
+  <int value="71" label="Issues - Main"/>
+  <int value="72" label="Issues - Drawer"/>
+  <int value="73" label="Settings - Shortcuts (custom) - Main"/>
+  <int value="74" label="Settings - Shortcuts (custom) - Drawer"/>
+  <int value="75" label="CSS Overview - Main"/>
+  <int value="76" label="CSS Overview - Drawer"/>
+  <int value="77" label="Recorder - Main"/>
+  <int value="78" label="Recorder - Drawer"/>
+  <int value="79" label="Application - Trust Tokens - Main"/>
+  <int value="80" label="Application - Trust Tokens - Drawer"/>
+  <int value="81" label="Application - Reporting API - Main"/>
+  <int value="82" label="Application - Reporting API - Drawer"/>
+  <int value="83" label="Application - Interest Groups - Main"/>
+  <int value="84" label="Application - Interest Groups - Drawer"/>
+  <int value="85" label="Application - Back/forward cache - Main"/>
+  <int value="86" label="Application - Back/forward cache - Drawer"/>
+  <int value="87" label="Application - Cache Storage - Main"/>
+  <int value="88" label="Application - Cache Storage - Drawer"/>
+  <int value="89" label="Application - Background Fetch - Main"/>
+  <int value="90" label="Application - Background Fetch - Drawer"/>
+  <int value="91" label="Application - Background Sync - Main"/>
+  <int value="92" label="Application - Background Sync - Drawer"/>
+  <int value="93" label="Application - Push Messaging - Main"/>
+  <int value="94" label="Application - Push Messaging - Drawer"/>
+  <int value="95" label="Application - Notifications - Main"/>
+  <int value="96" label="Application - Notifications - Drawer"/>
+  <int value="97" label="Application - Payment Handler - Main"/>
+  <int value="98" label="Application - Payment Handler - Drawer"/>
+  <int value="99" label="Application - Periodic Background Sync - Main"/>
+  <int value="100" label="Application - Periodic Background Sync - Drawer"/>
+  <int value="101" label="Application - Service Workers - Main"/>
+  <int value="102" label="Application - Service Workers - Drawer"/>
+  <int value="103" label="Application - Manifest - Main"/>
+  <int value="104" label="Application - Manifest - Drawer"/>
+  <int value="105" label="Application - Storage - Main"/>
+  <int value="106" label="Application - Storage - Drawer"/>
+  <int value="107" label="Application - Cookies - Main"/>
+  <int value="108" label="Application - Cookies - Drawer"/>
+  <int value="109" label="Application - Frame Details - Main"/>
+  <int value="110" label="Application - Frame Details - Drawer"/>
+  <int value="111" label="Application - Frame Resource - Main"/>
+  <int value="112" label="Application - Frame Resource - Drawer"/>
+  <int value="113" label="Application - Frame Window - Main"/>
+  <int value="114" label="Application - Frame Window - Drawer"/>
+  <int value="115" label="Application - Frame Worker - Main"/>
+  <int value="116" label="Application - Frame Worker - Drawer"/>
+  <int value="117" label="Application - Local/Session Storage - Main"/>
+  <int value="118" label="Application - Local/Session Storage - Drawer"/>
+  <int value="119" label="Application - IndexedDB - Main"/>
+  <int value="120" label="Application - IndexedDB - Drawer"/>
+  <int value="121" label="Application - Web SQL - Main"/>
+  <int value="122" label="Application - Web SQL - Drawer"/>
+  <int value="123" label="Performance Insights - Main"/>
+  <int value="124" label="Performance Insights - Drawer"/>
+  <int value="125" label="Application - Preloading - Main"/>
+  <int value="126" label="Application - Preloading - Drawer"/>
+  <int value="127" label="Application - Bounce Tracking Mitigations - Main"/>
+  <int value="128" label="Application - Bounce Tracking Mitigations - Drawer"/>
+  <int value="129" label="Resource Loading - Main"/>
+  <int value="130" label="Resource Loading - Drawer"/>
+  <int value="131" label="Autofill - Main"/>
+  <int value="132" label="Autofill - Drawer"/>
+</enum>
+
 <enum name="DevToolsRecordingAssertion">
   <int value="1" label="Assertion added"/>
   <int value="2" label="Property assertion edited"/>
@@ -21144,6 +21279,8 @@
   <int value="-1414699084" label="CameraAppLowStorageWarning:enabled"/>
   <int value="-1414531531" label="NtpModulesRedesignedLayout:disabled"/>
   <int value="-1414207639" label="RequestDesktopSiteDefaultsLogging:enabled"/>
+  <int value="-1412920508"
+      label="UserDisplayModeSyncStandaloneMitigation:enabled"/>
   <int value="-1412230070" label="query-tiles-instant-background-task"/>
   <int value="-1411980923" label="LinkCapturingUiUpdate:enabled"/>
   <int value="-1411777757" label="WebRtcSendPacketBatch:enabled"/>
@@ -21169,6 +21306,8 @@
   <int value="-1405048637" label="OfflinePagesResourceBasedSnapshot:enabled"/>
   <int value="-1404469375" label="RemoteCopyProgressNotification:enabled"/>
   <int value="-1404088616" label="TetheringExperimentalFunctionality:enabled"/>
+  <int value="-1404072922"
+      label="UserDisplayModeSyncBrowserMitigation:enabled"/>
   <int value="-1401308932" label="CaptureModeEducation:disabled"/>
   <int value="-1400745093" label="SystemEmojiPickerSearchExtension:enabled"/>
   <int value="-1400693146" label="LauncherImageSearchIca:enabled"/>
@@ -24312,6 +24451,7 @@
   <int value="61141457" label="DecodeScriptSourceOffThread:enabled"/>
   <int value="61205887" label="enable-text-input-focus-manager"/>
   <int value="61466986" label="AsyncDns:disabled"/>
+  <int value="61770125" label="LinkPreview:enabled"/>
   <int value="63187126" label="DownloadsAutoResumptionNative:enabled"/>
   <int value="63693157" label="cast-streaming-force-disable-hardware-h264"/>
   <int value="64016368" label="SafeBrowsingSeparateNetworkContexts:enabled"/>
@@ -24387,6 +24527,8 @@
   <int value="98134240" label="material-design-ink-drop-animation-speed"/>
   <int value="98218116" label="ContextualSearchLongpressResolve:disabled"/>
   <int value="98773632" label="DesktopPWAsTabStripCustomizations:enabled"/>
+  <int value="99128921"
+      label="SyncOnlySeparateUserDisplayModeForCrOS:disabled"/>
   <int value="99177659" label="OmniboxUICuesForSearchHistoryMatches:disabled"/>
   <int value="99660832" label="DockedMagnifierResizing:enabled"/>
   <int value="101253756" label="RequestDesktopSiteDefaults:disabled"/>
@@ -24914,6 +25056,7 @@
   <int value="345611669"
       label="OmniboxLocalZeroSuggestFrecencyRanking:enabled"/>
   <int value="345664265" label="BlinkHeapIncrementalMarking:disabled"/>
+  <int value="346080486" label="LinkPreview:disabled"/>
   <int value="346117324" label="FedCmLogoutRps:disabled"/>
   <int value="346406287" label="EnableWebAppUninstallFromOsSettings:disabled"/>
   <int value="346430505" label="SidePanel:enabled"/>
@@ -25358,6 +25501,7 @@
   <int value="552421509" label="AssistMultiWordExpanded:enabled"/>
   <int value="552724079" label="NotifierCollision:enabled"/>
   <int value="553197994" label="FedCmMetricsEndpoint:enabled"/>
+  <int value="554454443" label="SeparateUserDisplayModeForCrOS:enabled"/>
   <int value="555959995" label="ChromeSharingHub:enabled"/>
   <int value="556555487"
       label="AutofillDoNotUploadSaveUnsupportedCards:disabled"/>
@@ -25613,6 +25757,7 @@
   <int value="674788251" label="EnableNewBadgeOnMenuItems:enabled"/>
   <int value="675427884" label="EnableFakeMouseHeuristic:enabled"/>
   <int value="676353150" label="OmniboxRetainSuggestionsWithHeaders:disabled"/>
+  <int value="676922235" label="SeparateUserDisplayModeForCrOS:disabled"/>
   <int value="677866592" label="ClickToCallUI:disabled"/>
   <int value="677883800" label="DiagnosticsAppNavigation:disabled"/>
   <int value="678752544" label="VcWebApi:enabled"/>
@@ -26466,6 +26611,8 @@
       label="OmniboxMostVisitedTilesHorizontalRenderGroup:enabled"/>
   <int value="1084365949" label="ReleaseNotesNotification:disabled"/>
   <int value="1084484359" label="ContextualTriggersSelectionSize:disabled"/>
+  <int value="1084768575"
+      label="SyncOnlySeparateUserDisplayModeForCrOS:enabled"/>
   <int value="1084972292" label="WebXRAnchors:enabled"/>
   <int value="1085130793" label="LongPressBackForHistory:disabled"/>
   <int value="1085260595" label="WebAuthenticationAndroidCredMan:disabled"/>
@@ -26499,6 +26646,8 @@
   <int value="1095166532" label="PasspointARCSupport:disabled"/>
   <int value="1095706700" label="NavigationPredictor:enabled"/>
   <int value="1096477444" label="AssistantVoiceMatch:enabled"/>
+  <int value="1097339521"
+      label="UserDisplayModeSyncStandaloneMitigation:disabled"/>
   <int value="1097935876" label="CrostiniShowMicSetting:enabled"/>
   <int value="1097981222" label="OneGoogleBarModalOverlays:disabled"/>
   <int value="1098461149" label="AppToAppLinkCapturing:disabled"/>
@@ -27377,6 +27526,8 @@
   <int value="1506372577" label="NearbySharingSelfShare:disabled"/>
   <int value="1507233295" label="FilesTrash:disabled"/>
   <int value="1508761653" label="disable-site-isolation-trials"/>
+  <int value="1509300278"
+      label="UserDisplayModeSyncBrowserMitigation:disabled"/>
   <int value="1509482104"
       label="SupervisedPrefsControlledBySupervisedStore:disabled"/>
   <int value="1509586356" label="CrossOriginOpenerPolicy:disabled"/>
diff --git a/tools/metrics/histograms/metadata/android/enums.xml b/tools/metrics/histograms/metadata/android/enums.xml
index 8e63cda8d..60433ee 100644
--- a/tools/metrics/histograms/metadata/android/enums.xml
+++ b/tools/metrics/histograms/metadata/android/enums.xml
@@ -1173,6 +1173,12 @@
   <int value="39" label="CvcSave"/>
   <int value="40" label="TrackingProtectionNotice"/>
   <int value="41" label="DesktopSiteWindowSetting"/>
+  <int value="42" label="PromptHatsLocationCustomInvitation"/>
+  <int value="43" label="PromptHatsLocationGenericInvitation"/>
+  <int value="44" label="PromptHatsCameraCustomInvitation"/>
+  <int value="45" label="PromptHatsCameraGenericInvitation"/>
+  <int value="46" label="PromptHatsMicrophoneCustomInvitation"/>
+  <int value="47" label="PromptHatsMicrophoneGenericInvitation"/>
 </enum>
 
 <enum name="MinimizeAppAndCloseTabType">
diff --git a/tools/metrics/histograms/metadata/android/histograms.xml b/tools/metrics/histograms/metadata/android/histograms.xml
index 0e939a91..34d8f98 100644
--- a/tools/metrics/histograms/metadata/android/histograms.xml
+++ b/tools/metrics/histograms/metadata/android/histograms.xml
@@ -139,6 +139,12 @@
   <variant name=".OfferNotification"/>
   <variant name=".PermissionUpdate"/>
   <variant name=".PopupBlocked"/>
+  <variant name=".PromptHatsCameraCustomInvitation"/>
+  <variant name=".PromptHatsCameraGenericInvitation"/>
+  <variant name=".PromptHatsLocationCustomInvitation"/>
+  <variant name=".PromptHatsLocationGenericInvitation"/>
+  <variant name=".PromptHatsMicrophoneCustomInvitation"/>
+  <variant name=".PromptHatsMicrophoneGenericInvitation"/>
   <variant name=".ReaderMode"/>
   <variant name=".SafetyTip"/>
   <variant name=".SaveAddressProfile"/>
diff --git a/tools/metrics/histograms/metadata/autofill/enums.xml b/tools/metrics/histograms/metadata/autofill/enums.xml
index 35456be9..70bb277 100644
--- a/tools/metrics/histograms/metadata/autofill/enums.xml
+++ b/tools/metrics/histograms/metadata/autofill/enums.xml
@@ -539,6 +539,7 @@
   <int value="12" label="Credit card expiration date"/>
   <int value="13" label="Credir card expiration year"/>
   <int value="14" label="Credit card expiration month"/>
+  <int value="15" label="City"/>
 </enum>
 
 <enum name="AutofillFieldPredictionQuality">
diff --git a/tools/metrics/histograms/metadata/chromeos/histograms.xml b/tools/metrics/histograms/metadata/chromeos/histograms.xml
index df29c79..dee4e70 100644
--- a/tools/metrics/histograms/metadata/chromeos/histograms.xml
+++ b/tools/metrics/histograms/metadata/chromeos/histograms.xml
@@ -1732,6 +1732,25 @@
   </token>
 </histogram>
 
+<histogram name="ChromeOS.Inputs.ModifierKeyCombo.{KeyboardType}" units="hash"
+    expires_after="2024-05-26">
+  <owner>dpad@chromium.org</owner>
+  <owner>cros-peripherals@google.com</owner>
+  <summary>
+    Records how users use their physical {KeyboardType} keyboards. It groups
+    keys into common categories such as &quot;Alpha&quot; and
+    &quot;Digits&quot;.
+
+    See //ash/accelerators/modifier_key_combo_recorder.cc for info on how to
+    decode the metric.
+  </summary>
+  <token key="KeyboardType">
+    <variant name="CrOSExternal"/>
+    <variant name="External"/>
+    <variant name="Internal"/>
+  </token>
+</histogram>
+
 <histogram
     name="ChromeOS.Inputs.TouchscreenUsage.Temporary.{TimePeriod}.{Mode}"
     units="ms" expires_after="2024-06-02">
diff --git a/tools/metrics/histograms/metadata/dev/histograms.xml b/tools/metrics/histograms/metadata/dev/histograms.xml
index 01e9a3ca..869039d 100644
--- a/tools/metrics/histograms/metadata/dev/histograms.xml
+++ b/tools/metrics/histograms/metadata/dev/histograms.xml
@@ -497,6 +497,15 @@
   <summary>Specified DevTools panel was shown.</summary>
 </histogram>
 
+<histogram name="DevTools.PanelShownInLocation"
+    enum="DevToolsPanelWithLocation" expires_after="2025-01-15">
+  <owner>bmeurer@chromium.org</owner>
+  <owner>ergunsh@chromium.org</owner>
+  <summary>
+    Specified DevTools panel was shown in the specified location: Main or Drawer
+  </summary>
+</histogram>
+
 <histogram name="DevTools.RecordingAssertion" enum="DevToolsRecordingAssertion"
     expires_after="2024-05-19">
   <owner>jrandolf@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/navigation/histograms.xml b/tools/metrics/histograms/metadata/navigation/histograms.xml
index 8aaf441..2956a45 100644
--- a/tools/metrics/histograms/metadata/navigation/histograms.xml
+++ b/tools/metrics/histograms/metadata/navigation/histograms.xml
@@ -939,6 +939,51 @@
 </histogram>
 
 <histogram
+    name="Navigation.MainFrame.NewNavigation.IgnoreRestore.IsHTTPOrHTTPS.{DurationFromTo}.Time"
+    units="ms" expires_after="2024-04-20">
+  <owner>chikamune@chromium.org</owner>
+  <owner>yyanagisawa@chromium.org</owner>
+  <owner>kouhei@chromium.org</owner>
+  <summary>
+    Record when (a) the navigation happened in MainFrame, (b) it was a new
+    navigation, (c) it was not a session restore, (d) the URL was http or https,
+    and (e) the navigation was successfully committed. Measures the duration
+    between {DurationFromTo}. This is also included in the trace log.
+  </summary>
+  <token key="DurationFromTo">
+    <variant name="BeginNavigationToLoaderStart"
+        summary="when the BeginNavigation starts, to when the
+                 NavigationURLLoader starts"/>
+    <variant name="FetchStart"
+        summary="when the browser is ready to fetch the document using an
+                 HTTP(S) request (which is called as fetchStart), to when the
+                 first HTTP(S) request is sent"/>
+    <variant name="LoaderStartToFetchStart"
+        summary="when the NavigationURLLoader starts, to when the browser is
+                 ready to fetch the document using an HTTP(S) request (which
+                 is called as fetchStart)"/>
+    <variant name="LoaderStartToReceiveResponse"
+        summary="when the NavigationURLLoader starts, to when the browser
+                 process receives the response (HTTP header)"/>
+    <variant name="NavigationStartToBeginNavigation"
+        summary="when the navigation starts, to when the BeginNavigation
+                 starts"/>
+    <variant name="ReceiveHeaders"
+        summary="when the first HTTP(S) request is sent, to when the final
+                 headers are received in the network service. This duration
+                 can involve multiple redirects"/>
+    <variant name="ReceiveHeadersToReceiveResponse"
+        summary="when the final headers are received in the network service,
+                 to when the browser process receives the response (HTTP
+                 header)"/>
+    <variant name="ReceiveResponseToCommitNavigation"
+        summary="when the browser process receives the response (HTTP
+                 header), to when the CommitNavigation mojo API is called in
+                 the browser process"/>
+  </token>
+</histogram>
+
+<histogram
     name="Navigation.MainFrame.RendererInitiated.InitiatorFramePresentAtStart"
     enum="BooleanPresent" expires_after="2023-10-01">
   <owner>yaoxia@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/others/histograms.xml b/tools/metrics/histograms/metadata/others/histograms.xml
index c90188cc..07eea6aa 100644
--- a/tools/metrics/histograms/metadata/others/histograms.xml
+++ b/tools/metrics/histograms/metadata/others/histograms.xml
@@ -5482,7 +5482,7 @@
 </histogram>
 
 <histogram name="FetchKeepAlive.Browser.Metrics"
-    enum="FetchKeepAliveBrowserMetricType" expires_after="2024-04-10">
+    enum="FetchKeepAliveBrowserMetricType" expires_after="2024-07-10">
   <owner>mych@chromium.org</owner>
   <owner>chrome-bfcache@google.com</owner>
   <summary>
@@ -5491,156 +5491,43 @@
   </summary>
 </histogram>
 
-<histogram name="FetchKeepAlive.Browser.Total" enum="Boolean"
-    expires_after="2024-04-10">
+<histogram name="FetchKeepAlive.Browser.Total{LoaderStatus}" enum="Boolean"
+    expires_after="2024-07-10">
   <owner>mych@chromium.org</owner>
   <owner>chrome-bfcache@google.com</owner>
   <summary>
-    Recorded true every time a new Fetch keepalive request is created by a
-    browser. It does NOT equal to the total number of JavaScript Fetch keepalive
-    call, as the latter can be immediately rejected with various JavaScript
-    exceptions.
-
-    This number should be compared with `FetchKeepAlive.Renderer.Total`.
+    Recorded true every time a Fetch keepalive request loader in browser has
+    {LoaderStatus}
   </summary>
-</histogram>
-
-<histogram name="FetchKeepAlive.Browser.Total.Finished" enum="Boolean"
-    expires_after="2024-04-10">
-  <owner>mych@chromium.org</owner>
-  <owner>chrome-bfcache@google.com</owner>
-  <summary>
-    Recorded once every time a FetchLater request loading is finished in
-    browser. A finished loading can be either suceeded or failed.
-
-    This number may be &lt;= `FetchKeepAlive.Browser.Total.Started`, as a
-    request may be dropped for other undocumented reasons like browser killed or
-    crashes. Also, this number is ~= `kLoadingSuceeded` + `kLoadingFailed` in
-    `FetchKeepAlive.Browser.Metrics`.
-  </summary>
-</histogram>
-
-<histogram name="FetchKeepAlive.Browser.Total.ReceivedResponse" enum="Boolean"
-    expires_after="2024-04-10">
-  <owner>mych@chromium.org</owner>
-  <owner>chrome-bfcache@google.com</owner>
-  <summary>
-    Recorded once every time a Fetch keepalive request loader in browser has
-    received a response.
-
-    This number should be compared with
-    `FetchKeepAlive.Renderer.Total.ReceivedResponse`.
-  </summary>
-</histogram>
-
-<histogram name="FetchKeepAlive.Browser.Total.Redirected" enum="Boolean"
-    expires_after="2024-04-10">
-  <owner>mych@chromium.org</owner>
-  <owner>chrome-bfcache@google.com</owner>
-  <summary>
-    Recorded once every time a Fetch keepalive request loader in browser has
-    received a redirect.
-
-    This number should be compared with
-    `FetchKeepAlive.Renderer.Total.Redirected`.
-  </summary>
-</histogram>
-
-<histogram name="FetchKeepAlive.Browser.Total.Started" enum="Boolean"
-    expires_after="2024-04-10">
-  <owner>mych@chromium.org</owner>
-  <owner>chrome-bfcache@google.com</owner>
-  <summary>
-    Recorded once every time a created Fetch keepalive request is started in the
-    browser. This number should be the same as `FetchKeepAlive.Browser.Total`,
-    but logging to figure out if any differneces exist.
-  </summary>
-</histogram>
-
-<histogram name="FetchKeepAlive.Renderer.Duration" units="ms"
-    expires_after="2024-06-23">
-  <owner>mych@chromium.org</owner>
-  <owner>chrome-bfcache@google.com</owner>
-  <summary>
-    Recorded the time taken to complete a fetch keepalive request in a renderer
-    process, no matter a request succeeds or fails.
-
-    As this is from the view of a renderer, it should not be used if in-browser
-    migration is enabled, as renderer will not be able to outlive even if any of
-    fetch keepalive request is still pending.
-  </summary>
-</histogram>
-
-<histogram name="FetchKeepAlive.Renderer.Duration.Failed" units="ms"
-    expires_after="2024-06-23">
-  <owner>mych@chromium.org</owner>
-  <owner>chrome-bfcache@google.com</owner>
-  <summary>
-    Recorded the time taken to complete a failed fetch keepalive request in a
-    renderer process.
-
-    This is the duration of a subset of requests logged by
-    `FetchKeepAlive.Renderer.Duration`. See that histogram for more details.
-  </summary>
-</histogram>
-
-<histogram name="FetchKeepAlive.Renderer.Duration.Succeeded" units="ms"
-    expires_after="2024-06-23">
-  <owner>mych@chromium.org</owner>
-  <owner>chrome-bfcache@google.com</owner>
-  <summary>
-    Recorded the time taken to complete a succeeded fetch keepalive request in a
-    renderer process.
-
-    This is the duration of a subset of requests logged by
-    `FetchKeepAlive.Renderer.Duration`. See that histogram for more details.
-  </summary>
-</histogram>
-
-<histogram name="FetchKeepAlive.Renderer.DurationAfterDetached" units="ms"
-    expires_after="2024-04-10">
-  <owner>mych@chromium.org</owner>
-  <owner>chrome-bfcache@google.com</owner>
-  <summary>
-    Recorded the time between a fetch context is &quot;detached&quot; and
-    completing a fetch keepalive request, no matter a request succeeds or fails.
-
-    As this is from the view of a renderer, it should not be used if in-browser
-    migration is enabled, as renderer will not be able to detach even if any of
-    fetch keepalive request is still pending.
-  </summary>
-</histogram>
-
-<histogram name="FetchKeepAlive.Renderer.DurationAfterDetached.Failed"
-    units="ms" expires_after="2024-04-10">
-  <owner>mych@chromium.org</owner>
-  <owner>chrome-bfcache@google.com</owner>
-  <summary>
-    Recorded the time between a fetch context is &quot;detached&quot; and
-    completing a failed fetch keepalive request.
-
-    This is the duration of a subset of requests logged by
-    `FetchKeepAlive.Renderer.DurationAfterDetached`. See that histogram for more
-    details.
-  </summary>
-</histogram>
-
-<histogram name="FetchKeepAlive.Renderer.DurationAfterDetached.Succeeded"
-    units="ms" expires_after="2024-04-10">
-  <owner>mych@chromium.org</owner>
-  <owner>chrome-bfcache@google.com</owner>
-  <summary>
-    Recorded the time between a fetch context is &quot;detached&quot; and
-    completing a succeeded fetch keepalive request.
-
-    This is the duration of a subset of requests logged by
-    `FetchKeepAlive.Renderer.DurationAfterDetached`. See that histogram for more
-    details.
-  </summary>
+  <token key="LoaderStatus">
+    <variant name=""
+        summary="been created. It does NOT equal to the total number of
+                 JavaScript Fetch keepalive call, as the latter can be
+                 immediately rejected with various JavaScript exceptions.
+                 This number should be compared with
+                 `FetchKeepAlive.Renderer.Total`."/>
+    <variant name=".Finished"
+        summary="finished loading. A finished loading can be either suceeded
+                 or failed. This number may be &lt;=
+                 `FetchKeepAlive.Browser.Total.Started`, as a request may be
+                 dropped for other undocumented reasons like browser killed
+                 or crashes. Also, this number is ~= `kLoadingSuceeded` +
+                 `kLoadingFailed` in `FetchKeepAlive.Browser.Metrics`."/>
+    <variant name=".ReceivedResponse"
+        summary="received a response. This number should be compared with
+                 `FetchKeepAlive.Renderer.Total.ReceivedResponse`."/>
+    <variant name=".Redirected"
+        summary="received a redirect. This number should be compared with
+                 `FetchKeepAlive.Renderer.Total.Redirected`."/>
+    <variant name=".Started"
+        summary="started to load. This number should be the same as
+                 `FetchKeepAlive.Browser.Total`, but logging to figure out if
+                 any differneces exist."/>
+  </token>
 </histogram>
 
 <histogram name="FetchKeepAlive.Renderer.Metrics"
-    enum="FetchKeepAliveRendererMetricType" expires_after="2024-04-10">
+    enum="FetchKeepAliveRendererMetricType" expires_after="2024-07-10">
   <owner>mych@chromium.org</owner>
   <owner>chrome-bfcache@google.com</owner>
   <summary>
@@ -5649,76 +5536,69 @@
   </summary>
 </histogram>
 
-<histogram name="FetchKeepAlive.Renderer.Total" enum="Boolean"
-    expires_after="2024-04-10">
+<histogram name="FetchKeepAlive.Renderer.Total{LoaderStatus}" enum="Boolean"
+    expires_after="2024-07-10">
   <owner>mych@chromium.org</owner>
   <owner>chrome-bfcache@google.com</owner>
   <summary>
-    Recorded true every time a new FetchLater request is created by a renderer.
-    It does NOT equal to the total number of JavaScript FetchLater API call, as
-    the latter can be immediately rejected with various JavaScript exceptions.
-
-    This metric is recorded to compare whether different behavior happens
-    between renderer and browser. This number should be compared with
-    `FetchKeepAlive.Browser.Total`.
+    Recorded true every time a Fetch keepalive request loader in a renderer
+    {LoaderStatus}
   </summary>
+  <token key="LoaderStatus">
+    <variant name=""
+        summary="is created. It does NOT equal to the total number of
+                 JavaScript Fetch API call, as the latter can be immediately
+                 rejected with various JavaScript exceptions. This metric is
+                 recorded to compare whether different behavior happens
+                 between renderer and browser. This number should be compared
+                 with `FetchKeepAlive.Browser.Total`."/>
+    <variant name=".Finished"
+        summary="is finished loading. A finished loading can be either
+                 suceeded or failed. This number may be &lt;=
+                 `FetchKeepAlive.Renderer.Total.Started`, as a request may be
+                 dropped for other undocumented reasons like browser killed
+                 or crashes. Also, this number is &lt;=
+                 `FetchKeepAlive.Browser.Total.Finished`."/>
+    <variant name=".ReceivedResponse"
+        summary="has received a response. This number should be compared with
+                 `FetchKeepAlive.Renderer.Total.ReceivedResponse`."/>
+    <variant name=".Redirected"
+        summary="has received a redirect. This number should be compared with
+                 `FetchKeepAlive.Browser.Total.Redirected`."/>
+    <variant name=".Started"
+        summary="is started to load. This number may be &lt;=
+                 `FetchKeepAlive.Renderer.Total`, as a created request may
+                 not be started at all."/>
+  </token>
 </histogram>
 
-<histogram name="FetchKeepAlive.Renderer.Total.Finished" enum="Boolean"
-    expires_after="2024-04-10">
+<histogram name="FetchKeepAlive.Renderer.{DurationType}{RequestStatus}"
+    units="ms" expires_after="2024-07-10">
   <owner>mych@chromium.org</owner>
   <owner>chrome-bfcache@google.com</owner>
-  <summary>
-    Recorded once every time a Fetch keepalive request loading is finished in a
-    renderer process. A finished loading can be either suceeded or failed.
-
-    This number may be &lt;= `FetchKeepAlive.Renderer.Total.Started`, as a
-    request may be dropped for other undocumented reasons like browser killed or
-    crashes. Also, this number is &lt;= `FetchKeepAlive.Browser.Total.Finished`.
-  </summary>
-</histogram>
-
-<histogram name="FetchKeepAlive.Renderer.Total.ReceivedResponse" enum="Boolean"
-    expires_after="2024-04-10">
-  <owner>mych@chromium.org</owner>
-  <owner>chrome-bfcache@google.com</owner>
-  <summary>
-    Recorded once every time a Fetch keepalive request loader in a renderer has
-    received a response.
-
-    This number should be compared with
-    `FetchKeepAlive.Renderer.Total.ReceivedResponse`.
-  </summary>
-</histogram>
-
-<histogram name="FetchKeepAlive.Renderer.Total.Redirected" enum="Boolean"
-    expires_after="2024-04-10">
-  <owner>mych@chromium.org</owner>
-  <owner>chrome-bfcache@google.com</owner>
-  <summary>
-    Recorded once every time a Fetch keepalive request loader in a renderer has
-    received a redirect.
-
-    This number should be compared with
-    `FetchKeepAlive.Browser.Total.Redirected`.
-  </summary>
-</histogram>
-
-<histogram name="FetchKeepAlive.Renderer.Total.Started" enum="Boolean"
-    expires_after="2024-04-10">
-  <owner>mych@chromium.org</owner>
-  <owner>chrome-bfcache@google.com</owner>
-  <summary>
-    Recorded once every time a created Fetch keepalive request is started in a
-    renderer process.
-
-    This number may be &lt;= `FetchKeepAlive.Renderer.Total`, as a created
-    request may not be started at all.
-  </summary>
+  <summary>Recorded the time {DurationType} {RequestStatus}</summary>
+  <token key="DurationType">
+    <variant name="Duration"
+        summary="taken to complete a fetch keepalive request in a renderer
+                 process"/>
+    <variant name="DurationAfterDetached"
+        summary="between a fetch context is &quot;detached&quot; and
+                 completing a fetch keepalive request"/>
+  </token>
+  <token key="RequestStatus">
+    <variant name=""
+        summary=", no matter a request succeeds or fails. As this is from the
+                 view of a renderer, it should not be used if in-browser
+                 migration is enabled, as renderer will not be able to
+                 outlive even if any of fetch keepalive request is still
+                 pending."/>
+    <variant name=".Failed" summary=", where the request has failed."/>
+    <variant name=".Succeeded" summary=", where the request has succeeded."/>
+  </token>
 </histogram>
 
 <histogram name="FetchLater.Browser.Metrics" enum="FetchLaterBrowserMetricType"
-    expires_after="2024-04-10">
+    expires_after="2024-07-10">
   <owner>mych@chromium.org</owner>
   <owner>chrome-bfcache@google.com</owner>
   <summary>
@@ -5727,36 +5607,30 @@
   </summary>
 </histogram>
 
-<histogram name="FetchLater.Browser.Total" enum="Boolean"
-    expires_after="2024-04-10">
+<histogram name="FetchLater.Browser.Total{LoaderStatus}" enum="Boolean"
+    expires_after="2024-07-10">
   <owner>mych@chromium.org</owner>
   <owner>chrome-bfcache@google.com</owner>
-  <summary>
-    Records true every time a new FetchLater request is scheduled by a browser.
-    It does NOT equal to the total number of JavaScript FetchLater API call, as
-    the latter can be immediately rejected with various JavaScript exceptions.
-
-    This number should be compared with `FetchLater.Renderer.Total`, and it may
-    be &gt;= the sum of `FetchLater.Browser.Metrics`, as a request may be
-    dropped for other undocumented reasons like browser killed or crashes.
-  </summary>
-</histogram>
-
-<histogram name="FetchLater.Browser.Total.Started" enum="Boolean"
-    expires_after="2024-04-10">
-  <owner>mych@chromium.org</owner>
-  <owner>chrome-bfcache@google.com</owner>
-  <summary>
-    Records once every time a scheduled FetchLater request is started in
-    browser.
-
-    This number should be the sum of enum values &quot;kStarted*&quot; in
-    `FetchLater.Browser.Metrics`.
-  </summary>
+  <summary>Records true every time {LoaderStatus}</summary>
+  <token key="LoaderStatus">
+    <variant name=""
+        summary="a new FetchLater request is scheduled by a browser. It does
+                 NOT equal to the total number of JavaScript FetchLater API
+                 call, as the latter can be immediately rejected with various
+                 JavaScript exceptions. This number should be compared with
+                 `FetchLater.Renderer.Total`, and it may be &gt;= the sum of
+                 `FetchLater.Browser.Metrics`, as a request may be dropped
+                 for other undocumented reasons like browser killed or
+                 crashes."/>
+    <variant name=".Started"
+        summary="a scheduled FetchLater request is started in browser. This
+                 number should be the sum of enum values
+                 &quot;kStarted*&quot; in `FetchLater.Browser.Metrics`."/>
+  </token>
 </histogram>
 
 <histogram name="FetchLater.Renderer.Metrics"
-    enum="FetchLaterRendererMetricType" expires_after="2024-04-10">
+    enum="FetchLaterRendererMetricType" expires_after="2024-07-10">
   <owner>mych@chromium.org</owner>
   <owner>chrome-bfcache@google.com</owner>
   <summary>
@@ -5766,7 +5640,7 @@
 </histogram>
 
 <histogram name="FetchLater.Renderer.Total" enum="Boolean"
-    expires_after="2024-04-10">
+    expires_after="2024-07-10">
   <owner>mych@chromium.org</owner>
   <owner>chrome-bfcache@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/service/histograms.xml b/tools/metrics/histograms/metadata/service/histograms.xml
index e404239c..483f9ef 100644
--- a/tools/metrics/histograms/metadata/service/histograms.xml
+++ b/tools/metrics/histograms/metadata/service/histograms.xml
@@ -913,8 +913,7 @@
 </histogram>
 
 <histogram
-    name="ServiceWorker.LoadTiming.Subresource.
-          AutoPreloadResponseReceivedToFetchHandlerEnd.{ServiceWorkerFetchResponseFromName}"
+    name="ServiceWorker.LoadTiming.Subresource.AutoPreloadResponseReceivedToFetchHandlerEnd.{ServiceWorkerFetchResponseFromName}"
     units="ms" expires_after="2024-06-01">
   <owner>sisidovski@chromium.org</owner>
   <owner>chrome-worker@google.com</owner>
diff --git a/tools/metrics/ukm/ukm.xml b/tools/metrics/ukm/ukm.xml
index 2c4a372..bc520c1 100644
--- a/tools/metrics/ukm/ukm.xml
+++ b/tools/metrics/ukm/ukm.xml
@@ -9056,27 +9056,62 @@
       then it is considered medium; if this is greater than 64 presented frames,
       then it is considered large.
     </summary>
+    <aggregation>
+      <history>
+        <statistics>
+          <quantiles type="std-percentiles"/>
+        </statistics>
+      </history>
+    </aggregation>
   </metric>
   <metric name="PredictorJankyFrameCount">
     <summary>
       The number of frames that are deemed janky to the human eye after Chrome
       has applied its scroll prediction algorithm..
     </summary>
+    <aggregation>
+      <history>
+        <statistics>
+          <quantiles type="std-percentiles"/>
+        </statistics>
+      </history>
+    </aggregation>
   </metric>
   <metric name="ScrollJank.DelayedFrameCount">
     <summary>
       The number of delayed frames.
     </summary>
+    <aggregation>
+      <history>
+        <statistics>
+          <quantiles type="std-percentiles"/>
+        </statistics>
+      </history>
+    </aggregation>
   </metric>
   <metric name="ScrollJank.MissedVsyncsMax">
     <summary>
       The maximum number of vsyncs missed during any and all janks.
     </summary>
+    <aggregation>
+      <history>
+        <statistics>
+          <quantiles type="std-percentiles"/>
+        </statistics>
+      </history>
+    </aggregation>
   </metric>
   <metric name="ScrollJank.MissedVsyncsSum">
     <summary>
       The total number of vsyncs missed during any and all janks that occurred.
     </summary>
+    <aggregation>
+      <history>
+        <statistics>
+          <quantiles type="std-percentiles"/>
+        </statistics>
+      </history>
+    </aggregation>
   </metric>
   <metric name="VsyncCount">
     <summary>
@@ -9085,6 +9120,13 @@
       greater than 16 and less than or equal to 64 vsyncs, then it is considered
       medium; if this is greater than 64 vsyncs, then is considered large.
     </summary>
+    <aggregation>
+      <history>
+        <statistics>
+          <quantiles type="std-percentiles"/>
+        </statistics>
+      </history>
+    </aggregation>
   </metric>
 </event>
 
@@ -9182,6 +9224,13 @@
       threshold, the scroll is considered fast. This also helps to determine
       what the threshold for jank should be.
     </summary>
+    <aggregation>
+      <history>
+        <statistics>
+          <quantiles type="std-percentiles"/>
+        </statistics>
+      </history>
+    </aggregation>
   </metric>
   <metric name="ScrollUpdate.MissedVsync.FrameAboveJankyThreshold2">
     <summary>
@@ -9189,6 +9238,13 @@
       frame relative to its neighboring frames in the sequence, if the previous
       vsync was missed.
     </summary>
+    <aggregation>
+      <history>
+        <statistics>
+          <quantiles type="std-percentiles"/>
+        </statistics>
+      </history>
+    </aggregation>
   </metric>
   <metric name="ScrollUpdate.NoMissedVsync.FrameAboveJankyThreshold2">
     <summary>
@@ -9196,6 +9252,13 @@
       frame relative to its neighboring frames in the sequence, if the previous
       vsync was not missed.
     </summary>
+    <aggregation>
+      <history>
+        <statistics>
+          <quantiles type="std-percentiles"/>
+        </statistics>
+      </history>
+    </aggregation>
   </metric>
 </event>
 
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index 6b1f9feb..1e517be 100644
--- a/tools/perf/core/perfetto_binary_roller/binary_deps.json
+++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -6,7 +6,7 @@
         },
         "win": {
             "hash": "4fac8adf0559ee13d48de4ef8c88f42b312e206d",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/28eadcf5efccc550c5317bdccaca0e64d9698f65/trace_processor_shell.exe"
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/d6af17fef257af28ee2417216ef87d5c5b743a1b/trace_processor_shell.exe"
         },
         "linux_arm": {
             "hash": "b1a376729d0b7aefc4c38db1348d94d068be07f1",
@@ -14,7 +14,7 @@
         },
         "mac": {
             "hash": "bdc6d1ad97086a8bd93a52aecfe4131c04f36215",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/mac/28eadcf5efccc550c5317bdccaca0e64d9698f65/trace_processor_shell"
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/mac/d6af17fef257af28ee2417216ef87d5c5b743a1b/trace_processor_shell"
         },
         "mac_arm64": {
             "hash": "8cd6239efb03863974fdc94e5ee004352f999e3a",
@@ -22,7 +22,7 @@
         },
         "linux": {
             "hash": "c442c4d1342bcaaccc2571494e0de072b9412cd5",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/28eadcf5efccc550c5317bdccaca0e64d9698f65/trace_processor_shell"
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/d6af17fef257af28ee2417216ef87d5c5b743a1b/trace_processor_shell"
         }
     },
     "power_profile.sql": {
diff --git a/ui/chromeos/styles/cros_sys_colors.json5 b/ui/chromeos/styles/cros_sys_colors.json5
index 5c59a7f..2323001 100644
--- a/ui/chromeos/styles/cros_sys_colors.json5
+++ b/ui/chromeos/styles/cros_sys_colors.json5
@@ -169,6 +169,14 @@
       light: 'rgba($cros.ref.error40.rgb, 0.30)',
       dark: 'rgba($cros.ref.error80.rgb, 0.30)',
     },
+    'inverse-error': {
+      light: '$cros.ref.error80',
+      dark:  '$cros.ref.error40',
+    },
+    'inverse-on-error': {
+      light: '$cros.ref.error20',
+      dark:  '$cros.ref.error100',
+    },
 
     /* Neutral */
     'surface-variant': {
diff --git a/ui/events/ash/BUILD.gn b/ui/events/ash/BUILD.gn
index 5e9a347..6cdef86 100644
--- a/ui/events/ash/BUILD.gn
+++ b/ui/events/ash/BUILD.gn
@@ -60,3 +60,15 @@
   ]
   testonly = true
 }
+
+source_set("test_support") {
+  sources = [
+    "fake_event_rewriter_ash_delegate.cc",
+    "fake_event_rewriter_ash_delegate.h",
+  ]
+  deps = [
+    ":ash",
+    "//base",
+  ]
+  testonly = true
+}
diff --git a/ui/events/ash/event_rewriter_ash.h b/ui/events/ash/event_rewriter_ash.h
index 24dcd7ee..7f2619c0 100644
--- a/ui/events/ash/event_rewriter_ash.h
+++ b/ui/events/ash/event_rewriter_ash.h
@@ -87,8 +87,8 @@
     virtual bool RewriteModifierKeys() = 0;
 
     // Suppresses all modifier key rewrites and makes |RewriteModifierKeys|
-    // always return false if |should_supress| is true.
-    virtual void SuppressModifierKeyRewrites(bool should_supress) = 0;
+    // always return false if |should_suppress| is true.
+    virtual void SuppressModifierKeyRewrites(bool should_suppress) = 0;
 
     // Returns whether or not Meta + Top Row Keys should be rewritten. Should
     // return correctly with respect to the values set in
diff --git a/ui/events/ash/fake_event_rewriter_ash_delegate.cc b/ui/events/ash/fake_event_rewriter_ash_delegate.cc
new file mode 100644
index 0000000..9afd2d9
--- /dev/null
+++ b/ui/events/ash/fake_event_rewriter_ash_delegate.cc
@@ -0,0 +1,110 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/events/ash/fake_event_rewriter_ash_delegate.h"
+
+#include "base/check_op.h"
+
+namespace ui::test {
+
+FakeEventRewriterAshDelegate::FakeEventRewriterAshDelegate() = default;
+FakeEventRewriterAshDelegate::~FakeEventRewriterAshDelegate() = default;
+
+void FakeEventRewriterAshDelegate::SetModifierRemapping(
+    std::string_view pref_name,
+    ui::mojom::ModifierKey value) {
+  DCHECK_NE(ui::mojom::ModifierKey::kIsoLevel5ShiftMod3, value);
+  modifier_remapping_.insert_or_assign(std::string(pref_name), value);
+}
+
+bool FakeEventRewriterAshDelegate::RewriteModifierKeys() {
+  return !suppress_modifier_key_rewrites_;
+}
+
+void FakeEventRewriterAshDelegate::SuppressModifierKeyRewrites(
+    bool should_suppress) {
+  suppress_modifier_key_rewrites_ = should_suppress;
+}
+
+bool FakeEventRewriterAshDelegate::RewriteMetaTopRowKeyComboEvents(
+    int device_id) const {
+  return true;
+}
+
+void FakeEventRewriterAshDelegate::SuppressMetaTopRowKeyComboRewrites(
+    bool should_suppress) {}
+
+std::optional<mojom::ModifierKey>
+FakeEventRewriterAshDelegate::GetKeyboardRemappedModifierValue(
+    int device_id,
+    ui::mojom::ModifierKey modifier_key,
+    const std::string& pref_name) const {
+  auto it = modifier_remapping_.find(pref_name);
+  if (it == modifier_remapping_.end()) {
+    return std::nullopt;
+  }
+  return it->second;
+}
+
+bool FakeEventRewriterAshDelegate::TopRowKeysAreFunctionKeys(
+    int device_id) const {
+  return false;
+}
+
+bool FakeEventRewriterAshDelegate::IsExtensionCommandRegistered(
+    KeyboardCode key_code,
+    int flags) const {
+  return false;
+}
+
+bool FakeEventRewriterAshDelegate::IsSearchKeyAcceleratorReserved() const {
+  return false;
+}
+
+bool FakeEventRewriterAshDelegate::NotifyDeprecatedRightClickRewrite() {
+  return false;
+}
+
+bool FakeEventRewriterAshDelegate::NotifyDeprecatedSixPackKeyRewrite(
+    KeyboardCode key_code) {
+  return false;
+}
+
+void FakeEventRewriterAshDelegate::RecordEventRemappedToRightClick(
+    bool alt_based_right_click) {}
+
+void FakeEventRewriterAshDelegate::RecordSixPackEventRewrite(
+    KeyboardCode key_code,
+    bool alt_based) {}
+
+std::optional<ui::mojom::SimulateRightClickModifier>
+FakeEventRewriterAshDelegate::GetRemapRightClickModifier(int device_id) {
+  return std::nullopt;
+}
+
+std::optional<ui::mojom::SixPackShortcutModifier>
+FakeEventRewriterAshDelegate::GetShortcutModifierForSixPackKey(
+    int device_id,
+    ui::KeyboardCode key_code) {
+  return std::nullopt;
+}
+
+void FakeEventRewriterAshDelegate::NotifyRightClickRewriteBlockedBySetting(
+    ui::mojom::SimulateRightClickModifier blocked_modifier,
+    ui::mojom::SimulateRightClickModifier active_modifier) {}
+
+void FakeEventRewriterAshDelegate::NotifySixPackRewriteBlockedBySetting(
+    ui::KeyboardCode key_code,
+    ui::mojom::SixPackShortcutModifier blocked_modifier,
+    ui::mojom::SixPackShortcutModifier active_modifier,
+    int device_id) {}
+
+std::optional<ui::mojom::ExtendedFkeysModifier>
+FakeEventRewriterAshDelegate::GetExtendedFkeySetting(
+    int device_id,
+    ui::KeyboardCode key_code) {
+  return std::nullopt;
+}
+
+}  // namespace ui::test
diff --git a/ui/events/ash/fake_event_rewriter_ash_delegate.h b/ui/events/ash/fake_event_rewriter_ash_delegate.h
new file mode 100644
index 0000000..a140e255
--- /dev/null
+++ b/ui/events/ash/fake_event_rewriter_ash_delegate.h
@@ -0,0 +1,71 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_EVENTS_ASH_FAKE_EVENT_REWRITER_ASH_DELEGATE_H_
+#define UI_EVENTS_ASH_FAKE_EVENT_REWRITER_ASH_DELEGATE_H_
+
+#include <map>
+#include <string>
+#include <string_view>
+
+#include "ui/events/ash/event_rewriter_ash.h"
+
+namespace ui::test {
+
+// Fake implementation of EventRewriterAsh::Delegate to be used by tests.
+class FakeEventRewriterAshDelegate : public ui::EventRewriterAsh::Delegate {
+ public:
+  FakeEventRewriterAshDelegate();
+  FakeEventRewriterAshDelegate(const FakeEventRewriterAshDelegate&) = delete;
+  FakeEventRewriterAshDelegate& operator=(const FakeEventRewriterAshDelegate&) =
+      delete;
+  ~FakeEventRewriterAshDelegate() override;
+
+  // Configures the modifier remapping as specified.
+  void SetModifierRemapping(std::string_view pref_name,
+                            ui::mojom::ModifierKey value);
+
+  // ui::EventRewriterAsh::Delegate:
+  bool RewriteModifierKeys() override;
+  void SuppressModifierKeyRewrites(bool should_suppress) override;
+  bool RewriteMetaTopRowKeyComboEvents(int device_id) const override;
+  void SuppressMetaTopRowKeyComboRewrites(bool should_suppress) override;
+  std::optional<ui::mojom::ModifierKey> GetKeyboardRemappedModifierValue(
+      int device_id,
+      ui::mojom::ModifierKey modifier_key,
+      const std::string& pref_name) const override;
+  bool TopRowKeysAreFunctionKeys(int device_id) const override;
+  bool IsExtensionCommandRegistered(KeyboardCode key_code,
+                                    int flags) const override;
+  bool IsSearchKeyAcceleratorReserved() const override;
+  bool NotifyDeprecatedRightClickRewrite() override;
+  bool NotifyDeprecatedSixPackKeyRewrite(KeyboardCode key_code) override;
+  void RecordEventRemappedToRightClick(bool alt_based_right_click) override;
+  void RecordSixPackEventRewrite(KeyboardCode key_code,
+                                 bool alt_based) override;
+  std::optional<ui::mojom::SimulateRightClickModifier>
+  GetRemapRightClickModifier(int device_id) override;
+  std::optional<ui::mojom::SixPackShortcutModifier>
+  GetShortcutModifierForSixPackKey(int device_id,
+                                   ui::KeyboardCode key_code) override;
+  void NotifyRightClickRewriteBlockedBySetting(
+      ui::mojom::SimulateRightClickModifier blocked_modifier,
+      ui::mojom::SimulateRightClickModifier active_modifier) override;
+  void NotifySixPackRewriteBlockedBySetting(
+      ui::KeyboardCode key_code,
+      ui::mojom::SixPackShortcutModifier blocked_modifier,
+      ui::mojom::SixPackShortcutModifier active_modifier,
+      int device_id) override;
+  std::optional<ui::mojom::ExtendedFkeysModifier> GetExtendedFkeySetting(
+      int device_id,
+      ui::KeyboardCode key_code) override;
+
+ private:
+  bool suppress_modifier_key_rewrites_ = false;
+  std::map<std::string, ui::mojom::ModifierKey> modifier_remapping_;
+};
+
+}  // namespace ui::test
+
+#endif  // UI_EVENTS_ASH_FAKE_EVENT_REWRITER_ASH_DELEGATE_H_
diff --git a/ui/ozone/platform/drm/gpu/drm_gpu_util.cc b/ui/ozone/platform/drm/gpu/drm_gpu_util.cc
index df95698..d795e531 100644
--- a/ui/ozone/platform/drm/gpu/drm_gpu_util.cc
+++ b/ui/ozone/platform/drm/gpu/drm_gpu_util.cc
@@ -55,6 +55,10 @@
 ScopedDrmColorLutPtr CreateLutBlob(const display::GammaCurve& source,
                                    size_t size) {
   TRACE_EVENT0("drm", "CreateLutBlob");
+  if (source.IsDefaultIdentity()) {
+    return nullptr;
+  }
+
   ScopedDrmColorLutPtr lut(
       static_cast<drm_color_lut*>(malloc(sizeof(drm_color_lut) * size)));
   drm_color_lut* p = lut.get();
@@ -67,29 +71,18 @@
 }
 
 ScopedDrmColorCtmPtr CreateCTMBlob(const std::vector<float>& color_matrix) {
-  skcms_Matrix3x3 m = {{
-      {1.0f, 0.0f, 0.0f},
-      {0.0f, 1.0f, 0.0f},
-      {0.0f, 0.0f, 1.0f},
-  }};
-  if (color_matrix.size() == 9) {
-    for (size_t i = 0; i < 9; ++i) {
-      m.vals[i / 3][i % 3] = color_matrix[i];
-    }
-  }
-  return CreateCTMBlob(m);
-}
+  if (color_matrix.empty())
+    return nullptr;
 
-ScopedDrmColorCtmPtr CreateCTMBlob(const skcms_Matrix3x3& color_matrix) {
   ScopedDrmColorCtmPtr ctm(
       static_cast<drm_color_ctm*>(malloc(sizeof(drm_color_ctm))));
-  for (size_t i = 0; i < 9; ++i) {
-    float value = color_matrix.vals[i / 3][i % 3];
-    if (value < 0) {
-      ctm->matrix[i] = static_cast<uint64_t>(-value * (1ull << 32));
+  DCHECK_EQ(color_matrix.size(), std::size(ctm->matrix));
+  for (size_t i = 0; i < std::size(ctm->matrix); ++i) {
+    if (color_matrix[i] < 0) {
+      ctm->matrix[i] = static_cast<uint64_t>(-color_matrix[i] * (1ull << 32));
       ctm->matrix[i] |= static_cast<uint64_t>(1) << 63;
     } else {
-      ctm->matrix[i] = static_cast<uint64_t>(value * (1ull << 32));
+      ctm->matrix[i] = static_cast<uint64_t>(color_matrix[i] * (1ull << 32));
     }
   }
   return ctm;
diff --git a/ui/ozone/platform/drm/gpu/drm_gpu_util.h b/ui/ozone/platform/drm/gpu/drm_gpu_util.h
index 8737067..4ee764f 100644
--- a/ui/ozone/platform/drm/gpu/drm_gpu_util.h
+++ b/ui/ozone/platform/drm/gpu/drm_gpu_util.h
@@ -11,8 +11,6 @@
 #include "ui/ozone/platform/drm/common/drm_wrapper.h"
 #include "ui/ozone/platform/drm/common/scoped_drm_types.h"
 
-struct skcms_Matrix3x3;
-
 namespace ui {
 
 // Helper function that finds the property with the specified name.
@@ -34,7 +32,6 @@
 // significant bit is the sign.
 // |color_matrix| represents a 3x3 matrix in vector form.
 ScopedDrmColorCtmPtr CreateCTMBlob(const std::vector<float>& color_matrix);
-ScopedDrmColorCtmPtr CreateCTMBlob(const skcms_Matrix3x3& color_matrix);
 
 // Creates a FB Damage Clip Blob
 ScopedDrmModeRectPtr CreateDCBlob(const gfx::Rect& rect);
diff --git a/ui/ozone/platform/drm/gpu/hardware_display_plane_manager.cc b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager.cc
index 6c68eb6..9f4bbf66 100644
--- a/ui/ozone/platform/drm/gpu/hardware_display_plane_manager.cc
+++ b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager.cc
@@ -331,6 +331,13 @@
 bool HardwareDisplayPlaneManager::SetColorMatrix(
     uint32_t crtc_id,
     const std::vector<float>& color_matrix) {
+  if (color_matrix.empty()) {
+    // TODO: Consider allowing an empty matrix to disable the color transform
+    // matrix.
+    LOG(ERROR) << "CTM is empty. Expected a 3x3 matrix.";
+    return false;
+  }
+
   const auto crtc_index = LookupCrtcIndex(crtc_id);
   DCHECK(crtc_index.has_value());
   CrtcState* crtc_state = &crtc_state_[*crtc_index];
diff --git a/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_unittest.cc b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_unittest.cc
index 2fdfd8b4..f051a7a 100644
--- a/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_unittest.cc
+++ b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_unittest.cc
@@ -1033,22 +1033,22 @@
   }
 }
 
-TEST_P(HardwareDisplayPlaneManagerTest, SetColorMatrix_AllowEmptyCtm) {
+TEST_P(HardwareDisplayPlaneManagerTest, SetColorMatrix_ErrorEmptyCtm) {
   auto drm_state = MockDrmDevice::MockDrmState::CreateStateWithDefaultObjects(
       /*crtc_count=*/1, /*planes_per_crtc=*/1);
   drm_state.crtc_properties[0].properties.push_back(
       {.id = kCtmPropId, .value = 0});
   fake_drm_->InitializeState(drm_state, use_atomic_);
 
-  EXPECT_TRUE(fake_drm_->plane_manager()->SetColorMatrix(
+  EXPECT_FALSE(fake_drm_->plane_manager()->SetColorMatrix(
       fake_drm_->crtc_property(0).id, {}));
   if (use_atomic_) {
     HardwareDisplayPlaneList state;
     PerformPageFlip(/*crtc_idx=*/0, &state);
-    EXPECT_EQ(2, fake_drm_->get_commit_count());
-    EXPECT_NE(0u, GetCrtcPropertyValue(fake_drm_->crtc_property(0).id, "CTM"));
+    EXPECT_EQ(1, fake_drm_->get_commit_count());
+    EXPECT_EQ(0u, GetCrtcPropertyValue(fake_drm_->crtc_property(0).id, "CTM"));
   } else {
-    EXPECT_EQ(1, fake_drm_->get_set_object_property_count());
+    EXPECT_EQ(0, fake_drm_->get_set_object_property_count());
   }
 }
 
@@ -1169,9 +1169,9 @@
   if (use_atomic_) {
     PerformPageFlip(/*crtc_idx=*/0, &state);
     EXPECT_EQ(2, fake_drm_->get_commit_count());
-    EXPECT_NE(
+    EXPECT_EQ(
         0u, GetCrtcPropertyValue(fake_drm_->crtc_property(0).id, "GAMMA_LUT"));
-    EXPECT_NE(0u, GetCrtcPropertyValue(fake_drm_->crtc_property(0).id,
+    EXPECT_EQ(0u, GetCrtcPropertyValue(fake_drm_->crtc_property(0).id,
                                        "DEGAMMA_LUT"));
   } else {
     EXPECT_EQ(2, fake_drm_->get_set_object_property_count());
diff --git a/ui/webui/webui_allowlist_provider.cc b/ui/webui/webui_allowlist_provider.cc
index 55f0e09..72c7e3f 100644
--- a/ui/webui/webui_allowlist_provider.cc
+++ b/ui/webui/webui_allowlist_provider.cc
@@ -37,7 +37,8 @@
     const ContentSettingsPattern& primary_pattern,
     const ContentSettingsPattern& secondary_pattern,
     ContentSettingsType content_type) {
-  NotifyObservers(primary_pattern, secondary_pattern, content_type);
+  NotifyObservers(primary_pattern, secondary_pattern, content_type,
+                  /*partition_key=*/nullptr);
 }
 
 bool WebUIAllowlistProvider::SetWebsiteSetting(