diff --git a/DEPS b/DEPS
index 61060ee..af0bf08 100644
--- a/DEPS
+++ b/DEPS
@@ -304,7 +304,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': '0e0aa7e82d3a476d6e983b1fc383186082e948ad',
+  'skia_revision': '5fa5d5958628088f44a784fa5f69a27ec60488d9',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
@@ -312,7 +312,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': '34a06e25df2a1625bad36ab565319dace7cc5c54',
+  'angle_revision': '997c4c7b3d6bf1d0bc1e92267ae50d372f76a502',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -331,7 +331,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Fuchsia sdk
   # and whatever else without interference from each other.
-  'fuchsia_version': 'version:12.20230323.1.1',
+  'fuchsia_version': 'version:12.20230323.3.1',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling google-toolbox-for-mac
   # and whatever else without interference from each other.
@@ -455,7 +455,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling nearby
   # and whatever else without interference from each other.
-  'nearby_revision': '39df20d821ba6988fa3e2aa0ae42eadf4298c617',
+  'nearby_revision': '3f1b0c81e3c441f89b94843a60461677972f163e',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling securemessage
   # and whatever else without interference from each other.
@@ -495,7 +495,7 @@
 
   # If you change this, also update the libc++ revision in
   # //buildtools/deps_revisions.gni.
-  'libcxx_revision':       'e44019bfac2b2d3ebe1618628884f85c8600e322',
+  'libcxx_revision':       '5622befaf8a9d539bc94c9f1341b8e76065334db',
 
   # GN CIPD package version.
   'gn_version': 'git_revision:41fef642de70ecdcaaa26be96d56a0398f95abd4',
@@ -790,7 +790,7 @@
 
   'src/clank': {
     'url': Var('chrome_git') + '/clank/internal/apps.git' + '@' +
-    '52e08fba3410cf3b5d3d18734052bc45bc42f28c',
+    'e4e0d6c5312163f362c568a5b7dd89e23a612bf7',
     'condition': 'checkout_android and checkout_src_internal',
   },
 
@@ -819,7 +819,7 @@
   },
 
   'src/ios/third_party/material_components_ios/src': {
-      'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + 'c9eba0ffc740ea68f455e39c19e0c0230282c7cb',
+      'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + 'ba1e10d39bb1fa6086410c0a86d6b06604d082f5',
       'condition': 'checkout_ios',
   },
 
@@ -979,7 +979,7 @@
     'packages': [
       {
           'package': 'chromium/third_party/androidx',
-          'version': 'Bd5GuYdu-KY7DAsP-QU0XofyaFYU7ySZX8QQpca7j3sC',
+          'version': 'NiTVtksLm4vhVv9RrW0oOiw5xdUpMuEuyGmD1_YR5kkC',
       },
     ],
     'condition': 'checkout_android',
@@ -1230,7 +1230,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '479e1e9055020c8d1351bf2194d0a606aeca93d5',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'c799f76f30dec736e37da32da2395cb29ed4ede4',
 
   'src/third_party/devtools-frontend/src':
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
@@ -1702,7 +1702,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + '134b1104235eedbaa47555f5c27fa8d0b6c0c7c3',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + 'bfdb3fc5cc0fc2ea9324caef920ceb30c1e3d9f5',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1884,10 +1884,10 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + 'd1b65aa5a88f6efd900604dfcda840154e9f16e2',
 
   'src/third_party/webgpu-cts/src':
-    Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + 'b9f099aa09dfb96e4f9851c05cf900cc9e13a4f5',
+    Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '07ce6fa80556ad9ea530f68bd82ffcafede40728',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '31a10d321a6cf300da27610bd30469982b8e51fd',
+    Var('webrtc_git') + '/src.git' + '@' + 'd3e765e4eb4af8eba0c63b6bfe2e17a12dc670dc',
 
   # 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.
@@ -1964,7 +1964,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': Var('chrome_git') + '/chrome/src-internal.git@c6737ebfc10f0a0896b17cfd173c7df2c6c3f66f',
+    'url': Var('chrome_git') + '/chrome/src-internal.git@9f5a9e63630393ca7e0e10fcca591a9874348387',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/ash/accelerators/ash_accelerator_configuration.cc b/ash/accelerators/ash_accelerator_configuration.cc
index 5df2199..e0d975c2 100644
--- a/ash/accelerators/ash_accelerator_configuration.cc
+++ b/ash/accelerators/ash_accelerator_configuration.cc
@@ -11,6 +11,7 @@
 #include "ash/constants/ash_features.h"
 #include "ash/public/cpp/accelerators.h"
 #include "ash/public/cpp/accelerators_util.h"
+#include "ash/public/mojom/accelerator_configuration.mojom-shared.h"
 #include "ash/public/mojom/accelerator_configuration.mojom.h"
 #include "ash/public/mojom/accelerator_info.mojom.h"
 #include "base/containers/contains.h"
@@ -163,7 +164,19 @@
 AcceleratorConfigResult AshAcceleratorConfiguration::AddUserAccelerator(
     AcceleratorActionId action_id,
     const ui::Accelerator& accelerator) {
-  return AcceleratorConfigResult::kActionLocked;
+  CHECK(::features::IsShortcutCustomizationEnabled());
+  const AcceleratorConfigResult result =
+      DoAddAccelerator(action_id, accelerator);
+
+  if (result == AcceleratorConfigResult::kSuccess) {
+    UpdateAndNotifyAccelerators();
+  }
+
+  VLOG(1) << "AddAccelerator called for ActionID: " << action_id
+          << ", Accelerator: " << accelerator.GetShortcutText()
+          << " returned: " << static_cast<int>(result);
+
+  return result;
 }
 
 AcceleratorConfigResult AshAcceleratorConfiguration::RemoveAccelerator(
@@ -358,6 +371,37 @@
   return AcceleratorConfigResult::kSuccess;
 }
 
+AcceleratorConfigResult AshAcceleratorConfiguration::DoAddAccelerator(
+    AcceleratorActionId action_id,
+    const ui::Accelerator& accelerator) {
+  CHECK(::features::IsShortcutCustomizationEnabled());
+
+  const auto& accelerators_iter = id_to_accelerators_.find(action_id);
+  if (accelerators_iter == id_to_accelerators_.end()) {
+    return AcceleratorConfigResult::kNotFound;
+  }
+
+  // Check if `accelerator` is already used, in-use or deprecated. If so
+  // remove/disable it.
+  const auto* conflict_action_id = FindAcceleratorAction(accelerator);
+  if (conflict_action_id) {
+    const AcceleratorConfigResult remove_result =
+        DoRemoveAccelerator(*conflict_action_id, accelerator);
+    if (remove_result != AcceleratorConfigResult::kSuccess) {
+      return remove_result;
+    }
+  }
+
+  // Add the accelerator.
+  auto& accelerators = accelerators_iter->second;
+  accelerators.push_back(accelerator);
+  accelerator_to_id_.InsertNew(
+      {accelerator, static_cast<AcceleratorAction>(action_id)});
+
+  // TODO(jimmyxgong): Update prefs to match updated state.
+  return AcceleratorConfigResult::kSuccess;
+}
+
 const DeprecatedAcceleratorData*
 AshAcceleratorConfiguration::GetDeprecatedAcceleratorData(
     AcceleratorActionId action) {
diff --git a/ash/accelerators/ash_accelerator_configuration.h b/ash/accelerators/ash_accelerator_configuration.h
index d431a6a..b0bbdd2 100644
--- a/ash/accelerators/ash_accelerator_configuration.h
+++ b/ash/accelerators/ash_accelerator_configuration.h
@@ -12,6 +12,7 @@
 #include "ash/ash_export.h"
 #include "ash/public/cpp/accelerator_configuration.h"
 #include "ash/public/cpp/accelerators.h"
+#include "ash/public/mojom/accelerator_configuration.mojom-shared.h"
 #include "ash/public/mojom/accelerator_configuration.mojom.h"
 #include "ash/public/mojom/accelerator_info.mojom.h"
 #include "base/containers/flat_set.h"
@@ -118,6 +119,11 @@
       AcceleratorActionId action_id,
       const ui::Accelerator& accelerator);
 
+  // Adds the accelerator, does not notify observers.
+  mojom::AcceleratorConfigResult DoAddAccelerator(
+      AcceleratorActionId action_id,
+      const ui::Accelerator& accelerator);
+
   void NotifyAcceleratorsUpdated();
 
   void UpdateAndNotifyAccelerators();
diff --git a/ash/accelerators/ash_accelerator_configuration_unittest.cc b/ash/accelerators/ash_accelerator_configuration_unittest.cc
index 22c18dc..f475413a 100644
--- a/ash/accelerators/ash_accelerator_configuration_unittest.cc
+++ b/ash/accelerators/ash_accelerator_configuration_unittest.cc
@@ -741,4 +741,351 @@
   EXPECT_EQ(1, observer_.num_times_accelerator_updated_called());
 }
 
+// Add accelerator with no conflict.
+TEST_F(AshAcceleratorConfigurationTest, AddAccelerator) {
+  EXPECT_EQ(0, observer_.num_times_accelerator_updated_called());
+  const AcceleratorData test_data[] = {
+      {/*trigger_on_press=*/true, ui::VKEY_SPACE,
+       ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN, SWITCH_TO_LAST_USED_IME},
+      {/*trigger_on_press=*/true, ui::VKEY_TAB, ui::EF_ALT_DOWN,
+       CYCLE_FORWARD_MRU},
+      {/*trigger_on_press=*/true, ui::VKEY_TAB,
+       ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN, CYCLE_BACKWARD_MRU},
+  };
+
+  config_->Initialize(test_data);
+
+  ExpectAllAcceleratorsEqual(test_data, config_->GetAllAccelerators());
+  EXPECT_EQ(1, observer_.num_times_accelerator_updated_called());
+
+  // Add CTRL + SPACE to SWITCH_TO_LAST_USED_IME.
+  const AcceleratorData updated_test_data[] = {
+      {/*trigger_on_press=*/true, ui::VKEY_SPACE, ui::EF_CONTROL_DOWN,
+       SWITCH_TO_LAST_USED_IME},
+      {/*trigger_on_press=*/true, ui::VKEY_SPACE,
+       ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN, SWITCH_TO_LAST_USED_IME},
+      {/*trigger_on_press=*/true, ui::VKEY_TAB, ui::EF_ALT_DOWN,
+       CYCLE_FORWARD_MRU},
+      {/*trigger_on_press=*/true, ui::VKEY_TAB,
+       ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN, CYCLE_BACKWARD_MRU},
+  };
+
+  const ui::Accelerator new_accelerator(ui::VKEY_SPACE, ui::EF_CONTROL_DOWN);
+  AcceleratorConfigResult result =
+      config_->AddUserAccelerator(SWITCH_TO_LAST_USED_IME, new_accelerator);
+  EXPECT_EQ(AcceleratorConfigResult::kSuccess, result);
+
+  // Compare expected accelerators and that the observer was fired after
+  // adding an accelerator.
+  EXPECT_EQ(2, observer_.num_times_accelerator_updated_called());
+  ExpectAllAcceleratorsEqual(updated_test_data, config_->GetAllAccelerators());
+  const AcceleratorAction* found_action =
+      config_->FindAcceleratorAction(new_accelerator);
+  EXPECT_TRUE(found_action);
+  EXPECT_EQ(SWITCH_TO_LAST_USED_IME, *found_action);
+}
+
+// Add accelerator that conflict with default accelerator.
+TEST_F(AshAcceleratorConfigurationTest, AddAcceleratorDefaultConflict) {
+  EXPECT_EQ(0, observer_.num_times_accelerator_updated_called());
+  const AcceleratorData test_data[] = {
+      {/*trigger_on_press=*/true, ui::VKEY_SPACE,
+       ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN, SWITCH_TO_LAST_USED_IME},
+      {/*trigger_on_press=*/true, ui::VKEY_TAB, ui::EF_ALT_DOWN,
+       CYCLE_FORWARD_MRU},
+      {/*trigger_on_press=*/true, ui::VKEY_TAB,
+       ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN, CYCLE_BACKWARD_MRU},
+  };
+
+  config_->Initialize(test_data);
+
+  ExpectAllAcceleratorsEqual(test_data, config_->GetAllAccelerators());
+  EXPECT_EQ(1, observer_.num_times_accelerator_updated_called());
+
+  // Add ALT + SHIFT + TAB to SWITCH_TO_LAST_USED_IME, which conflicts with
+  // CYCLE_BACKWARD_MRU.
+  const AcceleratorData updated_test_data[] = {
+      {/*trigger_on_press=*/true, ui::VKEY_TAB,
+       ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN, SWITCH_TO_LAST_USED_IME},
+      {/*trigger_on_press=*/true, ui::VKEY_SPACE,
+       ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN, SWITCH_TO_LAST_USED_IME},
+      {/*trigger_on_press=*/true, ui::VKEY_TAB, ui::EF_ALT_DOWN,
+       CYCLE_FORWARD_MRU},
+  };
+
+  const ui::Accelerator new_accelerator(ui::VKEY_TAB,
+                                        ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN);
+  AcceleratorConfigResult result =
+      config_->AddUserAccelerator(SWITCH_TO_LAST_USED_IME, new_accelerator);
+  EXPECT_EQ(AcceleratorConfigResult::kSuccess, result);
+
+  // Compare expected accelerators and that the observer was fired after
+  // removing an accelerator.
+  EXPECT_EQ(2, observer_.num_times_accelerator_updated_called());
+  ExpectAllAcceleratorsEqual(updated_test_data, config_->GetAllAccelerators());
+  const AcceleratorAction* found_action =
+      config_->FindAcceleratorAction(new_accelerator);
+  EXPECT_TRUE(found_action);
+  EXPECT_EQ(SWITCH_TO_LAST_USED_IME, *found_action);
+
+  // Confirm that conflicting accelerator was removed.
+  const std::vector<ui::Accelerator>& backward_mru_accelerators =
+      config_->GetAcceleratorsForAction(CYCLE_BACKWARD_MRU);
+  EXPECT_TRUE(backward_mru_accelerators.empty());
+}
+
+// Add accelerator that conflicts with a deprecated accelerator.
+TEST_F(AshAcceleratorConfigurationTest, AddAcceleratorDeprecatedConflict) {
+  EXPECT_EQ(0, observer_.num_times_accelerator_updated_called());
+  const AcceleratorData test_data[] = {
+      {/*trigger_on_press=*/true, ui::VKEY_SPACE,
+       ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN, SWITCH_TO_LAST_USED_IME},
+      {/*trigger_on_press=*/true, ui::VKEY_TAB, ui::EF_ALT_DOWN,
+       CYCLE_FORWARD_MRU},
+      {/*trigger_on_press=*/true, ui::VKEY_TAB,
+       ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN, CYCLE_BACKWARD_MRU},
+  };
+
+  const DeprecatedAcceleratorData deprecated_data[] = {
+      {SHOW_TASK_MANAGER, /*uma_histogram_name=*/"deprecated.showTaskManager",
+       /*notification_message_id=*/1, /*old_shortcut_id=*/1,
+       /*new_shortcut_id=*/2, /*deprecated_enabled=*/true},
+  };
+
+  const AcceleratorData test_deprecated_accelerators[] = {
+      {/*trigger_on_press=*/true, ui::VKEY_ESCAPE, ui::EF_SHIFT_DOWN,
+       SHOW_TASK_MANAGER},
+  };
+
+  const AcceleratorData initial_expected_data[] = {
+      {/*trigger_on_press=*/true, ui::VKEY_SPACE,
+       ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN, SWITCH_TO_LAST_USED_IME},
+      {/*trigger_on_press=*/true, ui::VKEY_TAB, ui::EF_ALT_DOWN,
+       CYCLE_FORWARD_MRU},
+      {/*trigger_on_press=*/true, ui::VKEY_TAB,
+       ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN, CYCLE_BACKWARD_MRU},
+      {/*trigger_on_press=*/true, ui::VKEY_ESCAPE, ui::EF_SHIFT_DOWN,
+       SHOW_TASK_MANAGER},
+  };
+
+  config_->Initialize(test_data);
+  config_->InitializeDeprecatedAccelerators(deprecated_data,
+                                            test_deprecated_accelerators);
+
+  ExpectAllAcceleratorsEqual(initial_expected_data,
+                             config_->GetAllAccelerators());
+  // Initializing deprecated accelerators will also trigger the observer.
+  EXPECT_EQ(2, observer_.num_times_accelerator_updated_called());
+
+  const ui::Accelerator deprecated_accelerator(ui::VKEY_ESCAPE,
+                                               ui::EF_SHIFT_DOWN);
+  EXPECT_TRUE(config_->IsDeprecated(deprecated_accelerator));
+
+  // Add SHIFT + ESCAPE to SWITCH_TO_LAST_USED_IME, which conflicts with
+  // a deprecated accelerator.
+  const AcceleratorData updated_test_data[] = {
+      {/*trigger_on_press=*/true, ui::VKEY_SPACE,
+       ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN, SWITCH_TO_LAST_USED_IME},
+      {/*trigger_on_press=*/true, ui::VKEY_ESCAPE, ui::EF_SHIFT_DOWN,
+       SWITCH_TO_LAST_USED_IME},
+      {/*trigger_on_press=*/true, ui::VKEY_TAB, ui::EF_ALT_DOWN,
+       CYCLE_FORWARD_MRU},
+      {/*trigger_on_press=*/true, ui::VKEY_TAB,
+       ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN, CYCLE_BACKWARD_MRU},
+  };
+
+  AcceleratorConfigResult result = config_->AddUserAccelerator(
+      SWITCH_TO_LAST_USED_IME, deprecated_accelerator);
+  EXPECT_EQ(AcceleratorConfigResult::kSuccess, result);
+
+  // Compare expected accelerators and that the observer was fired after
+  // removing an accelerator.
+  EXPECT_EQ(3, observer_.num_times_accelerator_updated_called());
+  ExpectAllAcceleratorsEqual(updated_test_data, config_->GetAllAccelerators());
+  const AcceleratorAction* found_action =
+      config_->FindAcceleratorAction(deprecated_accelerator);
+  EXPECT_TRUE(found_action);
+  EXPECT_EQ(SWITCH_TO_LAST_USED_IME, *found_action);
+
+  // Confirm that the deprecated accelerator was removed.
+  EXPECT_FALSE(config_->IsDeprecated(deprecated_accelerator));
+}
+
+// Add and then remove an accelerator.
+TEST_F(AshAcceleratorConfigurationTest, AddRemoveAccelerator) {
+  EXPECT_EQ(0, observer_.num_times_accelerator_updated_called());
+  const AcceleratorData test_data[] = {
+      {/*trigger_on_press=*/true, ui::VKEY_SPACE,
+       ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN, SWITCH_TO_LAST_USED_IME},
+      {/*trigger_on_press=*/true, ui::VKEY_TAB, ui::EF_ALT_DOWN,
+       CYCLE_FORWARD_MRU},
+      {/*trigger_on_press=*/true, ui::VKEY_TAB,
+       ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN, CYCLE_BACKWARD_MRU},
+  };
+
+  config_->Initialize(test_data);
+
+  ExpectAllAcceleratorsEqual(test_data, config_->GetAllAccelerators());
+  EXPECT_EQ(1, observer_.num_times_accelerator_updated_called());
+
+  // Add CTRL + SPACE to SWITCH_TO_LAST_USED_IME.
+  const AcceleratorData added_updated_test_data[] = {
+      {/*trigger_on_press=*/true, ui::VKEY_SPACE, ui::EF_CONTROL_DOWN,
+       SWITCH_TO_LAST_USED_IME},
+      {/*trigger_on_press=*/true, ui::VKEY_SPACE,
+       ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN, SWITCH_TO_LAST_USED_IME},
+      {/*trigger_on_press=*/true, ui::VKEY_TAB, ui::EF_ALT_DOWN,
+       CYCLE_FORWARD_MRU},
+      {/*trigger_on_press=*/true, ui::VKEY_TAB,
+       ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN, CYCLE_BACKWARD_MRU},
+  };
+
+  const ui::Accelerator new_accelerator(ui::VKEY_SPACE, ui::EF_CONTROL_DOWN);
+  AcceleratorConfigResult result =
+      config_->AddUserAccelerator(SWITCH_TO_LAST_USED_IME, new_accelerator);
+  EXPECT_EQ(AcceleratorConfigResult::kSuccess, result);
+
+  // Compare expected accelerators and that the observer was fired after
+  // adding an accelerator.
+  EXPECT_EQ(2, observer_.num_times_accelerator_updated_called());
+  ExpectAllAcceleratorsEqual(added_updated_test_data,
+                             config_->GetAllAccelerators());
+  const AcceleratorAction* found_action =
+      config_->FindAcceleratorAction(new_accelerator);
+  EXPECT_TRUE(found_action);
+  EXPECT_EQ(SWITCH_TO_LAST_USED_IME, *found_action);
+
+  // Remove CTRL + SPACE from SWITCH_TO_LAST_USED_IME.
+  const AcceleratorData removed_updated_test_data[] = {
+      {/*trigger_on_press=*/true, ui::VKEY_SPACE,
+       ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN, SWITCH_TO_LAST_USED_IME},
+      {/*trigger_on_press=*/true, ui::VKEY_TAB, ui::EF_ALT_DOWN,
+       CYCLE_FORWARD_MRU},
+      {/*trigger_on_press=*/true, ui::VKEY_TAB,
+       ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN, CYCLE_BACKWARD_MRU},
+  };
+
+  // Remove the accelerator now.
+  result = config_->RemoveAccelerator(SWITCH_TO_LAST_USED_IME, new_accelerator);
+  EXPECT_EQ(AcceleratorConfigResult::kSuccess, result);
+  EXPECT_EQ(3, observer_.num_times_accelerator_updated_called());
+  ExpectAllAcceleratorsEqual(removed_updated_test_data,
+                             config_->GetAllAccelerators());
+  EXPECT_FALSE(config_->FindAcceleratorAction(new_accelerator));
+}
+
+// Add accelerator and restore its default.
+TEST_F(AshAcceleratorConfigurationTest, AddRestoreAccelerator) {
+  EXPECT_EQ(0, observer_.num_times_accelerator_updated_called());
+  const AcceleratorData test_data[] = {
+      {/*trigger_on_press=*/true, ui::VKEY_SPACE,
+       ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN, SWITCH_TO_LAST_USED_IME},
+      {/*trigger_on_press=*/true, ui::VKEY_TAB, ui::EF_ALT_DOWN,
+       CYCLE_FORWARD_MRU},
+      {/*trigger_on_press=*/true, ui::VKEY_TAB,
+       ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN, CYCLE_BACKWARD_MRU},
+  };
+
+  config_->Initialize(test_data);
+
+  ExpectAllAcceleratorsEqual(test_data, config_->GetAllAccelerators());
+  EXPECT_EQ(1, observer_.num_times_accelerator_updated_called());
+
+  // Add CTRL + SPACE to SWITCH_TO_LAST_USED_IME.
+  const AcceleratorData updated_test_data[] = {
+      {/*trigger_on_press=*/true, ui::VKEY_SPACE, ui::EF_CONTROL_DOWN,
+       SWITCH_TO_LAST_USED_IME},
+      {/*trigger_on_press=*/true, ui::VKEY_SPACE,
+       ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN, SWITCH_TO_LAST_USED_IME},
+      {/*trigger_on_press=*/true, ui::VKEY_TAB, ui::EF_ALT_DOWN,
+       CYCLE_FORWARD_MRU},
+      {/*trigger_on_press=*/true, ui::VKEY_TAB,
+       ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN, CYCLE_BACKWARD_MRU},
+  };
+
+  const ui::Accelerator new_accelerator(ui::VKEY_SPACE, ui::EF_CONTROL_DOWN);
+  AcceleratorConfigResult result =
+      config_->AddUserAccelerator(SWITCH_TO_LAST_USED_IME, new_accelerator);
+  EXPECT_EQ(AcceleratorConfigResult::kSuccess, result);
+
+  // Compare expected accelerators and that the observer was fired after
+  // adding an accelerator.
+  EXPECT_EQ(2, observer_.num_times_accelerator_updated_called());
+  ExpectAllAcceleratorsEqual(updated_test_data, config_->GetAllAccelerators());
+  const AcceleratorAction* found_action =
+      config_->FindAcceleratorAction(new_accelerator);
+  EXPECT_TRUE(found_action);
+  EXPECT_EQ(SWITCH_TO_LAST_USED_IME, *found_action);
+
+  // Restore default, expect to be back to default state.
+  result = config_->RestoreDefault(SWITCH_TO_LAST_USED_IME);
+  EXPECT_EQ(3, observer_.num_times_accelerator_updated_called());
+  EXPECT_EQ(AcceleratorConfigResult::kSuccess, result);
+  EXPECT_FALSE(config_->FindAcceleratorAction(new_accelerator));
+  ExpectAllAcceleratorsEqual(test_data, config_->GetAllAccelerators());
+}
+
+// Add an accelerator, then add the same accelerator to another action.
+TEST_F(AshAcceleratorConfigurationTest, ReAddAcceleratorToAnotherAction) {
+  EXPECT_EQ(0, observer_.num_times_accelerator_updated_called());
+  const AcceleratorData test_data[] = {
+      {/*trigger_on_press=*/true, ui::VKEY_SPACE,
+       ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN, SWITCH_TO_LAST_USED_IME},
+      {/*trigger_on_press=*/true, ui::VKEY_TAB, ui::EF_ALT_DOWN,
+       CYCLE_FORWARD_MRU},
+      {/*trigger_on_press=*/true, ui::VKEY_TAB,
+       ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN, CYCLE_BACKWARD_MRU},
+  };
+
+  config_->Initialize(test_data);
+
+  ExpectAllAcceleratorsEqual(test_data, config_->GetAllAccelerators());
+  EXPECT_EQ(1, observer_.num_times_accelerator_updated_called());
+
+  // Add CTRL + SPACE to SWITCH_TO_LAST_USED_IME.
+  const AcceleratorData updated_test_data[] = {
+      {/*trigger_on_press=*/true, ui::VKEY_SPACE, ui::EF_CONTROL_DOWN,
+       SWITCH_TO_LAST_USED_IME},
+      {/*trigger_on_press=*/true, ui::VKEY_SPACE,
+       ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN, SWITCH_TO_LAST_USED_IME},
+      {/*trigger_on_press=*/true, ui::VKEY_TAB, ui::EF_ALT_DOWN,
+       CYCLE_FORWARD_MRU},
+      {/*trigger_on_press=*/true, ui::VKEY_TAB,
+       ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN, CYCLE_BACKWARD_MRU},
+  };
+
+  const ui::Accelerator new_accelerator(ui::VKEY_SPACE, ui::EF_CONTROL_DOWN);
+  AcceleratorConfigResult result =
+      config_->AddUserAccelerator(SWITCH_TO_LAST_USED_IME, new_accelerator);
+  EXPECT_EQ(AcceleratorConfigResult::kSuccess, result);
+
+  // Compare expected accelerators and that the observer was fired after
+  // adding an accelerator.
+  EXPECT_EQ(2, observer_.num_times_accelerator_updated_called());
+  ExpectAllAcceleratorsEqual(updated_test_data, config_->GetAllAccelerators());
+  const AcceleratorAction* found_action =
+      config_->FindAcceleratorAction(new_accelerator);
+  EXPECT_TRUE(found_action);
+  EXPECT_EQ(SWITCH_TO_LAST_USED_IME, *found_action);
+
+  // Add CTRL + SPACE to CYCLE_BACKWARD_MRU.
+  const AcceleratorData reupdated_test_data[] = {
+      {/*trigger_on_press=*/true, ui::VKEY_SPACE,
+       ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN, SWITCH_TO_LAST_USED_IME},
+      {/*trigger_on_press=*/true, ui::VKEY_TAB, ui::EF_ALT_DOWN,
+       CYCLE_FORWARD_MRU},
+      {/*trigger_on_press=*/true, ui::VKEY_TAB,
+       ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN, CYCLE_BACKWARD_MRU},
+      {/*trigger_on_press=*/true, ui::VKEY_SPACE, ui::EF_CONTROL_DOWN,
+       CYCLE_BACKWARD_MRU},
+  };
+  result = config_->AddUserAccelerator(CYCLE_BACKWARD_MRU, new_accelerator);
+  EXPECT_EQ(AcceleratorConfigResult::kSuccess, result);
+  ExpectAllAcceleratorsEqual(reupdated_test_data,
+                             config_->GetAllAccelerators());
+  const AcceleratorAction* found_action2 =
+      config_->FindAcceleratorAction(new_accelerator);
+  EXPECT_TRUE(found_action2);
+  EXPECT_EQ(CYCLE_BACKWARD_MRU, *found_action2);
+}
 }  // namespace ash
diff --git a/ash/clipboard/clipboard_history_controller_impl.cc b/ash/clipboard/clipboard_history_controller_impl.cc
index 10b8c69..9070a1a9 100644
--- a/ash/clipboard/clipboard_history_controller_impl.cc
+++ b/ash/clipboard/clipboard_history_controller_impl.cc
@@ -322,20 +322,21 @@
   observers_.RemoveObserver(observer);
 }
 
-void ClipboardHistoryControllerImpl::ShowMenu(
+bool ClipboardHistoryControllerImpl::ShowMenu(
     const gfx::Rect& anchor_rect,
     ui::MenuSourceType source_type,
     crosapi::mojom::ClipboardHistoryControllerShowSource show_source) {
-  ShowMenu(anchor_rect, source_type, show_source, OnMenuClosingCallback());
+  return ShowMenu(anchor_rect, source_type, show_source,
+                  OnMenuClosingCallback());
 }
 
-void ClipboardHistoryControllerImpl::ShowMenu(
+bool ClipboardHistoryControllerImpl::ShowMenu(
     const gfx::Rect& anchor_rect,
     ui::MenuSourceType source_type,
     crosapi::mojom::ClipboardHistoryControllerShowSource show_source,
     OnMenuClosingCallback callback) {
   if (IsMenuShowing() || !CanShowMenu())
-    return;
+    return false;
 
   // Close the running context menu if any before showing the clipboard history
   // menu. Because the clipboard history menu should not be nested.
@@ -380,6 +381,7 @@
   for (auto& observer : observers_) {
     observer.OnClipboardHistoryMenuShown(show_source);
   }
+  return true;
 }
 
 void ClipboardHistoryControllerImpl::GetHistoryValues(
diff --git a/ash/clipboard/clipboard_history_controller_impl.h b/ash/clipboard/clipboard_history_controller_impl.h
index 264efd3..934fc154 100644
--- a/ash/clipboard/clipboard_history_controller_impl.h
+++ b/ash/clipboard/clipboard_history_controller_impl.h
@@ -91,11 +91,11 @@
   // ClipboardHistoryController:
   void AddObserver(ClipboardHistoryController::Observer* observer) override;
   void RemoveObserver(ClipboardHistoryController::Observer* observer) override;
-  void ShowMenu(const gfx::Rect& anchor_rect,
+  bool ShowMenu(const gfx::Rect& anchor_rect,
                 ui::MenuSourceType source_type,
                 crosapi::mojom::ClipboardHistoryControllerShowSource
                     show_source) override;
-  void ShowMenu(
+  bool ShowMenu(
       const gfx::Rect& anchor_rect,
       ui::MenuSourceType source_type,
       crosapi::mojom::ClipboardHistoryControllerShowSource show_source,
diff --git a/ash/clipboard/clipboard_history_controller_unittest.cc b/ash/clipboard/clipboard_history_controller_unittest.cc
index bf14d50c..b988dbf 100644
--- a/ash/clipboard/clipboard_history_controller_unittest.cc
+++ b/ash/clipboard/clipboard_history_controller_unittest.cc
@@ -235,6 +235,47 @@
       "Ash.ClipboardHistory.ContextMenu.UserJourneyTime", 2);
 }
 
+// Tests that `ShowMenu()` returns whether the menu was shown successfully.
+TEST_F(ClipboardHistoryControllerTest, ShowMenuReturnsSuccess) {
+  // Try to show the menu without populating the clipboard. The menu should not
+  // show.
+  EXPECT_FALSE(GetClipboardHistoryController()->ShowMenu(
+      gfx::Rect(), ui::MenuSourceType::MENU_SOURCE_NONE,
+      crosapi::mojom::ClipboardHistoryControllerShowSource::kUnknown));
+  EXPECT_FALSE(GetClipboardHistoryController()->IsMenuShowing());
+
+  // Copy something to enable the clipboard history menu.
+  WriteTextToClipboardAndConfirm(u"test");
+
+  // Try to show the menu with the screen locked. The menu should not show.
+  auto* session_controller = Shell::Get()->session_controller();
+  session_controller->LockScreen();
+  GetSessionControllerClient()->FlushForTest();
+  EXPECT_TRUE(session_controller->IsScreenLocked());
+
+  EXPECT_FALSE(GetClipboardHistoryController()->ShowMenu(
+      gfx::Rect(), ui::MenuSourceType::MENU_SOURCE_NONE,
+      crosapi::mojom::ClipboardHistoryControllerShowSource::kUnknown));
+  EXPECT_FALSE(GetClipboardHistoryController()->IsMenuShowing());
+
+  session_controller->HideLockScreen();
+  GetSessionControllerClient()->FlushForTest();
+  EXPECT_FALSE(session_controller->IsScreenLocked());
+
+  // Show the menu.
+  EXPECT_TRUE(GetClipboardHistoryController()->ShowMenu(
+      gfx::Rect(), ui::MenuSourceType::MENU_SOURCE_NONE,
+      crosapi::mojom::ClipboardHistoryControllerShowSource::kUnknown));
+  EXPECT_TRUE(GetClipboardHistoryController()->IsMenuShowing());
+
+  // Try to show the menu again without closing the active menu. The menu should
+  // still be showing, but this attempt should fail.
+  EXPECT_FALSE(GetClipboardHistoryController()->ShowMenu(
+      gfx::Rect(), ui::MenuSourceType::MENU_SOURCE_NONE,
+      crosapi::mojom::ClipboardHistoryControllerShowSource::kUnknown));
+  EXPECT_TRUE(GetClipboardHistoryController()->IsMenuShowing());
+}
+
 // Tests that the client-provided `OnMenuClosingCallback` runs before the menu
 // closes.
 TEST_F(ClipboardHistoryControllerTest, OnMenuClosingCallback) {
diff --git a/ash/components/arc/test/fake_file_system_instance.h b/ash/components/arc/test/fake_file_system_instance.h
index 60d42a5..3010f73 100644
--- a/ash/components/arc/test/fake_file_system_instance.h
+++ b/ash/components/arc/test/fake_file_system_instance.h
@@ -371,7 +371,7 @@
   void OpenUrlsWithPermissionAndWindowInfo(
       mojom::OpenUrlsRequestPtr request,
       mojom::WindowInfoPtr window_info,
-      DEPRECATED_OpenUrlsWithPermissionCallback callback) override;
+      OpenUrlsWithPermissionAndWindowInfoCallback callback) override;
 
  private:
   // A pair of an authority and a document ID which identifies the location
diff --git a/ash/constants/ash_features.cc b/ash/constants/ash_features.cc
index 696b454..13257430 100644
--- a/ash/constants/ash_features.cc
+++ b/ash/constants/ash_features.cc
@@ -929,7 +929,7 @@
 // Enable inline sync status in Files app.
 BASE_FEATURE(kFilesInlineSyncStatus,
              "FilesInlineSyncStatus",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+             base::FEATURE_ENABLED_BY_DEFAULT);
 
 // Enables V2 of search functionality in files.
 BASE_FEATURE(kFilesSearchV2,
diff --git a/ash/metrics/login_unlock_throughput_recorder.cc b/ash/metrics/login_unlock_throughput_recorder.cc
index fe0de18..aced9ce 100644
--- a/ash/metrics/login_unlock_throughput_recorder.cc
+++ b/ash/metrics/login_unlock_throughput_recorder.cc
@@ -19,6 +19,7 @@
 #include "base/functional/bind.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/metrics/histogram_macros.h"
+#include "base/metrics/histogram_macros_local.h"
 #include "base/ranges/algorithm.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/trace_event/trace_event.h"
@@ -541,6 +542,7 @@
     REPORT_LOGIN_THROUGHPUT_EVENT("Ash.UnlockAnimation.Jank.TabletMode");
     REPORT_LOGIN_THROUGHPUT_EVENT("Ash.UnlockAnimation.Duration.ClamshellMode");
     REPORT_LOGIN_THROUGHPUT_EVENT("Ash.UnlockAnimation.Duration.TabletMode");
+    REPORT_LOGIN_THROUGHPUT_EVENT("ArcUiAvailable");
     if (!reported) {
       constexpr char kFailedEvent[] = "FailedToReportEvent";
       TRACE_EVENT_NESTABLE_ASYNC_BEGIN_WITH_TIMESTAMP0(
@@ -568,6 +570,13 @@
   }
 }
 
+void LoginUnlockThroughputRecorder::ArcUiAvailableAfterLogin() {
+  AddLoginTimeMarker("ArcUiAvailable");
+  const base::TimeDelta duration =
+      base::TimeTicks::Now() - primary_user_logged_in_;
+  LOCAL_HISTOGRAM_TIMES("Ash.Tast.ArcUiAvailableAfterLogin.Duration", duration);
+}
+
 void LoginUnlockThroughputRecorder::MaybeReportLoginFinished() {
   if (login_finished_reported_) {
     return;
diff --git a/ash/metrics/login_unlock_throughput_recorder.h b/ash/metrics/login_unlock_throughput_recorder.h
index ef26110..c71132f 100644
--- a/ash/metrics/login_unlock_throughput_recorder.h
+++ b/ash/metrics/login_unlock_throughput_recorder.h
@@ -94,6 +94,9 @@
   // This flag signals that all expected browser windows are already scheduled.
   void RestoreDataLoaded();
 
+  // Records that ARC has finished booting.
+  void ArcUiAvailableAfterLogin();
+
  private:
   class TimeMarker {
    public:
diff --git a/ash/public/cpp/clipboard_history_controller.h b/ash/public/cpp/clipboard_history_controller.h
index e204f65a..8669345 100644
--- a/ash/public/cpp/clipboard_history_controller.h
+++ b/ash/public/cpp/clipboard_history_controller.h
@@ -51,17 +51,16 @@
   // Returns whether the clipboard history menu is able to show.
   virtual bool CanShowMenu() const = 0;
 
-  // Shows the clipboard history menu triggered by `source_type` at the
-  // position specified by `anchor_rect`, provided the menu can currently be
+  // Attempts to show the clipboard history menu triggered by `source_type` at
+  // the position specified by `anchor_rect`. Returns whether the menu was
   // shown. `show_source` indicates how the user opened the menu. As long as the
   // menu is shown, `callback` runs just before the menu closes to indicate
   // whether a clipboard history paste is imminent.
-  // TODO(b/267694199): Make these functions return whether the menu was shown.
-  virtual void ShowMenu(
+  virtual bool ShowMenu(
       const gfx::Rect& anchor_rect,
       ui::MenuSourceType source_type,
       crosapi::mojom::ClipboardHistoryControllerShowSource show_source) = 0;
-  virtual void ShowMenu(
+  virtual bool ShowMenu(
       const gfx::Rect& anchor_rect,
       ui::MenuSourceType source_type,
       crosapi::mojom::ClipboardHistoryControllerShowSource show_source,
diff --git a/ash/shell.h b/ash/shell.h
index a465f060..d53c57d65 100644
--- a/ash/shell.h
+++ b/ash/shell.h
@@ -14,6 +14,7 @@
 #include "ash/constants/ash_features.h"
 #include "ash/game_dashboard/game_dashboard_controller.h"
 #include "ash/in_session_auth/in_session_auth_dialog_controller_impl.h"
+#include "ash/metrics/login_unlock_throughput_recorder.h"
 #include "ash/public/cpp/session/session_observer.h"
 #include "ash/public/cpp/shelf_types.h"
 #include "ash/public/cpp/system_sounds_delegate.h"
diff --git a/ash/style/color_palette_controller.cc b/ash/style/color_palette_controller.cc
index 9ffa154..252fcba 100644
--- a/ash/style/color_palette_controller.cc
+++ b/ash/style/color_palette_controller.cc
@@ -7,32 +7,74 @@
 #include <memory>
 
 #include "ash/constants/ash_pref_names.h"
+#include "ash/public/cpp/style/color_mode_observer.h"
+#include "ash/public/cpp/style/dark_light_mode_controller.h"
+#include "ash/public/cpp/wallpaper/wallpaper_controller.h"
+#include "ash/public/cpp/wallpaper/wallpaper_controller_observer.h"
 #include "ash/session/session_controller_impl.h"
 #include "ash/shell.h"
+#include "ash/style/color_util.h"
+#include "ash/style/dark_light_mode_controller_impl.h"
+#include "ash/wallpaper/wallpaper_controller_impl.h"
 #include "base/logging.h"
 #include "base/observer_list.h"
+#include "base/scoped_observation.h"
 #include "base/task/sequenced_task_runner.h"
 #include "base/task/task_runner.h"
 #include "base/time/time.h"
+#include "chromeos/constants/chromeos_features.h"
 #include "components/pref_registry/pref_registry_syncable.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
 #include "ui/color/color_provider_manager.h"
+#include "ui/gfx/color_palette.h"
 
 namespace ash {
 
 namespace {
 
+class ColorPaletteControllerImpl;
+
+using ColorMode = ui::ColorProviderManager::ColorMode;
+
+// Returns the wallpaper colors for pre-Jelly.  Called for both dark and light.
+SkColor GetWallpaperColor(bool is_dark_mode_enabled) {
+  const SkColor default_color =
+      is_dark_mode_enabled ? gfx::kGoogleGrey900 : SK_ColorWHITE;
+  return ColorUtil::GetBackgroundThemedColor(default_color,
+                                             is_dark_mode_enabled);
+}
+
 PrefService* GetUserPrefService(const AccountId& account_id) {
   DCHECK(account_id.is_valid());
   return Shell::Get()->session_controller()->GetUserPrefServiceForUser(
       account_id);
 }
 
+// Returns the currently active user session (at index 0).
+const UserSession* GetActiveUserSession() {
+  return Shell::Get()->session_controller()->GetUserSession(/*index=*/0);
+}
+
+const AccountId& AccountFromSession(const UserSession* session) {
+  CHECK(session);
+  return session->user_info.account_id;
+}
+
 // TODO(b/258719005): Finish implementation with code that works/uses libmonet.
-class ColorPaletteControllerImpl : public ColorPaletteController {
+class ColorPaletteControllerImpl : public ColorPaletteController,
+                                   public WallpaperControllerObserver,
+                                   public ColorModeObserver {
  public:
-  ColorPaletteControllerImpl() = default;
+  ColorPaletteControllerImpl(
+      DarkLightModeController* dark_light_mode_controller,
+      WallpaperControllerImpl* wallpaper_controller)
+      : wallpaper_controller_(wallpaper_controller),
+        dark_light_mode_controller_(dark_light_mode_controller) {
+    wallpaper_observation_.Observe(wallpaper_controller);
+    wallpaper_color_[ColorMode::kDark] = SK_ColorTRANSPARENT;
+    wallpaper_color_[ColorMode::kLight] = SK_ColorTRANSPARENT;
+  }
 
   ~ColorPaletteControllerImpl() override = default;
 
@@ -57,6 +99,7 @@
                              static_cast<int>(scheme));
     // TODO(b/258719005): Call this after the native theme change has been
     // applied.
+    NotifyObservers(account_id);
     base::SequencedTaskRunner::GetCurrentDefault()->PostDelayedTask(
         FROM_HERE, std::move(on_complete), base::Milliseconds(100));
   }
@@ -77,25 +120,23 @@
     pref_service->SetUint64(prefs::kDynamicColorSeedColor, seed_color);
     // TODO(b/258719005): Call this after the native theme change has been
     // applied.
+    NotifyObservers(account_id);
     base::SequencedTaskRunner::GetCurrentDefault()->PostDelayedTask(
         FROM_HERE, std::move(on_complete), base::Milliseconds(100));
   }
 
   ColorPaletteSeed GetColorPaletteSeed(
       const AccountId& account_id) const override {
-    PrefService* pref_service = GetUserPrefService(account_id);
-    if (!pref_service) {
-      DVLOG(1) << "No user pref service available. Returning default color "
-                  "palette seed.";
-      return {.seed_color = SK_ColorBLUE,
-              .scheme = ColorScheme::kTonalSpot,
-              .color_mode = ui::ColorProviderManager::ColorMode::kLight};
-    }
-    SkColor color = static_cast<SkColor>(
-        pref_service->GetUint64(prefs::kDynamicColorSeedColor));
-    return {.seed_color = color,
-            .scheme = GetColorScheme(account_id),
-            .color_mode = ui::ColorProviderManager::ColorMode::kLight};
+    ColorPaletteSeed seed;
+    seed.color_mode = dark_light_mode_controller_->IsDarkModeEnabled()
+                          ? ui::ColorProviderManager::ColorMode::kDark
+                          : ui::ColorProviderManager::ColorMode::kLight;
+    seed.seed_color = UsesWallpaperSeedColor(account_id)
+                          ? wallpaper_color_.at(seed.color_mode)
+                          : GetStaticSeedColor(account_id);
+    seed.scheme = GetColorScheme(account_id);
+
+    return seed;
   }
 
   bool UsesWallpaperSeedColor(const AccountId& account_id) const override {
@@ -122,7 +163,7 @@
       return absl::nullopt;
     }
     if (GetColorScheme(account_id) == ColorScheme::kStatic) {
-      return pref_service->GetUint64(prefs::kDynamicColorSeedColor);
+      return GetStaticSeedColor(account_id);
     }
 
     return absl::nullopt;
@@ -140,8 +181,52 @@
         base::Milliseconds(20));
   }
 
+  // WallpaperControllerObserver overrides:
+  void OnWallpaperColorsChanged() override {
+    if (!chromeos::features::IsJellyEnabled()) {
+      SkColor dark_color = GetWallpaperColor(true);
+      SkColor light_color = GetWallpaperColor(false);
+      SetWallpaperColor(dark_color, light_color);
+      return;
+    }
+
+    SkColor wallpaper_color =
+        wallpaper_controller_->calculated_colors()->celebi_color;
+    // When Jelly is enabled, light/dark changes are handled in palette
+    // generation.  So it's the same color.
+    SetWallpaperColor(wallpaper_color, wallpaper_color);
+  }
+
+  // ColorModeObserver overrides:
+  void OnColorModeChanged(bool) override {
+    // Change colors and notify.
+    auto* session = GetActiveUserSession();
+    if (session) {
+      NotifyObservers(AccountFromSession(session));
+    }
+  }
+
  private:
-  base::ObserverList<ColorPaletteController::Observer> observers_;
+  void SetWallpaperColor(SkColor dark_color, SkColor light_color) {
+    wallpaper_color_[ColorMode::kDark] = dark_color;
+    wallpaper_color_[ColorMode::kLight] = light_color;
+    // TODO(b/258719005): Update Native Theme
+    auto* session = GetActiveUserSession();
+    if (session) {
+      NotifyObservers(AccountFromSession(session));
+    }
+  }
+
+  SkColor GetStaticSeedColor(const AccountId& account_id) const {
+    PrefService* pref_service = GetUserPrefService(account_id);
+    if (!pref_service) {
+      DVLOG(1) << "No user pref service available. Returning default color "
+                  "palette seed.";
+      return SK_ColorBLUE;
+    }
+    return static_cast<SkColor>(
+        pref_service->GetUint64(prefs::kDynamicColorSeedColor));
+  }
 
   SampleColorScheme GenerateSampleColorScheme(ColorScheme scheme) const {
     // TODO(b/258719005): Return correct and different schemes for each
@@ -154,13 +239,42 @@
             .secondary = SK_ColorGREEN,
             .tertiary = SK_ColorBLUE};
   }
+
+  void NotifyObservers(const AccountId& account_id) {
+    ColorPaletteSeed seed = GetColorPaletteSeed(account_id);
+    for (auto& observer : observers_) {
+      observer.OnColorPaletteChanging(seed);
+    }
+  }
+
+  base::flat_map<ui::ColorProviderManager::ColorMode, SkColor> wallpaper_color_;
+
+  base::ScopedObservation<WallpaperController, WallpaperControllerObserver>
+      wallpaper_observation_{this};
+
+  base::raw_ptr<WallpaperControllerImpl> wallpaper_controller_;  // unowned
+
+  base::raw_ptr<DarkLightModeController>
+      dark_light_mode_controller_;  // unowned
+
+  base::ObserverList<ColorPaletteController::Observer> observers_;
 };
 
 }  // namespace
 
 // static
 std::unique_ptr<ColorPaletteController> ColorPaletteController::Create() {
-  return std::make_unique<ColorPaletteControllerImpl>();
+  Shell* shell = Shell::Get();
+  return Create(shell->dark_light_mode_controller(),
+                shell->wallpaper_controller());
+}
+
+// static
+std::unique_ptr<ColorPaletteController> ColorPaletteController::Create(
+    DarkLightModeController* dark_light_mode_controller,
+    WallpaperControllerImpl* wallpaper_controller) {
+  return std::make_unique<ColorPaletteControllerImpl>(
+      dark_light_mode_controller, wallpaper_controller);
 }
 
 // static
diff --git a/ash/style/color_palette_controller.h b/ash/style/color_palette_controller.h
index 59b7a75..2370b57 100644
--- a/ash/style/color_palette_controller.h
+++ b/ash/style/color_palette_controller.h
@@ -18,6 +18,9 @@
 
 namespace ash {
 
+class DarkLightModeController;
+class WallpaperControllerImpl;
+
 // Types of ColorSchemes. For a given seed color, each ColorScheme will generate
 // a different color palette/set of ref colors.
 enum class ASH_EXPORT ColorScheme {
@@ -69,7 +72,12 @@
     virtual void OnColorPaletteChanging(const ColorPaletteSeed& seed) = 0;
   };
 
-  static ASH_EXPORT std::unique_ptr<ColorPaletteController> Create();
+  // Temporary factory for migration.  DO NOT USE.
+  static std::unique_ptr<ColorPaletteController> Create();
+
+  static std::unique_ptr<ColorPaletteController> Create(
+      DarkLightModeController* dark_light_mode_controller,
+      WallpaperControllerImpl* wallpaper_controller);
 
   ColorPaletteController() = default;
 
diff --git a/ash/style/color_palette_controller_unittest.cc b/ash/style/color_palette_controller_unittest.cc
index 5ab1df1..7bd5ae7 100644
--- a/ash/style/color_palette_controller_unittest.cc
+++ b/ash/style/color_palette_controller_unittest.cc
@@ -4,7 +4,10 @@
 
 #include "ash/style/color_palette_controller.h"
 
+#include "ash/shell.h"
+#include "ash/style/dark_light_mode_controller_impl.h"
 #include "ash/test/ash_test_base.h"
+#include "ash/wallpaper/wallpaper_controller_impl.h"
 #include "base/functional/callback_helpers.h"
 #include "third_party/skia/include/core/SkColor.h"
 
@@ -18,49 +21,65 @@
 }  // namespace
 
 class ColorPaletteControllerTest : public NoSessionAshTestBase {
+ public:
   void SetUp() override {
     NoSessionAshTestBase::SetUp();
     GetSessionControllerClient()->Reset();
     GetSessionControllerClient()->AddUserSession(kAccountId, kUser);
-    color_palette_controller = ColorPaletteController::Create();
+    color_palette_controller_ = ColorPaletteController::Create(
+        Shell::Get()->dark_light_mode_controller(),
+        Shell::Get()->wallpaper_controller());
   }
 
- protected:
-  std::unique_ptr<ColorPaletteController> color_palette_controller;
+  void TearDown() override {
+    // Must release the controller before Shell is destructed.
+    color_palette_controller_.reset();
+
+    NoSessionAshTestBase::TearDown();
+  }
+
+  ColorPaletteController* color_palette_controller() {
+    return color_palette_controller_.get();
+  }
+
+ private:
+  std::unique_ptr<ColorPaletteController> color_palette_controller_;
 };
 
 TEST_F(ColorPaletteControllerTest, ExpectedEmptyValues) {
   EXPECT_EQ(ColorScheme::kTonalSpot,
-            color_palette_controller->GetColorScheme(kAccountId));
+            color_palette_controller()->GetColorScheme(kAccountId));
   EXPECT_EQ(absl::nullopt,
-            color_palette_controller->GetStaticColor(kAccountId));
+            color_palette_controller()->GetStaticColor(kAccountId));
 }
 
 TEST_F(ColorPaletteControllerTest, SetColorScheme) {
   ColorScheme color_scheme = ColorScheme::kExpressive;
 
-  color_palette_controller->SetColorScheme(color_scheme, kAccountId,
-                                           base::DoNothing());
+  color_palette_controller()->SetColorScheme(color_scheme, kAccountId,
+                                             base::DoNothing());
 
-  EXPECT_EQ(color_scheme, color_palette_controller->GetColorScheme(kAccountId));
+  EXPECT_EQ(color_scheme,
+            color_palette_controller()->GetColorScheme(kAccountId));
   EXPECT_EQ(absl::nullopt,
-            color_palette_controller->GetStaticColor(kAccountId));
+            color_palette_controller()->GetStaticColor(kAccountId));
   auto color_palette_seed =
-      color_palette_controller->GetColorPaletteSeed(kAccountId);
+      color_palette_controller()->GetColorPaletteSeed(kAccountId);
   EXPECT_EQ(color_scheme, color_palette_seed.scheme);
 }
 
 TEST_F(ColorPaletteControllerTest, SetStaticColor) {
   SkColor static_color = SK_ColorGRAY;
 
-  color_palette_controller->SetStaticColor(static_color, kAccountId,
-                                           base::DoNothing());
+  color_palette_controller()->SetStaticColor(static_color, kAccountId,
+                                             base::DoNothing());
 
-  EXPECT_EQ(static_color, color_palette_controller->GetStaticColor(kAccountId));
+  EXPECT_EQ(static_color,
+            color_palette_controller()->GetStaticColor(kAccountId));
   EXPECT_EQ(ColorScheme::kStatic,
-            color_palette_controller->GetColorScheme(kAccountId));
+            color_palette_controller()->GetColorScheme(kAccountId));
   auto color_palette_seed =
-      color_palette_controller->GetColorPaletteSeed(kAccountId);
+      color_palette_controller()->GetColorPaletteSeed(kAccountId);
   EXPECT_EQ(ColorScheme::kStatic, color_palette_seed.scheme);
   EXPECT_EQ(static_color, color_palette_seed.seed_color);
 }
diff --git a/ash/webui/eche_app_ui/eche_recent_app_click_handler_unittest.cc b/ash/webui/eche_app_ui/eche_recent_app_click_handler_unittest.cc
index 072c08e..9f974b3 100644
--- a/ash/webui/eche_app_ui/eche_recent_app_click_handler_unittest.cc
+++ b/ash/webui/eche_app_ui/eche_recent_app_click_handler_unittest.cc
@@ -68,10 +68,10 @@
 
   void TearDown() override {
     apps_launch_info_provider_.reset();
-    connection_status_handler_.reset();
     launch_app_helper_.reset();
     handler_.reset();
     stream_status_change_handler_.reset();
+    connection_status_handler_.reset();
   }
 
   void FakeLaunchEcheAppFunction(
diff --git a/ash/webui/shortcut_customization_ui/backend/accelerator_configuration_provider_unittest.cc b/ash/webui/shortcut_customization_ui/backend/accelerator_configuration_provider_unittest.cc
index 24d90be..35134b1 100644
--- a/ash/webui/shortcut_customization_ui/backend/accelerator_configuration_provider_unittest.cc
+++ b/ash/webui/shortcut_customization_ui/backend/accelerator_configuration_provider_unittest.cc
@@ -333,6 +333,9 @@
     fake_keyboard_manager_ = std::make_unique<FakeDeviceManager>();
     provider_->ignore_layouts_for_testing_ = true;
     base::RunLoop().RunUntilIdle();
+    // After adding a fake keyboard, clear the observer call count.
+    observer_.clear_num_times_notified();
+    EXPECT_EQ(0, observer_.num_times_notified());
   }
 
   void TearDown() override {
@@ -434,13 +437,7 @@
   base::RunLoop().RunUntilIdle();
 }
 
-// TODO(crbug.com/1426992): Fix flakiness and re-enable.
-#if BUILDFLAG(IS_CHROMEOS)
-#define MAYBE_InitialAccelInitCalls DISABLED_InitialAccelInitCalls
-#else
-#define MAYBE_InitialAccelInitCalls InitialAccelInitCalls
-#endif
-TEST_F(AcceleratorConfigurationProviderTest, MAYBE_InitialAccelInitCalls) {
+TEST_F(AcceleratorConfigurationProviderTest, InitialAccelInitCalls) {
   FakeAcceleratorsUpdatedMojoObserver mojo_observer;
   SetUpObserver(&mojo_observer);
   EXPECT_EQ(0, mojo_observer.num_times_notified());
diff --git a/base/feature_list.cc b/base/feature_list.cc
index a689df4..9698a298 100644
--- a/base/feature_list.cc
+++ b/base/feature_list.cc
@@ -93,10 +93,7 @@
     // TODO(crbug.com/1383852): When we believe that all early accesses have
     // been fixed, remove this base::debug::DumpWithoutCrashing() and change the
     // above DCHECK to a CHECK.
-
-    // The following line is commented to reduce the crash volume while a fix
-    // for crbug.com/1392145 is prepared.
-    // base::debug::DumpWithoutCrashing();
+    base::debug::DumpWithoutCrashing();
 #endif  // !BUILDFLAG(IS_IOS) && !BUILDFLAG(IS_ANDROID) &&
         // !BUILDFLAG(IS_CHROMEOS)
   }
diff --git a/build/fuchsia/linux_internal.sdk.sha1 b/build/fuchsia/linux_internal.sdk.sha1
index 07441731..51c0d0f 100644
--- a/build/fuchsia/linux_internal.sdk.sha1
+++ b/build/fuchsia/linux_internal.sdk.sha1
@@ -1 +1 @@
-12.20230323.1.1
+12.20230323.3.1
diff --git a/buildtools/deps_revisions.gni b/buildtools/deps_revisions.gni
index 9a37bd1..fd64cd5 100644
--- a/buildtools/deps_revisions.gni
+++ b/buildtools/deps_revisions.gni
@@ -5,5 +5,5 @@
 declare_args() {
   # Used to cause full rebuilds on libc++ rolls. This should be kept in sync
   # with the libcxx_revision vars in //DEPS.
-  libcxx_revision = "e44019bfac2b2d3ebe1618628884f85c8600e322"
+  libcxx_revision = "5622befaf8a9d539bc94c9f1341b8e76065334db"
 }
diff --git a/cc/input/input_handler.cc b/cc/input/input_handler.cc
index c1db2de89..51b88ed 100644
--- a/cc/input/input_handler.cc
+++ b/cc/input/input_handler.cc
@@ -164,8 +164,6 @@
         // freshly created scroller hasn't yet been committed or a
         // scroller-destroying commit beats the hit test back to the compositor
         // thread. However, these cases shouldn't be user perceptible.
-        scroll_status.main_thread_scrolling_reasons =
-            MainThreadScrollingReason::kNoScrollingLayer;
         scroll_status.thread = InputHandler::ScrollThread::SCROLL_IGNORED;
         return scroll_status;
       }
@@ -185,8 +183,6 @@
           // drop the scroll as continuing could cause us to infinitely bounce
           // back and forth between here and hit testing on the main thread.
           NOTREACHED();
-          scroll_status.main_thread_scrolling_reasons =
-              MainThreadScrollingReason::kNoScrollingLayer;
           scroll_status.thread = InputHandler::ScrollThread::SCROLL_IGNORED;
           return scroll_status;
         }
@@ -261,11 +257,6 @@
     scroll_status.thread = InputHandler::ScrollThread::SCROLL_ON_MAIN_THREAD;
     return scroll_status;
   } else if (!scrolling_node) {
-    // TODO(crbug.com/1155663): Make sure to set main_thread_scrolling_reasons
-    // only when ScrollStatus.thread is set to
-    // InputHander::ScrollThread::SCROLL_ON_MAIN_THREAD
-    scroll_status.main_thread_scrolling_reasons =
-        MainThreadScrollingReason::kNoScrollingLayer;
     if (compositor_delegate_->GetSettings().is_for_embedded_frame) {
       // OOPIFs or fenced frames never have a viewport scroll node so if we
       // can't scroll we need to be bubble up to the parent frame. This happens
@@ -323,8 +314,6 @@
   if (!OuterViewportScrollNode()) {
     InputHandler::ScrollStatus scroll_status;
     scroll_status.thread = InputHandler::ScrollThread::SCROLL_IGNORED;
-    scroll_status.main_thread_scrolling_reasons =
-        MainThreadScrollingReason::kNoScrollingLayer;
     return scroll_status;
   }
 
@@ -1302,16 +1291,12 @@
   if (!screen_space_transform.IsInvertible()) {
     TRACE_EVENT0("cc", "LayerImpl::TryScroll: Ignored NonInvertibleTransform");
     scroll_status.thread = InputHandler::ScrollThread::SCROLL_IGNORED;
-    scroll_status.main_thread_scrolling_reasons =
-        MainThreadScrollingReason::kNonInvertibleTransform;
     return scroll_status;
   }
 
   if (!scroll_node->scrollable) {
     TRACE_EVENT0("cc", "LayerImpl::tryScroll: Ignored not scrollable");
     scroll_status.thread = InputHandler::ScrollThread::SCROLL_IGNORED;
-    scroll_status.main_thread_scrolling_reasons =
-        MainThreadScrollingReason::kNotScrollable;
     return scroll_status;
   }
 
@@ -1341,8 +1326,6 @@
                  "LayerImpl::tryScroll: Ignored. Technically scrollable,"
                  " but has no affordance in either direction.");
     scroll_status.thread = InputHandler::ScrollThread::SCROLL_IGNORED;
-    scroll_status.main_thread_scrolling_reasons =
-        MainThreadScrollingReason::kNotScrollable;
     return scroll_status;
   }
 
diff --git a/cc/input/input_handler.h b/cc/input/input_handler.h
index 4b47f75..ddc65a8 100644
--- a/cc/input/input_handler.h
+++ b/cc/input/input_handler.h
@@ -218,9 +218,7 @@
           main_thread_scrolling_reasons(main_thread_scrolling_reasons),
           needs_main_thread_hit_test(needs_main_thread_hit_test) {}
     ScrollThread thread = ScrollThread::SCROLL_ON_IMPL_THREAD;
-    // TODO(crbug.com/1155663): Make sure to set main_thread_scrolling_reasons
-    // only when ScrollStatus.thread is set to
-    // InputHander::ScrollThread::SCROLL_ON_MAIN_THREAD
+    // This should be set to nonzero iff `thread` is SCROLL_ON_MAIN_THREAD.
     uint32_t main_thread_scrolling_reasons =
         MainThreadScrollingReason::kNotScrollingOnMain;
     // TODO(crbug.com/1155758): This is a temporary workaround for GuestViews
diff --git a/cc/input/main_thread_scrolling_reason.cc b/cc/input/main_thread_scrolling_reason.cc
index e917018..5b351f8 100644
--- a/cc/input/main_thread_scrolling_reason.cc
+++ b/cc/input/main_thread_scrolling_reason.cc
@@ -48,10 +48,6 @@
     traced_value.AppendString("Failed hit test");
   if (reasons & kNoScrollingLayer)
     traced_value.AppendString("No scrolling layer");
-  if (reasons & kNotScrollable)
-    traced_value.AppendString("Not scrollable");
-  if (reasons & kNonInvertibleTransform)
-    traced_value.AppendString("Non-invertible transform");
   if (reasons & kWheelEventHandlerRegion)
     traced_value.AppendString("Wheel event handler region");
   if (reasons & kTouchEventHandlerRegion)
diff --git a/cc/input/main_thread_scrolling_reason.h b/cc/input/main_thread_scrolling_reason.h
index 132c9dc..7bb2f73 100644
--- a/cc/input/main_thread_scrolling_reason.h
+++ b/cc/input/main_thread_scrolling_reason.h
@@ -59,8 +59,6 @@
     kNonFastScrollableRegion = 1 << 8,
     kFailedHitTest = 1 << 9,
     kNoScrollingLayer = 1 << 10,
-    kNotScrollable = 1 << 11,
-    kNonInvertibleTransform = 1 << 12,
     kWheelEventHandlerRegion = 1 << 13,
     kTouchEventHandlerRegion = 1 << 14,
 
@@ -85,8 +83,7 @@
   static bool CompositorCanSetScrollReasons(uint32_t reasons) {
     constexpr uint32_t reasons_set_by_compositor =
         kNonFastScrollableRegion | kFailedHitTest | kNoScrollingLayer |
-        kNotScrollable | kNonInvertibleTransform | kWheelEventHandlerRegion |
-        kTouchEventHandlerRegion;
+        kWheelEventHandlerRegion | kTouchEventHandlerRegion;
     return (reasons & reasons_set_by_compositor) == reasons;
   }
 
diff --git a/cc/input/main_thread_scrolling_reason_unittest.cc b/cc/input/main_thread_scrolling_reason_unittest.cc
index 83fbc30..522e849 100644
--- a/cc/input/main_thread_scrolling_reason_unittest.cc
+++ b/cc/input/main_thread_scrolling_reason_unittest.cc
@@ -21,8 +21,6 @@
       "Non fast scrollable region, "
       "Failed hit test, "
       "No scrolling layer, "
-      "Not scrollable, "
-      "Non-invertible transform, "
       "Wheel event handler region, "
       "Touch event handler region",
       MainThreadScrollingReason::AsText(0xffffffffu));
diff --git a/cc/trees/layer_tree_host_impl_unittest.cc b/cc/trees/layer_tree_host_impl_unittest.cc
index c38b1195..5f394ac 100644
--- a/cc/trees/layer_tree_host_impl_unittest.cc
+++ b/cc/trees/layer_tree_host_impl_unittest.cc
@@ -1256,7 +1256,7 @@
           .get(),
       ui::ScrollInputType::kWheel);
   EXPECT_EQ(ScrollThread::SCROLL_IGNORED, status.thread);
-  EXPECT_EQ(MainThreadScrollingReason::kNoScrollingLayer,
+  EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
             status.main_thread_scrolling_reasons);
 
   status = GetInputHandler().RootScrollBegin(
@@ -1265,7 +1265,7 @@
           .get(),
       ui::ScrollInputType::kWheel);
   EXPECT_EQ(ScrollThread::SCROLL_IGNORED, status.thread);
-  EXPECT_EQ(MainThreadScrollingReason::kNoScrollingLayer,
+  EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
             status.main_thread_scrolling_reasons);
 }
 
@@ -1490,7 +1490,7 @@
           .get(),
       ui::ScrollInputType::kWheel);
   EXPECT_EQ(ScrollThread::SCROLL_IGNORED, status.thread);
-  EXPECT_EQ(MainThreadScrollingReason::kNoScrollingLayer,
+  EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
             status.main_thread_scrolling_reasons);
 }
 
@@ -6900,7 +6900,7 @@
           .get(),
       ui::ScrollInputType::kWheel);
   EXPECT_EQ(ScrollThread::SCROLL_IGNORED, status.thread);
-  EXPECT_EQ(MainThreadScrollingReason::kNoScrollingLayer,
+  EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
             status.main_thread_scrolling_reasons);
   EXPECT_FALSE(did_request_redraw_);
   EXPECT_FALSE(did_request_commit_);
@@ -18083,7 +18083,7 @@
     ScrollStatus status = ScrollBegin(gfx::Vector2d(0, 10));
     status = ContinuedScrollBegin(kUnknown);
     EXPECT_EQ(ScrollThread::SCROLL_IGNORED, status.thread);
-    EXPECT_EQ(MainThreadScrollingReason::kNoScrollingLayer,
+    EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
               status.main_thread_scrolling_reasons);
   }
 }
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index 5f88f43..fa35cf79 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -209,6 +209,7 @@
       ":app_hooks_java",
       "//chrome/browser/accessibility/hierarchysnapshotter/android:delegate_public_impl_java",
       "//chrome/browser/feed/android:hooks_public_impl_java",
+      "//chrome/browser/feedback/android:delegate_public_impl_java",
       "//chrome/browser/lens:delegate_public_impl_java",
       "//chrome/browser/locale:delegate_public_impl_java",
       "//chrome/browser/partnerbookmarks:delegate_public_impl_java",
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java
index eb251ed..b43431f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java
@@ -19,7 +19,6 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.os.CancellationSignal;
-import android.os.Handler;
 import android.os.SystemClock;
 import android.util.Pair;
 import android.util.TypedValue;
@@ -1663,19 +1662,9 @@
         // focus dependency is because doing it earlier can cause drawing bugs, e.g. crbug/673831.
         if (!mNativeInitialized || !hasWindowFocus()) return;
 
-        if (!DeviceFormFactor.isNonMultiDisplayContextOnTablet(this)) {
-            changeBackgroundColorForResizing();
-        } else {
-            // Post the background update call as a separate task, as doing it synchronously
-            // here can cause redrawing glitches. See crbug.com/686662 and crbug.com/1260127 for
-            // example problems.
-            Handler handler = new Handler();
-            handler.post(() -> {
-                // The window background color is used as the resizing background color in
-                // Android N+ multi-window mode. See crbug.com/602366.
-                changeBackgroundColorForResizing();
-            });
-        }
+        // The window background color is used as the resizing background color in Android N+
+        // multi-window mode. See crbug.com/602366.
+        changeBackgroundColorForResizing();
         mRemoveWindowBackgroundDone = true;
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorView.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorView.java
index b5edfbfc..a781fc5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorView.java
@@ -610,7 +610,7 @@
         }
         mHaveSwappedFramesSinceSurfaceCreated = true;
 
-        mRenderHost.didSwapBuffers(swappedCurrentSize);
+        mRenderHost.didSwapBuffers(swappedCurrentSize, mFramesUntilHideBackground);
 
         updateNeedsDidSwapBuffersCallback();
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorViewHolder.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorViewHolder.java
index 0d6e76e..755cdef 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorViewHolder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorViewHolder.java
@@ -547,8 +547,6 @@
             if (controlContainerVG != null) {
                 controlContainerVG.setBackgroundResource(0);
             }
-
-            mSetBackgroundRunnable = null;
         };
     }
 
@@ -1220,12 +1218,7 @@
     public void didSwapFrame(int pendingFrameCount) {
         TraceEvent.instant("didSwapFrame");
 
-        if (mHasDrawnOnce && mSetBackgroundRunnable != null) {
-            post(mSetBackgroundRunnable);
-        }
-
         mHasDrawnOnce = true;
-
         mPendingFrameCount = pendingFrameCount;
 
         if (!mSkipInvalidation || pendingFrameCount == 0) flushInvalidation();
@@ -1237,7 +1230,12 @@
     }
 
     @Override
-    public void didSwapBuffers(boolean swappedCurrentSize) {
+    public void didSwapBuffers(boolean swappedCurrentSize, int framesUntilHideBackground) {
+        if (mSetBackgroundRunnable != null && mHasDrawnOnce && framesUntilHideBackground == 0) {
+            post(mSetBackgroundRunnable);
+            mSetBackgroundRunnable = null;
+        }
+
         for (Runnable runnable : mDidSwapBuffersCallbacks) {
             runnable.run();
         }
@@ -1765,7 +1763,7 @@
 
     // Should be called any time inputs used to compute `needsSwapCallback` changes.
     private void updateNeedsSwapBuffersCallback() {
-        boolean needsSwapCallback = !mOnCompositorLayoutCallbacks.isEmpty()
+        boolean needsSwapCallback = !mHasDrawnOnce || !mOnCompositorLayoutCallbacks.isEmpty()
                 || !mDidSwapFrameCallbacks.isEmpty() || !mDidSwapBuffersCallbacks.isEmpty();
         mCompositorView.setRenderHostNeedsDidSwapBuffersCallback(needsSwapCallback);
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutRenderHost.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutRenderHost.java
index 39dbf2a..a981242 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutRenderHost.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutRenderHost.java
@@ -31,8 +31,10 @@
     /**
      * Indicates that the compositor swapped buffers.
      * @param swappedCurrentSize Whether the swapped buffer size is the same as the current one.
+     * @param framesUntilHideBackground The number of buffer swaps needed until the incoming surface
+     *         has a frame ready. Zero if no incoming surface or if the incoming surface is ready.
      */
-    default void didSwapBuffers(boolean swappedCurrentSize) {}
+    default void didSwapBuffers(boolean swappedCurrentSize, int framesUntilHideBackground) {}
 
     /**
      * Indicates that the rendering surface has just been created.
diff --git a/chrome/app/os_settings_strings.grdp b/chrome/app/os_settings_strings.grdp
index be5b16c..d374e00 100644
--- a/chrome/app/os_settings_strings.grdp
+++ b/chrome/app/os_settings_strings.grdp
@@ -2708,13 +2708,13 @@
   <message name="IDS_SETTINGS_MOUSE_TITLE" desc="In Device Settings, the title of the mouse settings subpage.">
     Mouse
   </message>
-  <message name="IDS_SETTINGS_BUILT_IN_POINTING_STICK_NAME" translateable="false" desc="In Device Settings, the name of the built-in/internal pointing stick device within the settings app.">
+  <message name="IDS_SETTINGS_BUILT_IN_POINTING_STICK_NAME" desc="In Device Settings, the name of the built-in/internal pointing stick device within the settings app.">
     Built-in TrackPoint
   </message>
   <message name="IDS_SETTINGS_POINTING_STICK_TITLE" desc="In Device Settings, the title of the pointing stick settings subpage. In most cases this will just be 'TrackPoint', the brand name of the pointing sticks used on Chromebooks.">
     TrackPoint
   </message>
-  <message name="IDS_SETTINGS_BUILT_IN_TOUCHPAD_NAME" translateable="false" desc="In Device Settings, the name of the built-in/internal touchpad device within the settings app.">
+  <message name="IDS_SETTINGS_BUILT_IN_TOUCHPAD_NAME" desc="In Device Settings, the name of the built-in/internal touchpad device within the settings app.">
     Built-in Touchpad
   </message>
   <message name="IDS_SETTINGS_TOUCHPAD_TITLE" desc="In Device Settings, the title of the touchpad settings subpage.">
@@ -4641,36 +4641,36 @@
   </message>
 
   <!-- Per Device Keyboard page (OS settings) -->
-  <message name="IDS_SETTINGS_BUILT_IN_KEYBOARD_NAME" translateable="false" desc="In Device Settings, the name of the built-in/internal keyboard device within the settings app.">
+  <message name="IDS_SETTINGS_BUILT_IN_KEYBOARD_NAME" desc="In Device Settings, the name of the built-in/internal keyboard device within the settings app.">
     Built-in Keyboard
   </message>
-  <message name="IDS_SETTINGS_KEYBOARD_REMAP_KEYS_ROW_LABEL" translateable="false" desc="In per-device keyboard Settings, the label of the Remap Keyboard Keys row.">
+  <message name="IDS_SETTINGS_KEYBOARD_REMAP_KEYS_ROW_LABEL" desc="In per-device keyboard Settings, the label of the Remap Keyboard Keys row.">
     Remap keyboard keys
   </message>
-  <message name="IDS_SETTINGS_KEYBOARD_REMAP_KEYS_ROW_SUB_LABEL" translateable="false" desc="Sub-label with number of remapped keys for the Remap Keyboard Keys row.">
+  <message name="IDS_SETTINGS_KEYBOARD_REMAP_KEYS_ROW_SUB_LABEL" desc="Sub-label with number of remapped keys for the Remap Keyboard Keys row.">
     {COUNT, plural,
      =1 {1 remapped key}
      other {{COUNT} remapped keys}}
   </message>
-  <message name="IDS_SETTINGS_KEYBOARD_REMAP_KEYS_DESCRIPTION" translateable="false" desc="In Keyboard remap keys subpage, the description label above remapping keys to describe the actions user can perform.">
+  <message name="IDS_SETTINGS_KEYBOARD_REMAP_KEYS_DESCRIPTION" desc="In Keyboard remap keys subpage, the description label above remapping keys to describe the actions user can perform.">
     For each key listed below, choose the action you want it to perform
   </message>
-  <message name="IDS_SETTINGS_KEYBOARD_SEND_INVERTED_FUNCTION_KEYS" translateable="false" desc="In Device Settings, the inverted checkbox label for interpreting the top-row keys as function keys instead.">
+  <message name="IDS_SETTINGS_KEYBOARD_SEND_INVERTED_FUNCTION_KEYS" desc="In Device Settings, the inverted checkbox label for interpreting the top-row keys as function keys instead.">
     Convert F keys to ChromeOS top-row keys
   </message>
-  <message name="IDS_SETTINGS_KEYBOARD_SEND_INVERTED_FUNCTION_KEYS_DESCRIPTION" translateable="false" desc="In Device Settings, the inverted label describing how to use the top-row keys' original actions when they are set to behave like function keys.">
+  <message name="IDS_SETTINGS_KEYBOARD_SEND_INVERTED_FUNCTION_KEYS_DESCRIPTION" desc="In Device Settings, the inverted label describing how to use the top-row keys' original actions when they are set to behave like function keys.">
     Change the behavior of F keys to ChromeOS top-row actions
   </message>
-  <message name="IDS_SETTINGS_KEYBOARD_BLOCK_META_FUNCTION_KEY_REWRITES" translateable="false" desc="In Device Settings, the checkbox label to enable the meta key to switch the behavior of the top-row keys.">
+  <message name="IDS_SETTINGS_KEYBOARD_BLOCK_META_FUNCTION_KEY_REWRITES" desc="In Device Settings, the checkbox label to enable the meta key to switch the behavior of the top-row keys.">
     Enable System/Launcher key to switch the behavior of the top-row keys
   </message>
-  <message name="IDS_SETTINGS_KEYBOARD_BLOCK_META_FUNCTION_KEY_REWRITES_DESCRIPTION" translateable="false" desc="In Device Settings, the label describing how to use the meta key to switch the behavior of the top-row keys.">
+  <message name="IDS_SETTINGS_KEYBOARD_BLOCK_META_FUNCTION_KEY_REWRITES_DESCRIPTION" desc="In Device Settings, the label describing how to use the meta key to switch the behavior of the top-row keys.">
     Hold the key to switch between F keys and ChromeOS actions
   </message>
-  <message name="IDS_SETTINGS_KEYBOARD_REMAP_RESTORE_BUTTON_LABEL" translateable="false" desc="In Keyboard remap keys subpage, the restore defaults button label.">
+  <message name="IDS_SETTINGS_KEYBOARD_REMAP_RESTORE_BUTTON_LABEL" desc="In Keyboard remap keys subpage, the restore defaults button label.">
     Restore defaults
   </message>
-  <message name="IDS_SETTINGS_KEYBOARD_NO_KEYBOARDS_HELP_MESSAGE" translateable="false" desc="In Keyboard remap keys subpage, the message that notifies users that no keyboards are connected.">
+  <message name="IDS_SETTINGS_KEYBOARD_NO_KEYBOARDS_HELP_MESSAGE" desc="In Keyboard remap keys subpage, the message that notifies users that no keyboards are connected.">
     No keyboard detected
   </message>
 
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_BUILT_IN_KEYBOARD_NAME.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_BUILT_IN_KEYBOARD_NAME.png.sha1
new file mode 100644
index 0000000..48c5bb9
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_BUILT_IN_KEYBOARD_NAME.png.sha1
@@ -0,0 +1 @@
+be1a3286e36c52767417e91af37fbf6c9753b079
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_BUILT_IN_POINTING_STICK_NAME.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_BUILT_IN_POINTING_STICK_NAME.png.sha1
new file mode 100644
index 0000000..1266392
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_BUILT_IN_POINTING_STICK_NAME.png.sha1
@@ -0,0 +1 @@
+2b5bb06cba628deb00f9c6b2234a55ea6e83b065
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_BUILT_IN_TOUCHPAD_NAME.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_BUILT_IN_TOUCHPAD_NAME.png.sha1
new file mode 100644
index 0000000..f920383
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_BUILT_IN_TOUCHPAD_NAME.png.sha1
@@ -0,0 +1 @@
+3a3f8f5128332d21f9ea90a87e571a13980aa180
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_KEYBOARD_BLOCK_META_FUNCTION_KEY_REWRITES.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_KEYBOARD_BLOCK_META_FUNCTION_KEY_REWRITES.png.sha1
new file mode 100644
index 0000000..48c5bb9
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_KEYBOARD_BLOCK_META_FUNCTION_KEY_REWRITES.png.sha1
@@ -0,0 +1 @@
+be1a3286e36c52767417e91af37fbf6c9753b079
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_KEYBOARD_BLOCK_META_FUNCTION_KEY_REWRITES_DESCRIPTION.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_KEYBOARD_BLOCK_META_FUNCTION_KEY_REWRITES_DESCRIPTION.png.sha1
new file mode 100644
index 0000000..48c5bb9
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_KEYBOARD_BLOCK_META_FUNCTION_KEY_REWRITES_DESCRIPTION.png.sha1
@@ -0,0 +1 @@
+be1a3286e36c52767417e91af37fbf6c9753b079
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_KEYBOARD_NO_KEYBOARDS_HELP_MESSAGE.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_KEYBOARD_NO_KEYBOARDS_HELP_MESSAGE.png.sha1
new file mode 100644
index 0000000..7a0c4a5
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_KEYBOARD_NO_KEYBOARDS_HELP_MESSAGE.png.sha1
@@ -0,0 +1 @@
+52a7a581c63cca9059fea5d678a96f7c7c8e1530
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_KEYBOARD_REMAP_KEYS_DESCRIPTION.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_KEYBOARD_REMAP_KEYS_DESCRIPTION.png.sha1
new file mode 100644
index 0000000..0ee0fd2
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_KEYBOARD_REMAP_KEYS_DESCRIPTION.png.sha1
@@ -0,0 +1 @@
+17aa5d2dbef604163b7aee42c994defe9e1b4a64
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_KEYBOARD_REMAP_KEYS_ROW_LABEL.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_KEYBOARD_REMAP_KEYS_ROW_LABEL.png.sha1
new file mode 100644
index 0000000..48c5bb9
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_KEYBOARD_REMAP_KEYS_ROW_LABEL.png.sha1
@@ -0,0 +1 @@
+be1a3286e36c52767417e91af37fbf6c9753b079
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_KEYBOARD_REMAP_KEYS_ROW_SUB_LABEL.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_KEYBOARD_REMAP_KEYS_ROW_SUB_LABEL.png.sha1
new file mode 100644
index 0000000..41ed761
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_KEYBOARD_REMAP_KEYS_ROW_SUB_LABEL.png.sha1
@@ -0,0 +1 @@
+61b2cffc5c3c4ef8c0a15d739a7a89620d205272
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_KEYBOARD_REMAP_RESTORE_BUTTON_LABEL.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_KEYBOARD_REMAP_RESTORE_BUTTON_LABEL.png.sha1
new file mode 100644
index 0000000..9dd935e
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_KEYBOARD_REMAP_RESTORE_BUTTON_LABEL.png.sha1
@@ -0,0 +1 @@
+3f5b7bf6daf0d7c3e2b890ac5173c26444bd07b6
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_KEYBOARD_SEND_INVERTED_FUNCTION_KEYS.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_KEYBOARD_SEND_INVERTED_FUNCTION_KEYS.png.sha1
new file mode 100644
index 0000000..48c5bb9
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_KEYBOARD_SEND_INVERTED_FUNCTION_KEYS.png.sha1
@@ -0,0 +1 @@
+be1a3286e36c52767417e91af37fbf6c9753b079
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_KEYBOARD_SEND_INVERTED_FUNCTION_KEYS_DESCRIPTION.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_KEYBOARD_SEND_INVERTED_FUNCTION_KEYS_DESCRIPTION.png.sha1
new file mode 100644
index 0000000..48c5bb9
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_KEYBOARD_SEND_INVERTED_FUNCTION_KEYS_DESCRIPTION.png.sha1
@@ -0,0 +1 @@
+be1a3286e36c52767417e91af37fbf6c9753b079
\ No newline at end of file
diff --git a/chrome/app/settings_chromium_strings.grdp b/chrome/app/settings_chromium_strings.grdp
index 850e42d6..f3d9170 100644
--- a/chrome/app/settings_chromium_strings.grdp
+++ b/chrome/app/settings_chromium_strings.grdp
@@ -11,6 +11,12 @@
   <message name="IDS_SETTINGS_ABOUT_PROGRAM" desc="Menu title for the About Chromium page.">
     About Chromium
   </message>
+  <message name="IDS_SETTINGS_GET_THE_MOST_OUT_OF_PROGRAM" desc="Section title for the 'Get the most out of Chromium' page.">
+    Get the most ouf of Chromium
+  </message>
+  <message name="IDS_SETTINGS_GET_THE_MOST_OUT_OF_PROGRAM_DESCRIPTION" desc="Explanatory text for the link to the 'Get the most out of Chromium' page.">
+    This guide helps you understand your choices, so that Chromium works the way you want to
+  </message>
   <message name="IDS_SETTINGS_GET_HELP_USING_CHROME" desc="Text of the button which takes the user to the Chrome help page.">
     Get help with Chromium
   </message>
diff --git a/chrome/app/settings_chromium_strings_grdp/IDS_SETTINGS_GET_THE_MOST_OUT_OF_PROGRAM.png.sha1 b/chrome/app/settings_chromium_strings_grdp/IDS_SETTINGS_GET_THE_MOST_OUT_OF_PROGRAM.png.sha1
new file mode 100644
index 0000000..9cdb789
--- /dev/null
+++ b/chrome/app/settings_chromium_strings_grdp/IDS_SETTINGS_GET_THE_MOST_OUT_OF_PROGRAM.png.sha1
@@ -0,0 +1 @@
+2eb8c42afba366b6db5ca4bf4043d5665f18cd74
\ No newline at end of file
diff --git a/chrome/app/settings_chromium_strings_grdp/IDS_SETTINGS_GET_THE_MOST_OUT_OF_PROGRAM_DESCRIPTION.png.sha1 b/chrome/app/settings_chromium_strings_grdp/IDS_SETTINGS_GET_THE_MOST_OUT_OF_PROGRAM_DESCRIPTION.png.sha1
new file mode 100644
index 0000000..9cdb789
--- /dev/null
+++ b/chrome/app/settings_chromium_strings_grdp/IDS_SETTINGS_GET_THE_MOST_OUT_OF_PROGRAM_DESCRIPTION.png.sha1
@@ -0,0 +1 @@
+2eb8c42afba366b6db5ca4bf4043d5665f18cd74
\ No newline at end of file
diff --git a/chrome/app/settings_google_chrome_strings.grdp b/chrome/app/settings_google_chrome_strings.grdp
index 2fd0bbe..242c3c7 100644
--- a/chrome/app/settings_google_chrome_strings.grdp
+++ b/chrome/app/settings_google_chrome_strings.grdp
@@ -11,6 +11,12 @@
   <message name="IDS_SETTINGS_ABOUT_PROGRAM" desc="Menu title for the About Chrome page.">
     About Chrome
   </message>
+  <message name="IDS_SETTINGS_GET_THE_MOST_OUT_OF_PROGRAM" desc="Section title for the 'Get the most out of Chrome' page.">
+    Get the most ouf of Chrome
+  </message>
+  <message name="IDS_SETTINGS_GET_THE_MOST_OUT_OF_PROGRAM_DESCRIPTION" desc="Explanatory text for the link to the 'Get the most out of Chrome' page.">
+    This guide helps you understand your choices, so that Chrome works the way you want to
+  </message>
   <message name="IDS_SETTINGS_GET_HELP_USING_CHROME" desc="Text of the button which takes the user to the Chrome help page.">
     Get help with Chrome
   </message>
diff --git a/chrome/app/settings_google_chrome_strings_grdp/IDS_SETTINGS_GET_THE_MOST_OUT_OF_PROGRAM.png.sha1 b/chrome/app/settings_google_chrome_strings_grdp/IDS_SETTINGS_GET_THE_MOST_OUT_OF_PROGRAM.png.sha1
new file mode 100644
index 0000000..5f2b1ea
--- /dev/null
+++ b/chrome/app/settings_google_chrome_strings_grdp/IDS_SETTINGS_GET_THE_MOST_OUT_OF_PROGRAM.png.sha1
@@ -0,0 +1 @@
+8a9fef939fbfd6a1b92873abfd5ef41fb3d08203
\ No newline at end of file
diff --git a/chrome/app/settings_google_chrome_strings_grdp/IDS_SETTINGS_GET_THE_MOST_OUT_OF_PROGRAM_DESCRIPTION.png.sha1 b/chrome/app/settings_google_chrome_strings_grdp/IDS_SETTINGS_GET_THE_MOST_OUT_OF_PROGRAM_DESCRIPTION.png.sha1
new file mode 100644
index 0000000..5f2b1ea
--- /dev/null
+++ b/chrome/app/settings_google_chrome_strings_grdp/IDS_SETTINGS_GET_THE_MOST_OUT_OF_PROGRAM_DESCRIPTION.png.sha1
@@ -0,0 +1 @@
+8a9fef939fbfd6a1b92873abfd5ef41fb3d08203
\ No newline at end of file
diff --git a/chrome/app/settings_strings.grdp b/chrome/app/settings_strings.grdp
index df00e47b..9fcac612 100644
--- a/chrome/app/settings_strings.grdp
+++ b/chrome/app/settings_strings.grdp
@@ -91,6 +91,12 @@
   <message name="IDS_SETTINGS_CAPTIONS_ENABLE_LIVE_CAPTION_TITLE" desc="Name of the setting to enable Live Caption feature.">
     Live Caption
   </message>
+  <message name="IDS_SETTINGS_CAPTIONS_ENABLE_LIVE_TRANSLATE_TITLE" desc="Name of the setting to enable Live Translate feature.">
+    Live Translate
+  </message>
+  <message name="IDS_SETTINGS_CAPTIONS_ENABLE_LIVE_TRANSLATE_SUBTITLE" desc="Description text for Live Translate feature.">
+    Automatically translate captions to a target language.
+  </message>
   <if expr="chromeos_ash">
     <message name="IDS_SETTINGS_CAPTIONS_ENABLE_LIVE_CAPTION_SUBTITLE_ENGLISH_ONLY" desc="Description text for Live Caption feature. When audio or video is playing, the user will see captions on the screen. Only works in Chrome browser initially. Currently only works for English media.">
       Automatically creates captions for media in Chrome browser (currently available in English). Audio and captions are processed locally and never leave the device.
@@ -1565,6 +1571,9 @@
   <message name="IDS_SETTINGS_LANGUAGES_MANAGE_LANGUAGES_TITLE" desc="Name of the settings dialog which allows enabling additional languages.">
     Add languages
   </message>
+  <message name="IDS_SETTINGS_LANGUAGES_NO_LANGUAGES_ADDED" desc="Placeholder for language list settings when no languages have been added, on the Manage Languages page.">
+    No languages added
+  </message>
   <if expr="not chromeos_ash">
     <message name="IDS_SETTINGS_LANGUAGES_EXPAND_ACCESSIBILITY_LABEL" desc="Label for the button that toggles showing the language options. Only visible by screen reader software.">
       Show language options
@@ -1590,9 +1599,6 @@
     <message name="IDS_SETTINGS_LANGUAGES_MANAGED_DIALOG_BODY" desc="Body text for the dialog informing users that the language they tried modifying is managed.">
       Your administrator has set a default language which cannot be modified.
     </message>
-    <message name="IDS_SETTINGS_LANGUAGES_NO_LANGUAGES_ADDED" desc="Placeholder for language list settings when no languages have been added, on the Manage Languages page.">
-      No languages added
-    </message>
     <message name="IDS_SETTINGS_LANGUAGES_ADD_ARIA_LABEL" desc="Text read by screen readers when focusing on the checkbox for adding a language. This text is only announced by screen readers and is not visible in the UI.">
       Add <ph name="LANGUAGE_NAME">$1<ex>Swahili</ex></ph>
     </message>
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_CAPTIONS_ENABLE_LIVE_TRANSLATE_SUBTITLE.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_CAPTIONS_ENABLE_LIVE_TRANSLATE_SUBTITLE.png.sha1
new file mode 100644
index 0000000..5d87310
--- /dev/null
+++ b/chrome/app/settings_strings_grdp/IDS_SETTINGS_CAPTIONS_ENABLE_LIVE_TRANSLATE_SUBTITLE.png.sha1
@@ -0,0 +1 @@
+2c2b88390f54b609450727c0b629efda2472d3c5
\ No newline at end of file
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_CAPTIONS_ENABLE_LIVE_TRANSLATE_TITLE.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_CAPTIONS_ENABLE_LIVE_TRANSLATE_TITLE.png.sha1
new file mode 100644
index 0000000..5d87310
--- /dev/null
+++ b/chrome/app/settings_strings_grdp/IDS_SETTINGS_CAPTIONS_ENABLE_LIVE_TRANSLATE_TITLE.png.sha1
@@ -0,0 +1 @@
+2c2b88390f54b609450727c0b629efda2472d3c5
\ No newline at end of file
diff --git a/chrome/app/shared_settings_strings.grdp b/chrome/app/shared_settings_strings.grdp
index 2fa012f..df803d31 100644
--- a/chrome/app/shared_settings_strings.grdp
+++ b/chrome/app/shared_settings_strings.grdp
@@ -94,6 +94,15 @@
   <message name="IDS_SETTINGS_CAPTIONS_DEFAULT_SETTING" desc="Name of the default setting for the caption text.">
     Default
   </message>
+  <message name="IDS_SETTINGS_CAPTIONS_LANGUAGE" desc="Name of the language setting for the caption text.">
+    Caption language
+  </message>
+  <message name="IDS_SETTINGS_CAPTIONS_MANAGE_LANGUAGES_TITLE" desc="Name of the caption settings section for managing language packs.">
+    Manage languages
+  </message>
+  <message name="IDS_SETTINGS_CAPTIONS_LIVE_TRANSLATE_TARGET_LANGUAGE" desc="Description of the target language setting for the Live Translate feature.">
+    Translate to
+  </message>
 
   <!-- Nearby Share -->
   <message name="IDS_SETTINGS_NEARBY_SHARE_TITLE" desc="Name of the settings page for the Nearby Share feature">
diff --git a/chrome/app/shared_settings_strings_grdp/IDS_SETTINGS_CAPTIONS_LANGUAGE.png.sha1 b/chrome/app/shared_settings_strings_grdp/IDS_SETTINGS_CAPTIONS_LANGUAGE.png.sha1
new file mode 100644
index 0000000..5d87310
--- /dev/null
+++ b/chrome/app/shared_settings_strings_grdp/IDS_SETTINGS_CAPTIONS_LANGUAGE.png.sha1
@@ -0,0 +1 @@
+2c2b88390f54b609450727c0b629efda2472d3c5
\ No newline at end of file
diff --git a/chrome/app/shared_settings_strings_grdp/IDS_SETTINGS_CAPTIONS_LIVE_TRANSLATE_TARGET_LANGUAGE.png.sha1 b/chrome/app/shared_settings_strings_grdp/IDS_SETTINGS_CAPTIONS_LIVE_TRANSLATE_TARGET_LANGUAGE.png.sha1
new file mode 100644
index 0000000..5d87310
--- /dev/null
+++ b/chrome/app/shared_settings_strings_grdp/IDS_SETTINGS_CAPTIONS_LIVE_TRANSLATE_TARGET_LANGUAGE.png.sha1
@@ -0,0 +1 @@
+2c2b88390f54b609450727c0b629efda2472d3c5
\ No newline at end of file
diff --git a/chrome/app/shared_settings_strings_grdp/IDS_SETTINGS_CAPTIONS_MANAGE_LANGUAGES_TITLE.png.sha1 b/chrome/app/shared_settings_strings_grdp/IDS_SETTINGS_CAPTIONS_MANAGE_LANGUAGES_TITLE.png.sha1
new file mode 100644
index 0000000..5d87310
--- /dev/null
+++ b/chrome/app/shared_settings_strings_grdp/IDS_SETTINGS_CAPTIONS_MANAGE_LANGUAGES_TITLE.png.sha1
@@ -0,0 +1 @@
+2c2b88390f54b609450727c0b629efda2472d3c5
\ No newline at end of file
diff --git a/chrome/app_shim/app_shim_controller.mm b/chrome/app_shim/app_shim_controller.mm
index 0df1f4aa..51d9451a 100644
--- a/chrome/app_shim/app_shim_controller.mm
+++ b/chrome/app_shim/app_shim_controller.mm
@@ -182,7 +182,20 @@
             runningApplicationWithProcessIdentifier:chrome_pid],
         base::scoped_policy::RETAIN);
     if (!chrome_to_connect_to_) {
-      LOG(FATAL) << "Failed to open process with PID: " << chrome_pid;
+      // Sometimes runningApplicationWithProcessIdentifier fails to return the
+      // application, even though it exists. If that happens, try to find the
+      // running application in the full list of running applications manually.
+      // See https://crbug.com/1426897.
+      NSArray<NSRunningApplication*>* apps =
+          [NSWorkspace sharedWorkspace].runningApplications;
+      for (unsigned i = 0; i < [apps count]; ++i) {
+        if (apps[i].processIdentifier == chrome_pid) {
+          chrome_to_connect_to_.reset(apps[i], base::scoped_policy::RETAIN);
+        }
+      }
+      if (!chrome_to_connect_to_) {
+        LOG(FATAL) << "Failed to open process with PID: " << chrome_pid;
+      }
     }
 
     return true;
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index bbd45870..4732c84 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -9833,6 +9833,13 @@
      FEATURE_VALUE_TYPE(ash::features::kDeskButton)},
 #endif
 
+#if !BUILDFLAG(IS_ANDROID)
+    {"settings-enable-get-the-most-out-of-program",
+     flag_descriptions::kSettingsEnableGetTheMostOutOfProgramName,
+     flag_descriptions::kSettingsEnableGetTheMostOutOfProgramDescription,
+     kOsDesktop, FEATURE_VALUE_TYPE(::features::kGetTheMostOutOfProgram)},
+#endif
+
     // NOTE: Adding a new flag requires adding a corresponding entry to enum
     // "LoginCustomFlags" in tools/metrics/histograms/enums.xml. See "Flag
     // Histograms" in tools/metrics/histograms/README.md (run the
diff --git a/chrome/browser/apps/app_service/app_service_proxy_ash.cc b/chrome/browser/apps/app_service/app_service_proxy_ash.cc
index 2c3b23b..ea61d19 100644
--- a/chrome/browser/apps/app_service/app_service_proxy_ash.cc
+++ b/chrome/browser/apps/app_service/app_service_proxy_ash.cc
@@ -230,6 +230,15 @@
     }
   }
 
+  // Close uninstall dialogs for any uninstalled apps.
+  for (const AppPtr& delta : deltas) {
+    if (delta->readiness != Readiness::kUnknown &&
+        !apps_util::IsInstalled(delta->readiness) &&
+        base::Contains(uninstall_dialogs_, delta->app_id)) {
+      uninstall_dialogs_[delta->app_id]->CloseDialog();
+    }
+  }
+
   if (crosapi_subscriber_) {
     crosapi_subscriber_->OnApps(deltas, app_type, should_notify_initialized);
   }
diff --git a/chrome/browser/apps/app_service/uninstall_dialog.cc b/chrome/browser/apps/app_service/uninstall_dialog.cc
index 041c8a6..0dd2bda 100644
--- a/chrome/browser/apps/app_service/uninstall_dialog.cc
+++ b/chrome/browser/apps/app_service/uninstall_dialog.cc
@@ -11,6 +11,7 @@
 #include "components/services/app_service/public/cpp/icon_loader.h"
 #include "extensions/browser/uninstall_reason.h"
 #include "ui/views/native_window_tracker.h"
+#include "ui/views/widget/widget.h"
 
 namespace {
 
@@ -62,6 +63,15 @@
                      weak_ptr_factory_.GetWeakPtr()));
 }
 
+void UninstallDialog::CloseDialog() {
+  if (widget_) {
+    widget_->CloseWithReason(views::Widget::ClosedReason::kUnspecified);
+    return;
+  }
+
+  OnDialogClosed(false, false, false);
+}
+
 views::Widget* UninstallDialog::GetWidget() {
   return widget_;
 }
@@ -69,6 +79,7 @@
 void UninstallDialog::OnDialogClosed(bool uninstall,
                                      bool clear_site_data,
                                      bool report_abuse) {
+  CHECK(uninstall_callback_);
   std::move(uninstall_callback_)
       .Run(uninstall, clear_site_data, report_abuse, this);
 }
diff --git a/chrome/browser/apps/app_service/uninstall_dialog.h b/chrome/browser/apps/app_service/uninstall_dialog.h
index 5a58c0b4..c6180e6 100644
--- a/chrome/browser/apps/app_service/uninstall_dialog.h
+++ b/chrome/browser/apps/app_service/uninstall_dialog.h
@@ -101,6 +101,11 @@
   // the dialog view.
   void PrepareToShow(IconKey icon_key, apps::IconLoader* icon_loader);
 
+  // Closes this dialog if it is open. If the dialog is not open yet because
+  // icons are still loading, immediately runs `uninstall_callback_` so that
+  // `this` can be deleted.
+  void CloseDialog();
+
   views::Widget* GetWidget();
 
   // Called when the uninstall dialog is closing to process uninstall or cancel
diff --git a/chrome/browser/ash/app_list/search/burn_in_controller.cc b/chrome/browser/ash/app_list/search/burn_in_controller.cc
index 39ea509b..be6e7c9 100644
--- a/chrome/browser/ash/app_list/search/burn_in_controller.cc
+++ b/chrome/browser/ash/app_list/search/burn_in_controller.cc
@@ -15,10 +15,6 @@
     : burn_in_period_elapsed_callback_(std::move(callback)),
       burn_in_period_(kBurnInPeriod) {}
 
-bool BurnInController::is_post_burn_in() {
-  return base::Time::Now() - session_start_ > burn_in_period_;
-}
-
 void BurnInController::Start() {
   burn_in_timer_.Start(FROM_HERE, burn_in_period_,
                        burn_in_period_elapsed_callback_);
@@ -34,10 +30,13 @@
   burn_in_timer_.Stop();
 }
 
-void BurnInController::UpdateResults(ResultsMap& results,
+bool BurnInController::UpdateResults(ResultsMap& results,
                                      CategoriesList& categories,
                                      ash::AppListSearchResultType result_type) {
-  if (is_post_burn_in()) {
+  // True if the burn-in period has elapsed.
+  const bool is_post_burn_in =
+      base::Time::Now() - session_start_ > burn_in_period_;
+  if (is_post_burn_in) {
     ++burn_in_iteration_counter_;
   }
 
@@ -71,6 +70,8 @@
       ids_to_burn_in_iteration_[result_id] = burn_in_iteration_counter_;
     }
   }
+
+  return is_post_burn_in;
 }
 
 }  // namespace app_list
diff --git a/chrome/browser/ash/app_list/search/burn_in_controller.h b/chrome/browser/ash/app_list/search/burn_in_controller.h
index e474c17..22fdfc9 100644
--- a/chrome/browser/ash/app_list/search/burn_in_controller.h
+++ b/chrome/browser/ash/app_list/search/burn_in_controller.h
@@ -15,7 +15,7 @@
 namespace app_list {
 
 // Manages operations related to the burn-in period. Owned by the
-// SearchControllerImplNew.
+// SearchController.
 class BurnInController {
  public:
   using BurnInPeriodElapsedCallback = base::RepeatingCallback<void()>;
@@ -26,9 +26,6 @@
   BurnInController(const BurnInController&) = delete;
   BurnInController& operator=(const BurnInController&) = delete;
 
-  // True if the burn-in period has elapsed.
-  bool is_post_burn_in();
-
   // Called at the beginning of a query search. Initiates the burn-in/ period.
   void Start();
 
@@ -43,11 +40,14 @@
   //
   // Performs house-keeping related to burn-in iteration numbers for categories
   // and individual results. These are later important for sorting purposes in
-  // the SearchControllerImplNew - see further documentation below.
+  // the SearchController - see further documentation below.
   //
   // Triggers the BurnInPeriodElapsedCallback if it is the first time
   // UpdateResults() has been called since the burn-in period has elapsed.
-  void UpdateResults(ResultsMap& results,
+  //
+  // Returns true if results are updated before `burn_in_period_`, and false
+  // otherwise.
+  bool UpdateResults(ResultsMap& results,
                      CategoriesList& categories,
                      ash::AppListSearchResultType result_type);
 
diff --git a/chrome/browser/ash/app_list/search/search_controller.cc b/chrome/browser/ash/app_list/search/search_controller.cc
index c93ebd1..12543d3 100644
--- a/chrome/browser/ash/app_list/search/search_controller.cc
+++ b/chrome/browser/ash/app_list/search/search_controller.cc
@@ -276,11 +276,11 @@
     metrics_manager_->OnSearchResultsUpdated(result->scoring());
   }
 
-  burn_in_controller_->UpdateResults(results_, categories_,
-                                     provider->ResultType());
+  bool is_post_burn_in = burn_in_controller_->UpdateResults(
+      results_, categories_, provider->ResultType());
   // If the burn-in period has not yet elapsed, don't call Publish here (this
   // case is covered by a call scheduled within the burn-in controller).
-  if (!last_query_.empty() && burn_in_controller_->is_post_burn_in()) {
+  if (!last_query_.empty() && is_post_burn_in) {
     Publish();
   }
 }
diff --git a/chrome/browser/ash/arc/arc_optin_uma.cc b/chrome/browser/ash/arc/arc_optin_uma.cc
index 7841f40..9ea6d88 100644
--- a/chrome/browser/ash/arc/arc_optin_uma.cc
+++ b/chrome/browser/ash/arc/arc_optin_uma.cc
@@ -10,8 +10,10 @@
 #include "ash/components/arc/metrics/stability_metrics_manager.h"
 #include "ash/components/arc/mojom/app.mojom.h"
 #include "ash/components/arc/mojom/auth.mojom.h"
+#include "ash/shell.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/metrics/histogram_macros.h"
+#include "base/metrics/histogram_macros_local.h"
 #include "chrome/browser/ash/arc/arc_util.h"
 #include "chrome/browser/ash/arc/policy/arc_policy_util.h"
 #include "chrome/browser/ash/arc/session/arc_provisioning_result.h"
@@ -159,10 +161,19 @@
 void UpdateArcUiAvailableTime(const base::TimeDelta& elapsed_time,
                               const std::string& mode,
                               const Profile* profile) {
+  if (ash::Shell::HasInstance()) {
+    ash::Shell::Get()
+        ->login_unlock_throughput_recorder()
+        ->ArcUiAvailableAfterLogin();
+  }
   base::UmaHistogramCustomTimes(
       GetHistogramNameByUserType("Arc.UiAvailable." + mode + ".TimeDelta",
                                  profile),
       elapsed_time, base::Seconds(1), base::Minutes(5), 50);
+
+  // This is local test-only histogram.
+  LOCAL_HISTOGRAM_CUSTOM_TIMES("Arc.Tast.UiAvailable.TimeDelta", elapsed_time,
+                               base::Seconds(1), base::Minutes(5), 50);
 }
 
 void UpdatePlayStoreLaunchTime(const base::TimeDelta& elapsed_time) {
diff --git a/chrome/browser/ash/arc/policy/arc_policy_bridge.cc b/chrome/browser/ash/arc/policy/arc_policy_bridge.cc
index 8f389b8c..65602fa 100644
--- a/chrome/browser/ash/arc/policy/arc_policy_bridge.cc
+++ b/chrome/browser/ash/arc/policy/arc_policy_bridge.cc
@@ -510,6 +510,11 @@
 
 ArcPolicyBridge::~ArcPolicyBridge() {
   VLOG(2) << "ArcPolicyBridge::~ArcPolicyBridge";
+  if (is_policy_service_observed) {
+    policy_service_->RemoveObserver(policy::POLICY_DOMAIN_CHROME, this);
+    is_policy_service_observed = false;
+    policy_service_ = nullptr;
+  }
   arc::ArcSessionManager* arc_session_manager = arc::ArcSessionManager::Get();
   // It can be null in unittests
   if (arc_session_manager) {
diff --git a/chrome/browser/ash/eche_app/eche_app_manager_factory_unittest.cc b/chrome/browser/ash/eche_app/eche_app_manager_factory_unittest.cc
index bf4345a..070754f 100644
--- a/chrome/browser/ash/eche_app/eche_app_manager_factory_unittest.cc
+++ b/chrome/browser/ash/eche_app/eche_app_manager_factory_unittest.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "chrome/browser/ash/eche_app/eche_app_manager_factory.h"
+#include <memory>
 
 #include "ash/constants/ash_features.h"
 #include "ash/system/eche/eche_tray.h"
@@ -47,6 +48,9 @@
     DCHECK(profile_);
     DCHECK(test_web_view_factory_.get());
     ChromeAshTestBase::SetUp();
+    connection_handler_ = std::make_unique<EcheConnectionStatusHandler>();
+    apps_launch_info_provider_ =
+        std::make_unique<AppsLaunchInfoProvider>(connection_handler_.get());
     eche_tray_ = StatusAreaWidgetTestHelper::GetStatusAreaWidget()->eche_tray();
     phone_hub_tray_ =
         StatusAreaWidgetTestHelper::GetStatusAreaWidget()->phone_hub_tray();
@@ -99,6 +103,9 @@
   }
 
   TestingProfile* GetProfile() { return profile_; }
+  AppsLaunchInfoProvider* GetAppsLaunchInfoProvider() {
+    return apps_launch_info_provider_.get();
+  }
   EcheTray* eche_tray() { return eche_tray_; }
   PhoneHubTray* phone_hub_tray() { return phone_hub_tray_; }
 
@@ -106,6 +113,8 @@
   base::test::ScopedFeatureList scoped_feature_list_;
   std::unique_ptr<TestingProfileManager> profile_manager_;
   TestingProfile* profile_;
+  std::unique_ptr<EcheConnectionStatusHandler> connection_handler_;
+  std::unique_ptr<AppsLaunchInfoProvider> apps_launch_info_provider_;
   EcheTray* eche_tray_ = nullptr;
   PhoneHubTray* phone_hub_tray_ = nullptr;
   EcheAppManagerFactory* eche_app_manager_factory_ = nullptr;
@@ -138,10 +147,16 @@
     DCHECK(profile_);
     DCHECK(test_web_view_factory_.get());
     ChromeAshTestBase::SetUp();
+    connection_handler_ = std::make_unique<EcheConnectionStatusHandler>();
+    apps_launch_info_provider_ =
+        std::make_unique<AppsLaunchInfoProvider>(connection_handler_.get());
     eche_tray_ = StatusAreaWidgetTestHelper::GetStatusAreaWidget()->eche_tray();
   }
 
   TestingProfile* GetProfile() { return profile_; }
+  AppsLaunchInfoProvider* GetAppsLaunchInfoProvider() {
+    return apps_launch_info_provider_.get();
+  }
 
   EcheTray* eche_tray() { return eche_tray_; }
 
@@ -149,6 +164,8 @@
   base::test::ScopedFeatureList scoped_feature_list_;
   std::unique_ptr<TestingProfileManager> profile_manager_;
   TestingProfile* profile_;
+  std::unique_ptr<EcheConnectionStatusHandler> connection_handler_;
+  std::unique_ptr<AppsLaunchInfoProvider> apps_launch_info_provider_;
   EcheTray* eche_tray_ = nullptr;
   // Calling the factory constructor is enough to set it up.
   std::unique_ptr<TestAshWebViewFactory> test_web_view_factory_ =
@@ -160,13 +177,11 @@
   const char16_t visible_name_1[] = u"Fake App 1";
   const char package_name_1[] = "com.fakeapp1";
   const char16_t phone_name[] = u"your phone";
-  auto apps_launch_info_provider = std::make_unique<AppsLaunchInfoProvider>(
-      std::make_unique<EcheConnectionStatusHandler>().get());
 
   EcheAppManagerFactory::LaunchEcheApp(
       GetProfile(), /*notification_id=*/absl::nullopt, package_name_1,
       visible_name_1, user_id, gfx::Image(), phone_name,
-      apps_launch_info_provider.get());
+      GetAppsLaunchInfoProvider());
   // Wait for Eche Tray to load Eche Web to complete
   base::RunLoop().RunUntilIdle();
   // Eche icon should be visible after launch.
@@ -179,7 +194,7 @@
   EcheAppManagerFactory::LaunchEcheApp(
       GetProfile(), /*notification_id=*/absl::nullopt, package_name_2,
       visible_name_2, user_id, gfx::Image(), phone_name,
-      apps_launch_info_provider.get());
+      GetAppsLaunchInfoProvider());
   // Wait for Eche Tray to load Eche Web to complete
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(widget, eche_tray()->GetBubbleWidget());
@@ -191,12 +206,10 @@
   const std::string package_name = "com.fakeapp";
   const gfx::Image icon = gfx::test::CreateImage(100, 100);
   const std::u16string phone_name = u"your phone";
-  auto apps_launch_info_provider = std::make_unique<AppsLaunchInfoProvider>(
-      std::make_unique<EcheConnectionStatusHandler>().get());
 
   EcheAppManagerFactory::LaunchEcheApp(
       GetProfile(), /*notification_id=*/absl::nullopt, package_name,
-      visible_name, user_id, icon, phone_name, apps_launch_info_provider.get());
+      visible_name, user_id, icon, phone_name, GetAppsLaunchInfoProvider());
 
   std::unique_ptr<LaunchedAppInfo> launched_app_info =
       EcheAppManagerFactory::GetInstance()->GetLastLaunchedAppInfo();
@@ -216,13 +229,11 @@
   const char16_t visible_name[] = u"Fake App";
   const char package_name[] = "com.fakeapp";
   const char16_t phone_name[] = u"your phone";
-  auto apps_launch_info_provider = std::make_unique<AppsLaunchInfoProvider>(
-      std::make_unique<EcheConnectionStatusHandler>().get());
 
   EcheAppManagerFactory::LaunchEcheApp(
       GetProfile(), /*notification_id=*/absl::nullopt, package_name,
       visible_name, user_id, gfx::Image(), phone_name,
-      apps_launch_info_provider.get());
+      GetAppsLaunchInfoProvider());
   // Wait for Eche Tray to load Eche Web to complete
   base::RunLoop().RunUntilIdle();
   // Eche tray should be visible when streaming is active, not ative when
diff --git a/chrome/browser/ash/extensions/file_manager/drivefs_event_router.cc b/chrome/browser/ash/extensions/file_manager/drivefs_event_router.cc
index c5e5b8f..848a89b 100644
--- a/chrome/browser/ash/extensions/file_manager/drivefs_event_router.cc
+++ b/chrome/browser/ash/extensions/file_manager/drivefs_event_router.cc
@@ -53,23 +53,25 @@
 DriveFsEventRouter::SyncingStatusState::~SyncingStatusState() = default;
 
 void DriveFsEventRouter::OnUnmounted() {
-  sync_status_state_.completed_bytes = 0;
-  sync_status_state_.group_id_to_bytes_to_transfer.clear();
-  pin_status_state_.completed_bytes = 0;
-  pin_status_state_.group_id_to_bytes_to_transfer.clear();
+  if (!base::FeatureList::IsEnabled(ash::features::kFilesInlineSyncStatus)) {
+    sync_status_state_.completed_bytes = 0;
+    sync_status_state_.group_id_to_bytes_to_transfer.clear();
+    pin_status_state_.completed_bytes = 0;
+    pin_status_state_.group_id_to_bytes_to_transfer.clear();
 
-  // Ensure any existing sync progress indicator is cleared.
-  FileTransferStatus sync_status;
-  sync_status.transfer_state = file_manager_private::TRANSFER_STATE_FAILED;
-  sync_status.show_notification = true;
-  sync_status.hide_when_zero_jobs = true;
-  FileTransferStatus pin_status;
-  pin_status.transfer_state = file_manager_private::TRANSFER_STATE_FAILED;
-  pin_status.show_notification = true;
-  pin_status.hide_when_zero_jobs = true;
+    // Ensure any existing sync progress indicator is cleared.
+    FileTransferStatus sync_status;
+    sync_status.transfer_state = file_manager_private::TRANSFER_STATE_FAILED;
+    sync_status.show_notification = true;
+    sync_status.hide_when_zero_jobs = true;
+    FileTransferStatus pin_status;
+    pin_status.transfer_state = file_manager_private::TRANSFER_STATE_FAILED;
+    pin_status.show_notification = true;
+    pin_status.hide_when_zero_jobs = true;
 
-  BroadcastTransferEvent(kTransferEvent, sync_status);
-  BroadcastTransferEvent(kPinEvent, pin_status);
+    BroadcastTransferEvent(kTransferEvent, sync_status);
+    BroadcastTransferEvent(kPinEvent, pin_status);
+  }
 
   dialog_callback_.Reset();
 }
diff --git a/chrome/browser/ash/extensions/file_manager/drivefs_event_router_unittest.cc b/chrome/browser/ash/extensions/file_manager/drivefs_event_router_unittest.cc
index b0420ff..8df442f 100644
--- a/chrome/browser/ash/extensions/file_manager/drivefs_event_router_unittest.cc
+++ b/chrome/browser/ash/extensions/file_manager/drivefs_event_router_unittest.cc
@@ -183,6 +183,11 @@
 };
 
 class DriveFsEventRouterTest : public testing::Test {
+ public:
+  DriveFsEventRouterTest() {
+    feature_list_.InitWithFeatures({}, {ash::features::kFilesInlineSyncStatus});
+  }
+
  protected:
   void SetUp() override {
     event_router_ = std::make_unique<TestDriveFsEventRouter>();
@@ -212,6 +217,9 @@
   DriveFsEventRouterTestInlineSyncStatus() {
     feature_list_.InitWithFeatures({ash::features::kFilesInlineSyncStatus}, {});
   }
+
+ protected:
+  base::test::ScopedFeatureList feature_list_;
 };
 
 inline void AddEvent(std::vector<drivefs::mojom::ItemEventPtr>& events,
diff --git a/chrome/browser/ash/extensions/file_manager/private_api_tasks.cc b/chrome/browser/ash/extensions/file_manager/private_api_tasks.cc
index 4500f89..640db834 100644
--- a/chrome/browser/ash/extensions/file_manager/private_api_tasks.cc
+++ b/chrome/browser/ash/extensions/file_manager/private_api_tasks.cc
@@ -12,6 +12,7 @@
 #include <utility>
 #include <vector>
 
+#include "ash/webui/system_apps/public/system_web_app_type.h"
 #include "base/command_line.h"
 #include "base/functional/bind.h"
 #include "chrome/browser/ash/drive/file_system_util.h"
@@ -22,6 +23,8 @@
 #include "chrome/browser/ash/fileapi/file_system_backend.h"
 #include "chrome/browser/extensions/chrome_extension_function_details.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/ash/system_web_apps/system_web_app_ui_utils.h"
+#include "chrome/browser/ui/browser_window.h"
 #include "chrome/common/extensions/api/file_manager_private.h"
 #include "chrome/common/extensions/api/file_manager_private_internal.h"
 #include "extensions/browser/api/file_handlers/directory_util.h"
@@ -133,8 +136,11 @@
     urls.push_back(url);
   }
 
+  // Get Files App window, if it exists.
+  Browser* browser =
+      FindSystemWebAppBrowser(profile, ash::SystemWebAppType::FILE_MANAGER);
   gfx::NativeWindow modal_parent =
-      ChromeExtensionFunctionDetails(this).GetNativeWindowForUI();
+      browser ? browser->window()->GetNativeWindow() : nullptr;
 
   const bool result = file_manager::file_tasks::ExecuteFileTask(
       profile, task, urls, modal_parent,
diff --git a/chrome/browser/ash/input_method/assistive_suggester.cc b/chrome/browser/ash/input_method/assistive_suggester.cc
index 7bdd71e..9b6c77b 100644
--- a/chrome/browser/ash/input_method/assistive_suggester.cc
+++ b/chrome/browser/ash/input_method/assistive_suggester.cc
@@ -464,15 +464,17 @@
   }
 
   if (IsLongpressEnabledControlV(current_longpress_keydown_.value())) {
-    current_suggester_ = &longpress_control_v_suggester_;
     const auto anchor_rect =
         IMEBridge::Get()->GetInputContextHandler()->GetTextFieldBounds();
-    Shell::Get()->clipboard_history_controller()->ShowMenu(
-        anchor_rect, ui::MenuSourceType::MENU_SOURCE_KEYBOARD,
-        crosapi::mojom::ClipboardHistoryControllerShowSource::
-            kControlVLongpress,
-        base::BindOnce(&AssistiveSuggester::OnClipboardHistoryMenuClosing,
-                       weak_ptr_factory_.GetWeakPtr()));
+    if (Shell::Get()->clipboard_history_controller()->ShowMenu(
+            anchor_rect, ui::MenuSourceType::MENU_SOURCE_KEYBOARD,
+            crosapi::mojom::ClipboardHistoryControllerShowSource::
+                kControlVLongpress,
+            base::BindOnce(&AssistiveSuggester::OnClipboardHistoryMenuClosing,
+                           weak_ptr_factory_.GetWeakPtr()))) {
+      // Only set `current_suggester_` if the clipboard history menu was shown.
+      current_suggester_ = &longpress_control_v_suggester_;
+    }
   } else if (longpress_diacritics_suggester_.TrySuggestOnLongpress(
                  current_longpress_keydown_->GetCharacter())) {
     current_suggester_ = &longpress_diacritics_suggester_;
diff --git a/chrome/browser/autofill/autofill_interactive_uitest.cc b/chrome/browser/autofill/autofill_interactive_uitest.cc
index 49fc7c6..ac7f415 100644
--- a/chrome/browser/autofill/autofill_interactive_uitest.cc
+++ b/chrome/browser/autofill/autofill_interactive_uitest.cc
@@ -1322,8 +1322,7 @@
 
 // Test the basic form-fill flow.
 // TODO(https://crbug.com/1045709): Check back if flakiness is fixed now.
-IN_PROC_BROWSER_TEST_F(AutofillInteractiveTestWithHistogramTester,
-                       BasicFormFill) {
+IN_PROC_BROWSER_TEST_F(AutofillInteractiveTest, BasicFormFill) {
   CreateTestProfile();
   SetTestUrlResponse(kTestShippingFormString);
   ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetTestUrl()));
@@ -1333,15 +1332,6 @@
                             .after_select = ExpectValues(MergeValue(
                                 kEmptyAddress, {"firstname", "M"}))}));
   EXPECT_THAT(GetFormValues(), ValuesAre(kDefaultAddress));
-
-  ::metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
-  // Assert that the network isolation key is populated for 2 requests:
-  // - Navigation: /internal/test_url_path
-  // - Autofill query: https://clients1.google.com/tbproxy/af/query?...
-  //   or "https://content-autofill.googleapis.com/..." (depending on the
-  //   finch configuration of the AutofillUseApi feature).
-  histogram_tester().ExpectBucketCount("HttpCache.NetworkIsolationKeyPresent2",
-                                       2 /*kPresent*/, 2 /*count*/);
 }
 
 IN_PROC_BROWSER_TEST_F(AutofillInteractiveTest, BasicClear) {
diff --git a/chrome/browser/bluetooth/web_bluetooth_browsertest.cc b/chrome/browser/bluetooth/web_bluetooth_browsertest.cc
index 64f61d4..581374e 100644
--- a/chrome/browser/bluetooth/web_bluetooth_browsertest.cc
+++ b/chrome/browser/bluetooth/web_bluetooth_browsertest.cc
@@ -9,6 +9,7 @@
 #include "base/functional/callback.h"
 #include "base/memory/raw_ptr.h"
 #include "base/metrics/field_trial.h"
+#include "base/metrics/field_trial_params.h"
 #include "base/run_loop.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/task/single_thread_task_runner.h"
@@ -631,8 +632,8 @@
 
   if (base::FieldTrialList::TrialExists("WebBluetoothBlocklist")) {
     LOG(INFO) << "WebBluetoothBlocklist field trial already configured.";
-    ASSERT_NE(variations::GetVariationParamValue("WebBluetoothBlocklist",
-                                                 "blocklist_additions")
+    ASSERT_NE(base::GetFieldTrialParamValue("WebBluetoothBlocklist",
+                                            "blocklist_additions")
                   .find("ed5f25a4"),
               std::string::npos)
         << "ERROR: WebBluetoothBlocklist field trial being tested in\n"
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 44da0bc..cfbed0fd 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -3259,7 +3259,7 @@
   // TODO(crbug.com/598890): Don't disable if
   // base::CommandLine::ForCurrentProcess()->
   // HasSwitch(switches::kEnableWebBluetooth) is true.
-  if (variations::GetVariationParamValue(
+  if (base::GetFieldTrialParamValue(
           permissions::PermissionContextBase::kPermissionsKillSwitchFieldStudy,
           "Bluetooth") ==
       permissions::PermissionContextBase::kPermissionsKillSwitchBlockedValue) {
@@ -3280,8 +3280,8 @@
 }
 
 std::string ChromeContentBrowserClient::GetWebBluetoothBlocklist() {
-  return variations::GetVariationParamValue("WebBluetoothBlocklist",
-                                            "blocklist_additions");
+  return base::GetFieldTrialParamValue("WebBluetoothBlocklist",
+                                       "blocklist_additions");
 }
 
 bool ChromeContentBrowserClient::IsInterestGroupAPIAllowed(
diff --git a/chrome/browser/download/download_browsertest.cc b/chrome/browser/download/download_browsertest.cc
index 789638c..8449967 100644
--- a/chrome/browser/download/download_browsertest.cc
+++ b/chrome/browser/download/download_browsertest.cc
@@ -2611,14 +2611,6 @@
   ASSERT_EQ(url, download_items[0]->GetOriginalUrl());
   ASSERT_EQ(url, download_items[1]->GetOriginalUrl());
 
-  metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
-  // Assert that the NIK is populated for 4 requests:
-  // - Navigation: image.jpg
-  // - favicon.ico
-  // - SavePage: image.jpg
-  // - context menu: image.jpg
-  histogram_tester().ExpectBucketCount("HttpCache.NetworkIsolationKeyPresent2",
-                                       2 /*kPresent*/, 4 /*count*/);
   ResetURLLoaderInterceptor();
 }
 
diff --git a/chrome/browser/extensions/api/content_settings/content_settings_api.cc b/chrome/browser/extensions/api/content_settings/content_settings_api.cc
index 0c97213..0db0e1e 100644
--- a/chrome/browser/extensions/api/content_settings/content_settings_api.cc
+++ b/chrome/browser/extensions/api/content_settings/content_settings_api.cc
@@ -25,6 +25,7 @@
 #include "components/content_settings/core/browser/cookie_settings.h"
 #include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "components/content_settings/core/common/content_settings.h"
+#include "components/content_settings/core/common/content_settings_types.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/common/webplugininfo.h"
@@ -229,6 +230,15 @@
       content_settings::ContentSettingsRegistry::GetInstance()->Get(
           content_type);
 
+  // The ANTI_ABUSE content setting does not support site-specific settings.
+  if (content_type == ContentSettingsType::ANTI_ABUSE &&
+      (primary_pattern != ContentSettingsPattern::Wildcard() ||
+       secondary_pattern != ContentSettingsPattern::Wildcard())) {
+    return RespondNow(
+        Error("Site-specific settings are not allowed for this type. The URL "
+              "pattern must be '<all_urls>'."));
+  }
+
   // Some content setting types support the full set of values listed in
   // content_settings.json only for exceptions. For the default setting,
   // some values might not be supported.
diff --git a/chrome/browser/extensions/api/content_settings/content_settings_apitest.cc b/chrome/browser/extensions/api/content_settings/content_settings_apitest.cc
index d58b5ef..fca8048 100644
--- a/chrome/browser/extensions/api/content_settings/content_settings_apitest.cc
+++ b/chrome/browser/extensions/api/content_settings/content_settings_apitest.cc
@@ -130,6 +130,9 @@
     EXPECT_EQ(CONTENT_SETTING_ALLOW,
               map->GetContentSetting(example_url, example_url,
                                      ContentSettingsType::AUTOPLAY));
+    EXPECT_EQ(CONTENT_SETTING_BLOCK,
+              map->GetContentSetting(example_url, example_url,
+                                     ContentSettingsType::ANTI_ABUSE));
 
     // Check content settings for www.google.com
     GURL url("http://www.google.com");
@@ -160,6 +163,9 @@
                                      ContentSettingsType::AUTOMATIC_DOWNLOADS));
     EXPECT_EQ(CONTENT_SETTING_ALLOW,
               map->GetContentSetting(url, url, ContentSettingsType::AUTOPLAY));
+    EXPECT_EQ(
+        CONTENT_SETTING_BLOCK,
+        map->GetContentSetting(url, url, ContentSettingsType::ANTI_ABUSE));
   }
 
   void CheckContentSettingsDefault() {
@@ -198,6 +204,9 @@
                                      ContentSettingsType::AUTOMATIC_DOWNLOADS));
     EXPECT_EQ(CONTENT_SETTING_ALLOW,
               map->GetContentSetting(url, url, ContentSettingsType::AUTOPLAY));
+    EXPECT_EQ(
+        CONTENT_SETTING_ALLOW,
+        map->GetContentSetting(url, url, ContentSettingsType::ANTI_ABUSE));
   }
 
   // Returns a snapshot of content settings for a given URL.
diff --git a/chrome/browser/feedback/android/BUILD.gn b/chrome/browser/feedback/android/BUILD.gn
index 26f9680..898c99b 100644
--- a/chrome/browser/feedback/android/BUILD.gn
+++ b/chrome/browser/feedback/android/BUILD.gn
@@ -28,38 +28,32 @@
 
 android_library("java") {
   sources = [
-    "java/src/org/chromium/chrome/browser/feedback/AsyncFeedbackSource.java",
     "java/src/org/chromium/chrome/browser/feedback/AsyncFeedbackSourceAdapter.java",
     "java/src/org/chromium/chrome/browser/feedback/DeviceInfoFeedbackSource.java",
     "java/src/org/chromium/chrome/browser/feedback/FamilyInfoFeedbackSource.java",
-    "java/src/org/chromium/chrome/browser/feedback/FeedbackCollector.java",
     "java/src/org/chromium/chrome/browser/feedback/FeedbackContextFeedbackSource.java",
     "java/src/org/chromium/chrome/browser/feedback/FeedbackReporter.java",
-    "java/src/org/chromium/chrome/browser/feedback/FeedbackSource.java",
     "java/src/org/chromium/chrome/browser/feedback/FragmentHelpAndFeedbackLauncher.java",
     "java/src/org/chromium/chrome/browser/feedback/HelpAndFeedbackLauncher.java",
-    "java/src/org/chromium/chrome/browser/feedback/HistogramFeedbackSource.java",
     "java/src/org/chromium/chrome/browser/feedback/IMEFeedbackSource.java",
     "java/src/org/chromium/chrome/browser/feedback/InterestFeedFeedbackSource.java",
     "java/src/org/chromium/chrome/browser/feedback/LowEndDeviceFeedbackSource.java",
     "java/src/org/chromium/chrome/browser/feedback/PermissionFeedbackSource.java",
     "java/src/org/chromium/chrome/browser/feedback/ProcessIdFeedbackSource.java",
-    "java/src/org/chromium/chrome/browser/feedback/ScreenshotSource.java",
-    "java/src/org/chromium/chrome/browser/feedback/StaticScreenshotSource.java",
     "java/src/org/chromium/chrome/browser/feedback/SystemInfoFeedbackSource.java",
     "java/src/org/chromium/chrome/browser/feedback/UrlFeedbackSource.java",
     "java/src/org/chromium/chrome/browser/feedback/VariationsFeedbackSource.java",
   ]
   deps = [
+    ":delegate_java",
+    ":feedback_collector_java",
     ":java_resources",
     "//base:base_java",
     "//base:jni_java",
     "//build/android:build_java",
     "//chrome/browser/flags:java",
     "//chrome/browser/profiles/android:java",
-    "//chrome/browser/signin/services/android:java",
     "//components/browser_ui/util/android:java",
-    "//components/signin/public/android:java",
     "//components/variations/android:variations_java",
     "//content/public/android:content_java",
     "//net/android:net_java",
@@ -67,10 +61,63 @@
     "//third_party/androidx:androidx_annotation_annotation_java",
     "//ui/android:ui_no_recycler_view_java",
   ]
+  public_deps = [
+    ":delegate_java",
+    ":feedback_collector_java",
+  ]
   annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
   resources_package = "org.chromium.chrome.browser.feedback"
 }
 
+android_library("feedback_collector_java") {
+  sources = [
+    "java/src/org/chromium/chrome/browser/feedback/AsyncFeedbackSource.java",
+    "java/src/org/chromium/chrome/browser/feedback/FeedbackCollector.java",
+    "java/src/org/chromium/chrome/browser/feedback/FeedbackSource.java",
+    "java/src/org/chromium/chrome/browser/feedback/HistogramFeedbackSource.java",
+    "java/src/org/chromium/chrome/browser/feedback/ScreenshotSource.java",
+    "java/src/org/chromium/chrome/browser/feedback/StaticScreenshotSource.java",
+  ]
+  deps = [
+    "//base:base_java",
+    "//build/android:build_java",
+    "//chrome/browser/profiles/android:java",
+    "//chrome/browser/signin/services/android:java",
+    "//components/browser_ui/util/android:java",
+    "//components/signin/public/android:java",
+    "//third_party/android_deps:guava_android_java",
+    "//third_party/androidx:androidx_annotation_annotation_java",
+  ]
+}
+
+android_library("delegate_java") {
+  sources = [
+    "java/src/org/chromium/chrome/browser/feedback/HelpAndFeedbackLauncherDelegate.java",
+    "java/src/org/chromium/chrome/browser/feedback/HelpAndFeedbackLauncherDelegateImpl.java",
+  ]
+
+  deps = [
+    ":feedback_collector_java",
+    "//base:base_java",
+    "//third_party/androidx:androidx_annotation_annotation_java",
+  ]
+
+  # Add the actual implementation where necessary so that downstream targets
+  # can provide their own implementations.
+  jar_excluded_patterns = [ "*/HelpAndFeedbackLauncherDelegateImpl.class" ]
+}
+
+android_library("delegate_public_impl_java") {
+  sources = [ "java/src/org/chromium/chrome/browser/feedback/HelpAndFeedbackLauncherDelegateImpl.java" ]
+
+  deps = [
+    ":delegate_java",
+    ":feedback_collector_java",
+    "//base:base_java",
+    "//third_party/androidx:androidx_annotation_annotation_java",
+  ]
+}
+
 android_resources("java_resources") {
   sources = [ "java/res/values/strings.xml" ]
 }
diff --git a/chrome/browser/feedback/android/java/src/org/chromium/chrome/browser/feedback/HelpAndFeedbackLauncherDelegate.java b/chrome/browser/feedback/android/java/src/org/chromium/chrome/browser/feedback/HelpAndFeedbackLauncherDelegate.java
new file mode 100644
index 0000000..663e657e
--- /dev/null
+++ b/chrome/browser/feedback/android/java/src/org/chromium/chrome/browser/feedback/HelpAndFeedbackLauncherDelegate.java
@@ -0,0 +1,54 @@
+// 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.
+
+package org.chromium.chrome.browser.feedback;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.provider.Browser;
+
+import androidx.annotation.NonNull;
+
+/**
+ * Delegate that handles the display of the HelpAndFeedback flows.
+ */
+public interface HelpAndFeedbackLauncherDelegate {
+    static final String FALLBACK_SUPPORT_URL = "https://support.google.com/chrome/topic/6069782";
+
+    /**
+     * Starts an activity showing a help page for the specified context ID.
+     *
+     * @param activity The activity to use for starting the help activity and to take a
+     *                 screenshot of.
+     * @param helpContext One of the CONTEXT_* constants. This should describe the user's current
+     *                    context and will be used to show a more relevant help page.
+     * @param collector the {@link FeedbackCollector} to use for extra data. Must not be null.
+     */
+    void show(Activity activity, String helpContext, @NonNull FeedbackCollector collector);
+
+    /**
+     * Starts an activity prompting the user to enter feedback.
+     *
+     * @param activity The activity to use for starting the feedback activity and to take a
+     *                 screenshot of.
+     * @param collector the {@link FeedbackCollector} to use for extra data. Must not be null.
+     */
+    void showFeedback(Activity activity, @NonNull FeedbackCollector collector);
+
+    /**
+     * Handles the fallback help case of opening the URL in the browser.
+     * @param context The context launching the fallback support.
+     */
+    static void launchFallbackSupportUri(Context context) {
+        Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(FALLBACK_SUPPORT_URL));
+        // Let Chrome know that this intent is from Chrome, so that it does not close the app when
+        // the user presses 'back' button.
+        intent.putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName());
+        intent.putExtra(Browser.EXTRA_CREATE_NEW_TAB, true);
+        intent.setPackage(context.getPackageName());
+        context.startActivity(intent);
+    }
+}
diff --git a/chrome/browser/feedback/android/java/src/org/chromium/chrome/browser/feedback/HelpAndFeedbackLauncherDelegateImpl.java b/chrome/browser/feedback/android/java/src/org/chromium/chrome/browser/feedback/HelpAndFeedbackLauncherDelegateImpl.java
new file mode 100644
index 0000000..a403dee
--- /dev/null
+++ b/chrome/browser/feedback/android/java/src/org/chromium/chrome/browser/feedback/HelpAndFeedbackLauncherDelegateImpl.java
@@ -0,0 +1,32 @@
+// 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.
+
+package org.chromium.chrome.browser.feedback;
+
+import android.app.Activity;
+
+import androidx.annotation.NonNull;
+
+import org.chromium.base.Log;
+
+/**
+ * Basic implementation for displaying help support for Chrome.
+ *
+ * NOTE: This class is designed to be replaced by downstream targets.
+ */
+public class HelpAndFeedbackLauncherDelegateImpl implements HelpAndFeedbackLauncherDelegate {
+    private static final String TAG = "HelpAndFeedback";
+
+    @Override
+    public void show(Activity activity, String helpContext, @NonNull FeedbackCollector collector) {
+        Log.d(TAG, "Feedback data: " + collector.getBundle());
+        HelpAndFeedbackLauncherDelegate.launchFallbackSupportUri(activity);
+    }
+
+    @Override
+    public void showFeedback(Activity activity, @NonNull FeedbackCollector collector) {
+        Log.d(TAG, "Feedback data: " + collector.getBundle());
+        HelpAndFeedbackLauncherDelegate.launchFallbackSupportUri(activity);
+    }
+}
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 195457e6..34f915c 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -6470,6 +6470,11 @@
     "expiry_milestone": 96
   },
   {
+    "name": "settings-enable-get-the-most-out-of-program",
+    "owners": [ "jochen" ],
+    "expiry_milestone": 130
+  },
+  {
     "name": "share-sheet-migration-android",
     "owners": [ "wenyufu" ],
     "expiry_milestone": 121
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 01f5e77b..dd747bf 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -4535,6 +4535,12 @@
     "Enables SCT auditing for users who have opted in to Safe Browsing "
     "Extended Reporting.";
 
+const char kSettingsEnableGetTheMostOutOfProgramName[] =
+    "'Get the most out of Chrome' documentation";
+const char kSettingsEnableGetTheMostOutOfProgramDescription[] =
+    "When enabled, the 'Get the most out of Chrome' documentation section "
+    "will be available.";
+
 const char kSharingDesktopSharePreviewName[] = "Desktop share hub preview";
 const char kSharingDesktopSharePreviewDescription[] =
     "Adds a preview section to the desktop sharing hub to make it clearer what "
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 65ef3cb..441bd3e 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -2607,6 +2607,9 @@
 extern const char kSCTAuditingName[];
 extern const char kSCTAuditingDescription[];
 
+extern const char kSettingsEnableGetTheMostOutOfProgramName[];
+extern const char kSettingsEnableGetTheMostOutOfProgramDescription[];
+
 extern const char kSharingDesktopSharePreviewName[];
 extern const char kSharingDesktopSharePreviewDescription[];
 
diff --git a/chrome/browser/metrics/desktop_session_duration/chrome_visibility_observer.cc b/chrome/browser/metrics/desktop_session_duration/chrome_visibility_observer.cc
index e836dd2..bf28599 100644
--- a/chrome/browser/metrics/desktop_session_duration/chrome_visibility_observer.cc
+++ b/chrome/browser/metrics/desktop_session_duration/chrome_visibility_observer.cc
@@ -6,12 +6,12 @@
 
 #include "base/functional/bind.h"
 #include "base/memory/singleton.h"
+#include "base/metrics/field_trial_params.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/task/single_thread_task_runner.h"
 #include "chrome/browser/metrics/desktop_session_duration/desktop_session_duration_tracker.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_list.h"
-#include "components/variations/variations_associated_data.h"
 
 namespace metrics {
 
@@ -67,7 +67,7 @@
   const int kDefaultVisibilityGapTimeout = 3;
 
   int timeout_seconds = kDefaultVisibilityGapTimeout;
-  std::string param_value = variations::GetVariationParamValue(
+  std::string param_value = base::GetFieldTrialParamValue(
       "DesktopSessionDuration", "visibility_gap_timeout");
   if (!param_value.empty())
     base::StringToInt(param_value, &timeout_seconds);
diff --git a/chrome/browser/metrics/desktop_session_duration/desktop_session_duration_tracker.cc b/chrome/browser/metrics/desktop_session_duration/desktop_session_duration_tracker.cc
index 6eb14e9d..3713470 100644
--- a/chrome/browser/metrics/desktop_session_duration/desktop_session_duration_tracker.cc
+++ b/chrome/browser/metrics/desktop_session_duration/desktop_session_duration_tracker.cc
@@ -6,10 +6,10 @@
 
 #include "base/functional/bind.h"
 #include "base/logging.h"
+#include "base/metrics/field_trial_params.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/observer_list.h"
 #include "base/strings/string_number_conversions.h"
-#include "components/variations/variations_associated_data.h"
 
 namespace metrics {
 
@@ -174,7 +174,7 @@
   const int kDefaultInactivityTimeoutMinutes = 5;
 
   int timeout_minutes = kDefaultInactivityTimeoutMinutes;
-  std::string param_value = variations::GetVariationParamValue(
+  std::string param_value = base::GetFieldTrialParamValue(
       "DesktopSessionDuration", "inactivity_timeout");
   if (!param_value.empty())
     base::StringToInt(param_value, &timeout_minutes);
diff --git a/chrome/browser/new_tab_page/modules/history_clusters/history_clusters_page_handler.cc b/chrome/browser/new_tab_page/modules/history_clusters/history_clusters_page_handler.cc
index a8156a6..5101c25 100644
--- a/chrome/browser/new_tab_page/modules/history_clusters/history_clusters_page_handler.cc
+++ b/chrome/browser/new_tab_page/modules/history_clusters/history_clusters_page_handler.cc
@@ -78,6 +78,17 @@
                                                           categories.end());
 }
 
+int GetMinVisitsToShow() {
+  static int min_visits = base::GetFieldTrialParamByFeatureAsInt(
+      ntp_features::kNtpHistoryClustersModuleMinimumVisitsRequired,
+      ntp_features::kNtpHistoryClustersModuleMinimumVisitsRequiredParam,
+      kMinRequiredVisits);
+  if (min_visits < 0) {
+    return kMinRequiredVisits;
+  }
+  return min_visits;
+}
+
 int GetMinImagesToShow() {
   static int min_images_to_show = base::GetFieldTrialParamByFeatureAsInt(
       ntp_features::kNtpHistoryClustersModuleMinimumImagesRequired,
@@ -100,6 +111,7 @@
 
 history_clusters::QueryClustersFilterParams GetFilterParamsFromFeatureFlags() {
   history_clusters::QueryClustersFilterParams filter_params;
+  filter_params.min_visits = GetMinVisitsToShow();
   filter_params.min_visits_with_images = GetMinImagesToShow();
   filter_params.categories_allowlist = GetCategories(
       ntp_features::kNtpHistoryClustersModuleCategoriesAllowlistParam);
diff --git a/chrome/browser/performance_manager/policies/heuristic_memory_saver_policy.cc b/chrome/browser/performance_manager/policies/heuristic_memory_saver_policy.cc
index 15f18bb..100d16a 100644
--- a/chrome/browser/performance_manager/policies/heuristic_memory_saver_policy.cc
+++ b/chrome/browser/performance_manager/policies/heuristic_memory_saver_policy.cc
@@ -80,7 +80,7 @@
       static_cast<float>(pmf_threshold_percent_)) {
     PageDiscardingHelper::GetFromGraph(graph_)->DiscardAPage(
         /*post_discard_cb=*/base::DoNothing(),
-        /*discard_reason=*/::mojom::LifecycleUnitDiscardReason::PROACTIVE,
+        PageDiscardingHelper::DiscardReason::PROACTIVE,
         /*minimum_time_in_background=*/minimum_time_in_background_);
     next_interval = threshold_reached_heartbeat_interval_;
   }
diff --git a/chrome/browser/performance_manager/policies/high_efficiency_mode_policy.cc b/chrome/browser/performance_manager/policies/high_efficiency_mode_policy.cc
index 4273eb2..3abcdf9 100644
--- a/chrome/browser/performance_manager/policies/high_efficiency_mode_policy.cc
+++ b/chrome/browser/performance_manager/policies/high_efficiency_mode_policy.cc
@@ -152,7 +152,7 @@
   DCHECK(IsHighEfficiencyDiscardingEnabled());
 
   PageDiscardingHelper::GetFromGraph(graph_)->ImmediatelyDiscardSpecificPage(
-      page_node, ::mojom::LifecycleUnitDiscardReason::PROACTIVE);
+      page_node, PageDiscardingHelper::DiscardReason::PROACTIVE);
 }
 
 }  // namespace performance_manager::policies
diff --git a/chrome/browser/performance_manager/policies/oom_score_policy_lacros.cc b/chrome/browser/performance_manager/policies/oom_score_policy_lacros.cc
index 80bb19f..8505825 100644
--- a/chrome/browser/performance_manager/policies/oom_score_policy_lacros.cc
+++ b/chrome/browser/performance_manager/policies/oom_score_policy_lacros.cc
@@ -46,13 +46,13 @@
 
   std::vector<PageNodeSortProxy> candidates;
   for (const auto* page_node : page_nodes) {
-    PageDiscardingHelper::CanUrgentlyDiscardResult can_discard_result =
-        discarding_helper->CanUrgentlyDiscard(page_node);
-    bool is_marked = (can_discard_result ==
-                      PageDiscardingHelper::CanUrgentlyDiscardResult::kMarked);
-    bool is_protected =
-        (can_discard_result ==
-         PageDiscardingHelper::CanUrgentlyDiscardResult::kProtected);
+    PageDiscardingHelper::CanDiscardResult can_discard_result =
+        discarding_helper->CanDiscard(
+            page_node, PageDiscardingHelper::DiscardReason::URGENT);
+    bool is_marked =
+        (can_discard_result == PageDiscardingHelper::CanDiscardResult::kMarked);
+    bool is_protected = (can_discard_result ==
+                         PageDiscardingHelper::CanDiscardResult::kProtected);
     candidates.emplace_back(page_node, is_marked, is_protected,
                             page_node->GetTimeSinceLastVisibilityChange());
   }
diff --git a/chrome/browser/performance_manager/policies/page_discarding_helper.cc b/chrome/browser/performance_manager/policies/page_discarding_helper.cc
index 31ce278..2e1757c 100644
--- a/chrome/browser/performance_manager/policies/page_discarding_helper.cc
+++ b/chrome/browser/performance_manager/policies/page_discarding_helper.cc
@@ -18,7 +18,6 @@
 #include "build/chromeos_buildflags.h"
 #include "chrome/browser/performance_manager/mechanisms/page_discarder.h"
 #include "chrome/browser/performance_manager/policies/policy_features.h"
-#include "chrome/browser/resource_coordinator/lifecycle_unit_state.mojom.h"
 #include "components/performance_manager/graph/node_attached_data_impl.h"
 #include "components/performance_manager/graph/page_node_impl.h"
 #include "components/performance_manager/public/graph/frame_node.h"
@@ -42,7 +41,7 @@
 // discard a PageNode.
 // TODO(sebmarchand): The only reason for a discard attempt to fail is if we try
 // to discard a prerenderer, remove this once we can detect if a PageNode is a
-// prerenderer in |CanUrgentlyDiscard|.
+// prerenderer in CanDiscard().
 class DiscardAttemptMarker : public NodeAttachedDataImpl<DiscardAttemptMarker> {
  public:
   struct Traits : public NodeAttachedDataInMap<PageNodeImpl> {};
@@ -113,7 +112,7 @@
 
 void PageDiscardingHelper::DiscardAPage(
     base::OnceCallback<void(bool)> post_discard_cb,
-    ::mojom::LifecycleUnitDiscardReason discard_reason,
+    DiscardReason discard_reason,
     base::TimeDelta minimum_time_in_background) {
   DiscardMultiplePages(absl::nullopt, false, std::move(post_discard_cb),
                        discard_reason, minimum_time_in_background);
@@ -123,7 +122,7 @@
     absl::optional<uint64_t> reclaim_target_kb,
     bool discard_protected_tabs,
     base::OnceCallback<void(bool)> post_discard_cb,
-    ::mojom::LifecycleUnitDiscardReason discard_reason,
+    DiscardReason discard_reason,
     base::TimeDelta minimum_time_in_background) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
@@ -139,13 +138,12 @@
 
   std::vector<PageNodeSortProxy> candidates;
   for (const auto* page_node : page_nodes) {
-    CanUrgentlyDiscardResult can_discard_result =
-        CanUrgentlyDiscard(page_node, minimum_time_in_background);
-    if (can_discard_result == CanUrgentlyDiscardResult::kMarked) {
+    CanDiscardResult can_discard_result =
+        CanDiscard(page_node, discard_reason, minimum_time_in_background);
+    if (can_discard_result == CanDiscardResult::kMarked) {
       continue;
     }
-    bool is_protected =
-        (can_discard_result == CanUrgentlyDiscardResult::kProtected);
+    bool is_protected = (can_discard_result == CanDiscardResult::kProtected);
     if (!discard_protected_tabs && is_protected) {
       continue;
     }
@@ -214,12 +212,12 @@
 
 void PageDiscardingHelper::ImmediatelyDiscardSpecificPage(
     const PageNode* page_node,
-    ::mojom::LifecycleUnitDiscardReason discard_reason,
+    DiscardReason discard_reason,
     base::OnceCallback<void(bool)> post_discard_cb) {
   // Pass 0 TimeDelta to bypass the minimum time in background check.
-  if (CanUrgentlyDiscard(page_node,
-                         /* minimum_time_in_background */ base::TimeDelta()) ==
-      CanUrgentlyDiscardResult::kEligible) {
+  if (CanDiscard(page_node, discard_reason,
+                 /*minimum_time_in_background=*/base::TimeDelta()) ==
+      CanDiscardResult::kEligible) {
     page_discarder_->DiscardPageNodes({page_node}, discard_reason,
                                       std::move(post_discard_cb));
   } else {
@@ -293,45 +291,58 @@
   return PageLiveStateDecorator::Data::FromPageNode(page_node);
 }
 
-PageDiscardingHelper::CanUrgentlyDiscardResult
-PageDiscardingHelper::CanUrgentlyDiscard(
+PageDiscardingHelper::CanDiscardResult PageDiscardingHelper::CanDiscard(
     const PageNode* page_node,
+    DiscardReason discard_reason,
     base::TimeDelta minimum_time_in_background) const {
   if (DiscardAttemptMarker::Get(PageNodeImpl::FromNode(page_node))) {
-    return CanUrgentlyDiscardResult::kMarked;
+    return CanDiscardResult::kMarked;
+  }
+
+  bool is_proactive;
+  switch (discard_reason) {
+    case DiscardReason::EXTERNAL:
+      // Always allow discards from external sources like extensions.
+      return CanDiscardResult::kEligible;
+    case DiscardReason::URGENT:
+      is_proactive = false;
+      break;
+    case DiscardReason::PROACTIVE:
+      is_proactive = true;
+      break;
   }
 
   if (page_node->IsVisible()) {
-    return CanUrgentlyDiscardResult::kProtected;
+    return CanDiscardResult::kProtected;
   }
   if (page_node->IsAudible()) {
-    return CanUrgentlyDiscardResult::kProtected;
+    return CanDiscardResult::kProtected;
   }
 
   // Don't discard tabs that have recently played audio.
   auto it = last_change_to_non_audible_time_.find(page_node);
   if (it != last_change_to_non_audible_time_.end()) {
     if (base::TimeTicks::Now() - it->second < kTabAudioProtectionTime) {
-      return CanUrgentlyDiscardResult::kProtected;
+      return CanDiscardResult::kProtected;
     }
   }
 
   if (page_node->GetTimeSinceLastVisibilityChange() <
       minimum_time_in_background) {
-    return CanUrgentlyDiscardResult::kProtected;
+    return CanDiscardResult::kProtected;
   }
 
   // Do not discard PDFs as they might contain entry that is not saved and they
   // don't remember their scrolling positions. See crbug.com/547286 and
   // crbug.com/65244.
   if (page_node->GetContentsMimeType() == "application/pdf") {
-    return CanUrgentlyDiscardResult::kProtected;
+    return CanDiscardResult::kProtected;
   }
 
   // Don't discard tabs that don't have a main frame yet.
   auto* main_frame = page_node->GetMainFrameNode();
   if (!main_frame) {
-    return CanUrgentlyDiscardResult::kProtected;
+    return CanDiscardResult::kProtected;
   }
 
   // Only discard http(s) pages and internal pages to make sure that we don't
@@ -340,16 +351,18 @@
       main_frame->GetURL().SchemeIsHTTPOrHTTPS() ||
       main_frame->GetURL().SchemeIs("chrome");
   if (!is_web_page_or_internal_page) {
-    return CanUrgentlyDiscardResult::kProtected;
+    return CanDiscardResult::kProtected;
   }
 
   if (!main_frame->GetURL().is_valid() || main_frame->GetURL().is_empty()) {
-    return CanUrgentlyDiscardResult::kProtected;
+    return CanDiscardResult::kProtected;
   }
 
+  // The enterprise policy to except pages from discarding applies to both
+  // proactive and urgent discards.
   if (IsPageOptedOutOfDiscarding(page_node->GetBrowserContextID(),
                                  main_frame->GetURL())) {
-    return CanUrgentlyDiscardResult::kProtected;
+    return CanDiscardResult::kProtected;
   }
 
   const auto* live_state_data = GetPageNodeLiveStateData(page_node);
@@ -357,50 +370,60 @@
   // The live state data won't be available if none of these events ever
   // happened on the page.
   if (live_state_data) {
+    // Don't discard the page if an extension is protecting it from discards.
     if (!live_state_data->IsAutoDiscardable()) {
-      return CanUrgentlyDiscardResult::kProtected;
+      return CanDiscardResult::kProtected;
     }
     if (live_state_data->IsCapturingVideo()) {
-      return CanUrgentlyDiscardResult::kProtected;
+      return CanDiscardResult::kProtected;
     }
     if (live_state_data->IsCapturingAudio()) {
-      return CanUrgentlyDiscardResult::kProtected;
+      return CanDiscardResult::kProtected;
     }
     if (live_state_data->IsBeingMirrored()) {
-      return CanUrgentlyDiscardResult::kProtected;
+      return CanDiscardResult::kProtected;
     }
     if (live_state_data->IsCapturingWindow()) {
-      return CanUrgentlyDiscardResult::kProtected;
+      return CanDiscardResult::kProtected;
     }
     if (live_state_data->IsCapturingDisplay()) {
-      return CanUrgentlyDiscardResult::kProtected;
+      return CanDiscardResult::kProtected;
     }
     if (live_state_data->IsConnectedToBluetoothDevice()) {
-      return CanUrgentlyDiscardResult::kProtected;
+      return CanDiscardResult::kProtected;
     }
     if (live_state_data->IsConnectedToUSBDevice()) {
-      return CanUrgentlyDiscardResult::kProtected;
+      return CanDiscardResult::kProtected;
     }
+    // Don't discard the active tab in any window, even if the window is not
+    // visible. Otherwise the user would see a blank page when the window
+    // becomes visible again, as the tab isn't reloaded until they click on it.
     if (live_state_data->IsActiveTab()) {
-      return CanUrgentlyDiscardResult::kProtected;
+      return CanDiscardResult::kProtected;
     }
+    // Pinning a tab is a strong signal the user wants to keep it.
     if (live_state_data->IsPinnedTab()) {
-      return CanUrgentlyDiscardResult::kProtected;
+      return CanDiscardResult::kProtected;
     }
-    if (live_state_data->IsContentSettingTypeAllowed(
-            ContentSettingsType::NOTIFICATIONS)) {
-      return CanUrgentlyDiscardResult::kProtected;
-    }
+    // Don't discard pages with devtools attached, because when it's restored
+    // the devtools window won't come back. The user may be monitoring the page
+    // in the background with devtools.
     if (live_state_data->IsDevToolsOpen()) {
-      return CanUrgentlyDiscardResult::kProtected;
+      return CanDiscardResult::kProtected;
     }
-    if (live_state_data->UpdatedTitleOrFaviconInBackground()) {
-      return CanUrgentlyDiscardResult::kProtected;
+    if (is_proactive) {
+      if (live_state_data->IsContentSettingTypeAllowed(
+              ContentSettingsType::NOTIFICATIONS)) {
+        return CanDiscardResult::kProtected;
+      }
+      if (live_state_data->UpdatedTitleOrFaviconInBackground()) {
+        return CanDiscardResult::kProtected;
+      }
     }
 #if !BUILDFLAG(IS_CHROMEOS)
     // TODO(sebmarchand): Skip this check if the Entreprise memory limit is set.
     if (live_state_data->WasDiscarded()) {
-      return CanUrgentlyDiscardResult::kProtected;
+      return CanDiscardResult::kProtected;
     }
     // TODO(sebmarchand): Consider resetting the |WasDiscarded| value when the
     // main frame document changes, also remove the DiscardAttemptMarker in
@@ -411,12 +434,12 @@
   // `HadUserEdits()` is currently a superset of `HadFormInteraction()` but
   // that may change so check both here (the check is not expensive).
   if (page_node->HadFormInteraction() || page_node->HadUserEdits()) {
-    return CanUrgentlyDiscardResult::kProtected;
+    return CanDiscardResult::kProtected;
   }
 
   // TODO(sebmarchand): Do not discard crashed tabs.
 
-  return CanUrgentlyDiscardResult::kEligible;
+  return CanDiscardResult::kEligible;
 }
 
 bool PageDiscardingHelper::IsPageOptedOutOfDiscarding(
@@ -440,21 +463,20 @@
 
 base::Value::Dict PageDiscardingHelper::DescribePageNodeData(
     const PageNode* node) const {
-  base::StringPiece can_discard;
-  switch (CanUrgentlyDiscard(node, base::TimeDelta())) {
-    case CanUrgentlyDiscardResult::kEligible:
-      can_discard = "eligible";
-      break;
-    case CanUrgentlyDiscardResult::kProtected:
-      can_discard = "protected";
-      break;
-    case CanUrgentlyDiscardResult::kMarked:
-      can_discard = "marked";
-      break;
-  }
+  auto can_discard = [this, node](DiscardReason discard_reason) {
+    switch (this->CanDiscard(node, discard_reason, base::TimeDelta())) {
+      case CanDiscardResult::kEligible:
+        return "eligible";
+      case CanDiscardResult::kProtected:
+        return "protected";
+      case CanDiscardResult::kMarked:
+        return "marked";
+    }
+  };
 
   base::Value::Dict ret;
-  ret.Set("can_urgently_discard", can_discard);
+  ret.Set("can_urgently_discard", can_discard(DiscardReason::URGENT));
+  ret.Set("can_proactively_discard", can_discard(DiscardReason::PROACTIVE));
   auto it = last_change_to_non_audible_time_.find(node);
   if (it != last_change_to_non_audible_time_.end()) {
     ret.Set("non_audible_change_time", TimeDeltaFromNowToValue(it->second));
@@ -471,7 +493,7 @@
     absl::optional<uint64_t> reclaim_target_kb,
     bool discard_protected_tabs,
     base::OnceCallback<void(bool)> post_discard_cb,
-    ::mojom::LifecycleUnitDiscardReason discard_reason,
+    DiscardReason discard_reason,
     base::TimeDelta minimum_time_in_background,
     bool success) {
   // When there is no discard candidate, DiscardMultiplePages returns
diff --git a/chrome/browser/performance_manager/policies/page_discarding_helper.h b/chrome/browser/performance_manager/policies/page_discarding_helper.h
index 4c6b67a..48c4d31 100644
--- a/chrome/browser/performance_manager/policies/page_discarding_helper.h
+++ b/chrome/browser/performance_manager/policies/page_discarding_helper.h
@@ -11,7 +11,7 @@
 #include "base/memory/raw_ptr.h"
 #include "base/memory/weak_ptr.h"
 #include "base/time/time.h"
-#include "chrome/browser/resource_coordinator/lifecycle_unit_state.mojom.h"
+#include "chrome/browser/resource_coordinator/lifecycle_unit_state.mojom-shared.h"
 #include "components/performance_manager/public/decorators/page_live_state_decorator.h"
 #include "components/performance_manager/public/features.h"
 #include "components/performance_manager/public/graph/graph.h"
@@ -85,7 +85,7 @@
                              public GraphRegisteredImpl<PageDiscardingHelper>,
                              public NodeDataDescriberDefaultImpl {
  public:
-  enum class CanUrgentlyDiscardResult {
+  enum class CanDiscardResult {
     // Discarding eligible nodes is hard to notice for user.
     kEligible,
     // Discarding protected nodes is noticeable to user.
@@ -94,6 +94,9 @@
     kMarked,
   };
 
+  // Export discard reason in the public interface.
+  using DiscardReason = ::mojom::LifecycleUnitDiscardReason;
+
   PageDiscardingHelper();
   ~PageDiscardingHelper() override;
   PageDiscardingHelper(const PageDiscardingHelper& other) = delete;
@@ -102,30 +105,30 @@
   // Selects a tab to discard and posts to the UI thread to discard it. This
   // will try to discard a tab until there's been a successful discard or until
   // there's no more discard candidate.
-  // `minimum_time_in_background` is passed to `CanUrgentlyDiscard()`, see the
-  // comment there about its usage.
+  // `minimum_time_in_background` is passed to `CanDiscard()`, see the comment
+  // there about its usage.
   void DiscardAPage(base::OnceCallback<void(bool)> post_discard_cb,
-                    ::mojom::LifecycleUnitDiscardReason discard_reason,
+                    DiscardReason discard_reason,
                     base::TimeDelta minimum_time_in_background =
                         kNonVisiblePagesUrgentProtectionTime);
 
   // Discards multiple tabs to meet the reclaim target based and posts to the UI
   // thread to discard these tabs. Retries discarding if all discardings in the
   // UI thread fail. If |reclaim_target_kb| is nullopt, only discard one tab. If
-  // |discard_protected_tabs| is true, protected tab (CanUrgentlyDiscard()
-  // returns kProtected) can also be discarded.
-  // `minimum_time_in_background` is passed to `CanUrgentlyDiscard()`, see the
-  // comment there about its usage.
+  // |discard_protected_tabs| is true, protected tabs (CanDiscard() returns
+  // kProtected) can also be discarded.
+  // `minimum_time_in_background` is passed to `CanDiscard()`, see the comment
+  // there about its usage.
   void DiscardMultiplePages(absl::optional<uint64_t> reclaim_target_kb,
                             bool discard_protected_tabs,
                             base::OnceCallback<void(bool)> post_discard_cb,
-                            ::mojom::LifecycleUnitDiscardReason discard_reason,
+                            DiscardReason discard_reason,
                             base::TimeDelta minimum_time_in_background =
                                 kNonVisiblePagesUrgentProtectionTime);
 
   void ImmediatelyDiscardSpecificPage(
       const PageNode* page_node,
-      ::mojom::LifecycleUnitDiscardReason discard_reason,
+      DiscardReason discard_reason,
       base::OnceCallback<void(bool)> post_discard_cb = base::DoNothing());
 
   // PageNodeObserver:
@@ -138,21 +141,15 @@
 
   void SetMockDiscarderForTesting(
       std::unique_ptr<mechanism::PageDiscarder> discarder);
-  bool CanUrgentlyDiscardForTesting(
-      const PageNode* page_node,
-      base::TimeDelta minimum_time_in_background =
-          kNonVisiblePagesUrgentProtectionTime) const {
-    return CanUrgentlyDiscard(page_node, minimum_time_in_background) ==
-           CanUrgentlyDiscardResult::kEligible;
-  }
-  // Indicates if a PageNode can be urgently discarded.
-  // If `minimum_time_in_background` is non-zero, the page will not be discarded
-  // if it has not spent at least `minimum_time_in_background` in the
-  // not-visible state.
-  CanUrgentlyDiscardResult CanUrgentlyDiscard(
-      const PageNode* page_node,
-      base::TimeDelta minimum_time_in_background =
-          kNonVisiblePagesUrgentProtectionTime) const;
+
+  // Indicates if `page_node` can be urgently discarded, using a list of
+  // criteria depending on `discard_reason`. If `minimum_time_in_background` is
+  // non-zero, the page will not be discarded if it has not spent at least
+  // `minimum_time_in_background` in the not-visible state.
+  CanDiscardResult CanDiscard(const PageNode* page_node,
+                              DiscardReason discard_reason,
+                              base::TimeDelta minimum_time_in_background =
+                                  kNonVisiblePagesUrgentProtectionTime) const;
 
   void SetGraphForTesting(Graph* graph) { graph_ = graph; }
   static void AddDiscardAttemptMarkerForTesting(PageNode* page_node);
@@ -182,7 +179,7 @@
       absl::optional<uint64_t> reclaim_target_kb,
       bool discard_protected_tabs,
       base::OnceCallback<void(bool)> post_discard_cb,
-      ::mojom::LifecycleUnitDiscardReason discard_reason,
+      DiscardReason discard_reason,
       base::TimeDelta minimum_time_in_background,
       bool success);
 
diff --git a/chrome/browser/performance_manager/policies/page_discarding_helper_browsertest.cc b/chrome/browser/performance_manager/policies/page_discarding_helper_browsertest.cc
index aae794d..ba7db7e 100644
--- a/chrome/browser/performance_manager/policies/page_discarding_helper_browsertest.cc
+++ b/chrome/browser/performance_manager/policies/page_discarding_helper_browsertest.cc
@@ -4,12 +4,12 @@
 
 #include "chrome/browser/performance_manager/policies/page_discarding_helper.h"
 
+#include "base/location.h"
 #include "base/memory/weak_ptr.h"
 #include "base/run_loop.h"
 #include "base/strings/strcat.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/bind.h"
-#include "chrome/browser/resource_coordinator/lifecycle_unit_state.mojom.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/test/base/in_process_browser_test.h"
@@ -34,6 +34,8 @@
 
 namespace {
 
+using DiscardReason = PageDiscardingHelper::DiscardReason;
+
 class FaviconWatcher final : public content::WebContentsObserver {
  public:
   explicit FaviconWatcher(content::WebContents* web_contents)
@@ -106,18 +108,39 @@
     favicon_watcher.Wait();
   }
 
-  void ExpectImmediateDiscard(int index, bool expected_result) {
+  void ExpectImmediateDiscard(
+      int index,
+      DiscardReason discard_reason,
+      bool expected_result,
+      const base::Location& location = base::Location::Current()) {
+    const char* discard_string;
+    switch (discard_reason) {
+      case DiscardReason::URGENT:
+        discard_string = "Urgent";
+        break;
+      case DiscardReason::PROACTIVE:
+        discard_string = "Proactive";
+        break;
+      case DiscardReason::EXTERNAL:
+        discard_string = "External";
+        break;
+    }
+    SCOPED_TRACE(::testing::Message()
+                 << discard_string << " discard from " << location.ToString());
     base::WeakPtr<PageNode> page_node =
         PerformanceManager::GetPrimaryPageNodeForWebContents(
             browser()->tab_strip_model()->GetWebContentsAt(index));
     base::RunLoop run_loop;
     PerformanceManager::CallOnGraph(
         FROM_HERE, base::BindLambdaForTesting([&](Graph* graph) {
+          SCOPED_TRACE(::testing::Message()
+                       << discard_string << " discard, called on graph from "
+                       << location.ToString());
           ASSERT_TRUE(page_node);
           auto* helper = PageDiscardingHelper::GetFromGraph(graph);
           ASSERT_TRUE(helper);
           helper->ImmediatelyDiscardSpecificPage(
-              page_node.get(), ::mojom::LifecycleUnitDiscardReason::URGENT,
+              page_node.get(), discard_reason,
               base::BindLambdaForTesting([&](bool success) {
                 EXPECT_EQ(success, expected_result);
                 run_loop.Quit();
@@ -132,38 +155,59 @@
 };
 
 IN_PROC_BROWSER_TEST_F(PageDiscardingHelperBrowserTest, DiscardSpecificPage) {
-  // Background pages can be discarded.
-  const int index1 = OpenNewBackgroundPage();
-  ExpectImmediateDiscard(index1, true);
+  // Test urgent and proactive discards in a loop to avoid the overhead of
+  // starting a new browser every time.
+  // TODO(crbug.com/1426484): Add tests for all the other heuristics in
+  // PageDiscardingHelper::CanDiscard().
+  for (auto discard_reason :
+       {DiscardReason::URGENT, DiscardReason::PROACTIVE}) {
+    {
+      // Background pages can be discarded.
+      const int index1 = OpenNewBackgroundPage();
+      ExpectImmediateDiscard(index1, discard_reason, true);
 
-  // Foreground page should be blocked.
-  const int index2 = OpenNewBackgroundPage();
-  browser()->tab_strip_model()->ActivateTabAt(index2);
-  ExpectImmediateDiscard(index2, false);
+      // Foreground page should be blocked.
+      // TODO(crbug.com/1426484): Also test when the browser window is occluded.
+      // They should still be blocked.
+      const int index2 = OpenNewBackgroundPage();
+      browser()->tab_strip_model()->ActivateTabAt(index2);
+      ExpectImmediateDiscard(index2, discard_reason, false);
+    }
 
-  // Updating the title while in the background should block the discard.
-  const int index3 = OpenNewBackgroundPage();
-  UpdatePageTitle(index3);
-  ExpectImmediateDiscard(index3, false);
+    {
+      // Updating the title while in the background should block only proactive
+      // discards.
+      const int index1 = OpenNewBackgroundPage();
+      UpdatePageTitle(index1);
+      ExpectImmediateDiscard(index1, discard_reason,
+                             discard_reason == DiscardReason::URGENT);
 
-  // Updating the page title while in the foreground should not.
-  const int index4 = OpenNewBackgroundPage();
-  browser()->tab_strip_model()->ActivateTabAt(index4);
-  UpdatePageTitle(index4);
-  browser()->tab_strip_model()->ActivateTabAt(index3);
-  ExpectImmediateDiscard(index4, true);
+      // Updating the page title while in the foreground should not block any
+      // discards.
+      const int index2 = OpenNewBackgroundPage();
+      browser()->tab_strip_model()->ActivateTabAt(index2);
+      UpdatePageTitle(index2);
+      browser()->tab_strip_model()->ActivateTabAt(index1);
+      ExpectImmediateDiscard(index2, discard_reason, true);
+    }
 
-  // Updating the favicon while in the background should block the discard.
-  const int index5 = OpenNewBackgroundPage();
-  UpdateFavicon(index5);
-  ExpectImmediateDiscard(index5, false);
+    {
+      // Updating the favicon while in the background should block only
+      // proactive discards.
+      const int index1 = OpenNewBackgroundPage();
+      UpdateFavicon(index1);
+      ExpectImmediateDiscard(index1, discard_reason,
+                             discard_reason == DiscardReason::URGENT);
 
-  // Updating the favicon while in the foreground should not.
-  const int index6 = OpenNewBackgroundPage();
-  browser()->tab_strip_model()->ActivateTabAt(index6);
-  UpdateFavicon(index6);
-  browser()->tab_strip_model()->ActivateTabAt(index5);
-  ExpectImmediateDiscard(index6, true);
+      // Updating the favicon while in the foreground should not block any
+      // discards.
+      const int index2 = OpenNewBackgroundPage();
+      browser()->tab_strip_model()->ActivateTabAt(index2);
+      UpdateFavicon(index2);
+      browser()->tab_strip_model()->ActivateTabAt(index1);
+      ExpectImmediateDiscard(index2, discard_reason, true);
+    }
+  }
 }
 
 }  // namespace
diff --git a/chrome/browser/performance_manager/policies/page_discarding_helper_unittest.cc b/chrome/browser/performance_manager/policies/page_discarding_helper_unittest.cc
index 5b6fc64..c7a44fc 100644
--- a/chrome/browser/performance_manager/policies/page_discarding_helper_unittest.cc
+++ b/chrome/browser/performance_manager/policies/page_discarding_helper_unittest.cc
@@ -21,6 +21,8 @@
 namespace performance_manager {
 namespace policies {
 
+using CanDiscardResult = PageDiscardingHelper::CanDiscardResult;
+using DiscardReason = PageDiscardingHelper::DiscardReason;
 using ::testing::Return;
 
 class PageDiscardingHelperTest
@@ -42,6 +44,21 @@
     testing::GraphTestHarnessWithMockDiscarder::TearDown();
   }
 
+  // Convenience wrappers for PageNodeHelper::CanDiscard().
+  bool CanDiscard(const PageNode* page_node, DiscardReason discard_reason) {
+    return PageDiscardingHelper::GetFromGraph(graph())->CanDiscard(
+               page_node, discard_reason) == CanDiscardResult::kEligible;
+  }
+
+  bool CanDiscardWithMinimumTimeInBackground(
+      const PageNode* page_node,
+      DiscardReason discard_reason,
+      base::TimeDelta minimum_time_in_background) {
+    return PageDiscardingHelper::GetFromGraph(graph())->CanDiscard(
+               page_node, discard_reason, minimum_time_in_background) ==
+           CanDiscardResult::kEligible;
+  }
+
  protected:
   base::HistogramTester* histogram_tester() { return histogram_tester_.get(); }
 
@@ -51,33 +68,33 @@
 
 TEST_F(PageDiscardingHelperTest, TestCannotDiscardVisiblePage) {
   page_node()->SetIsVisible(true);
-  EXPECT_FALSE(
-      PageDiscardingHelper::GetFromGraph(graph())->CanUrgentlyDiscardForTesting(
-          page_node()));
+  EXPECT_FALSE(CanDiscard(page_node(), DiscardReason::URGENT));
+  EXPECT_FALSE(CanDiscard(page_node(), DiscardReason::PROACTIVE));
+  EXPECT_TRUE(CanDiscard(page_node(), DiscardReason::EXTERNAL));
 }
 
 TEST_F(PageDiscardingHelperTest, TestCannotDiscardAudiblePage) {
   page_node()->SetIsAudible(true);
-  EXPECT_FALSE(
-      PageDiscardingHelper::GetFromGraph(graph())->CanUrgentlyDiscardForTesting(
-          page_node()));
+  EXPECT_FALSE(CanDiscard(page_node(), DiscardReason::URGENT));
+  EXPECT_FALSE(CanDiscard(page_node(), DiscardReason::PROACTIVE));
+  EXPECT_TRUE(CanDiscard(page_node(), DiscardReason::EXTERNAL));
 }
 
 TEST_F(PageDiscardingHelperTest,
        TestCannotDiscardPageWithDiscardAttemptMarker) {
   PageDiscardingHelper::GetFromGraph(graph())
       ->AddDiscardAttemptMarkerForTesting(page_node());
-  EXPECT_FALSE(
-      PageDiscardingHelper::GetFromGraph(graph())->CanUrgentlyDiscardForTesting(
-          page_node()));
+  EXPECT_FALSE(CanDiscard(page_node(), DiscardReason::URGENT));
+  EXPECT_FALSE(CanDiscard(page_node(), DiscardReason::PROACTIVE));
+  EXPECT_FALSE(CanDiscard(page_node(), DiscardReason::EXTERNAL));
 }
 
 TEST_F(PageDiscardingHelperTest, TestCannotDiscardRecentlyAudiblePage) {
   page_node()->SetIsAudible(true);
   page_node()->SetIsAudible(false);
-  EXPECT_FALSE(
-      PageDiscardingHelper::GetFromGraph(graph())->CanUrgentlyDiscardForTesting(
-          page_node()));
+  EXPECT_FALSE(CanDiscard(page_node(), DiscardReason::URGENT));
+  EXPECT_FALSE(CanDiscard(page_node(), DiscardReason::PROACTIVE));
+  EXPECT_TRUE(CanDiscard(page_node(), DiscardReason::EXTERNAL));
 }
 
 #if !BUILDFLAG(IS_CHROMEOS)
@@ -86,12 +103,16 @@
   page_node()->SetIsVisible(true);
   page_node()->SetIsVisible(false);
   AdvanceClock(base::Seconds(1));
-  EXPECT_FALSE(
-      PageDiscardingHelper::GetFromGraph(graph())->CanUrgentlyDiscardForTesting(
-          page_node()));
-  EXPECT_TRUE(
-      PageDiscardingHelper::GetFromGraph(graph())->CanUrgentlyDiscardForTesting(
-          page_node(), /* minimum_time_in_background */ base::Seconds(1)));
+  EXPECT_FALSE(CanDiscard(page_node(), DiscardReason::URGENT));
+  EXPECT_FALSE(CanDiscard(page_node(), DiscardReason::PROACTIVE));
+  EXPECT_TRUE(CanDiscard(page_node(), DiscardReason::EXTERNAL));
+
+  EXPECT_TRUE(CanDiscardWithMinimumTimeInBackground(
+      page_node(), DiscardReason::URGENT, base::Seconds(1)));
+  EXPECT_TRUE(CanDiscardWithMinimumTimeInBackground(
+      page_node(), DiscardReason::PROACTIVE, base::Seconds(1)));
+  EXPECT_TRUE(CanDiscardWithMinimumTimeInBackground(
+      page_node(), DiscardReason::EXTERNAL, base::Seconds(1)));
 }
 #endif
 
@@ -99,138 +120,139 @@
   page_node()->OnMainFrameNavigationCommitted(false, base::TimeTicks::Now(), 53,
                                               GURL("https://foo.com/doc.pdf"),
                                               "application/pdf");
-  EXPECT_FALSE(
-      PageDiscardingHelper::GetFromGraph(graph())->CanUrgentlyDiscardForTesting(
-          page_node()));
+  EXPECT_FALSE(CanDiscard(page_node(), DiscardReason::URGENT));
+  EXPECT_FALSE(CanDiscard(page_node(), DiscardReason::PROACTIVE));
+  EXPECT_TRUE(CanDiscard(page_node(), DiscardReason::EXTERNAL));
 }
 
 TEST_F(PageDiscardingHelperTest, TestCannotDiscardPageWithoutMainFrame) {
   ResetFrameNode();
-  EXPECT_FALSE(
-      PageDiscardingHelper::GetFromGraph(graph())->CanUrgentlyDiscardForTesting(
-          page_node()));
+  EXPECT_FALSE(CanDiscard(page_node(), DiscardReason::URGENT));
+  EXPECT_FALSE(CanDiscard(page_node(), DiscardReason::PROACTIVE));
+  EXPECT_TRUE(CanDiscard(page_node(), DiscardReason::EXTERNAL));
 }
 
 TEST_F(PageDiscardingHelperTest, TestCannotDiscardExtension) {
   frame_node()->OnNavigationCommitted(GURL("chrome-extention://foo"), false);
-  EXPECT_FALSE(
-      PageDiscardingHelper::GetFromGraph(graph())->CanUrgentlyDiscardForTesting(
-          page_node()));
+  EXPECT_FALSE(CanDiscard(page_node(), DiscardReason::URGENT));
+  EXPECT_FALSE(CanDiscard(page_node(), DiscardReason::PROACTIVE));
+  EXPECT_TRUE(CanDiscard(page_node(), DiscardReason::EXTERNAL));
 }
 
 TEST_F(PageDiscardingHelperTest, TestCannotDiscardPageWithInvalidURL) {
   frame_node()->OnNavigationCommitted(GURL("foo42"), false);
-  EXPECT_FALSE(
-      PageDiscardingHelper::GetFromGraph(graph())->CanUrgentlyDiscardForTesting(
-          page_node()));
+  EXPECT_FALSE(CanDiscard(page_node(), DiscardReason::URGENT));
+  EXPECT_FALSE(CanDiscard(page_node(), DiscardReason::PROACTIVE));
+  EXPECT_TRUE(CanDiscard(page_node(), DiscardReason::EXTERNAL));
 }
 
 TEST_F(PageDiscardingHelperTest, TestCannotDiscardPageProtectedByExtension) {
   PageLiveStateDecorator::Data::GetOrCreateForPageNode(page_node())
       ->SetIsAutoDiscardableForTesting(false);
-  EXPECT_FALSE(
-      PageDiscardingHelper::GetFromGraph(graph())->CanUrgentlyDiscardForTesting(
-          page_node()));
+  EXPECT_FALSE(CanDiscard(page_node(), DiscardReason::URGENT));
+  EXPECT_FALSE(CanDiscard(page_node(), DiscardReason::PROACTIVE));
+  EXPECT_TRUE(CanDiscard(page_node(), DiscardReason::EXTERNAL));
 }
 
 TEST_F(PageDiscardingHelperTest, TestCannotDiscardPageCapturingVideo) {
   PageLiveStateDecorator::Data::GetOrCreateForPageNode(page_node())
       ->SetIsCapturingVideoForTesting(true);
-  EXPECT_FALSE(
-      PageDiscardingHelper::GetFromGraph(graph())->CanUrgentlyDiscardForTesting(
-          page_node()));
+  EXPECT_FALSE(CanDiscard(page_node(), DiscardReason::URGENT));
+  EXPECT_FALSE(CanDiscard(page_node(), DiscardReason::PROACTIVE));
+  EXPECT_TRUE(CanDiscard(page_node(), DiscardReason::EXTERNAL));
 }
 
 TEST_F(PageDiscardingHelperTest, TestCannotDiscardPageCapturingAudio) {
   PageLiveStateDecorator::Data::GetOrCreateForPageNode(page_node())
       ->SetIsCapturingAudioForTesting(true);
-  EXPECT_FALSE(
-      PageDiscardingHelper::GetFromGraph(graph())->CanUrgentlyDiscardForTesting(
-          page_node()));
+  EXPECT_FALSE(CanDiscard(page_node(), DiscardReason::URGENT));
+  EXPECT_FALSE(CanDiscard(page_node(), DiscardReason::PROACTIVE));
+  EXPECT_TRUE(CanDiscard(page_node(), DiscardReason::EXTERNAL));
 }
 
 TEST_F(PageDiscardingHelperTest, TestCannotDiscardPageBeingMirrored) {
   PageLiveStateDecorator::Data::GetOrCreateForPageNode(page_node())
       ->SetIsBeingMirroredForTesting(true);
-  EXPECT_FALSE(
-      PageDiscardingHelper::GetFromGraph(graph())->CanUrgentlyDiscardForTesting(
-          page_node()));
+  EXPECT_FALSE(CanDiscard(page_node(), DiscardReason::URGENT));
+  EXPECT_FALSE(CanDiscard(page_node(), DiscardReason::PROACTIVE));
+  EXPECT_TRUE(CanDiscard(page_node(), DiscardReason::EXTERNAL));
 }
 
 TEST_F(PageDiscardingHelperTest, TestCannotDiscardPageCapturingWindow) {
   PageLiveStateDecorator::Data::GetOrCreateForPageNode(page_node())
       ->SetIsCapturingWindowForTesting(true);
-  EXPECT_FALSE(
-      PageDiscardingHelper::GetFromGraph(graph())->CanUrgentlyDiscardForTesting(
-          page_node()));
+  EXPECT_FALSE(CanDiscard(page_node(), DiscardReason::URGENT));
+  EXPECT_FALSE(CanDiscard(page_node(), DiscardReason::PROACTIVE));
+  EXPECT_TRUE(CanDiscard(page_node(), DiscardReason::EXTERNAL));
 }
 
 TEST_F(PageDiscardingHelperTest, TestCannotDiscardPageCapturingDisplay) {
   PageLiveStateDecorator::Data::GetOrCreateForPageNode(page_node())
       ->SetIsCapturingDisplayForTesting(true);
-  EXPECT_FALSE(
-      PageDiscardingHelper::GetFromGraph(graph())->CanUrgentlyDiscardForTesting(
-          page_node()));
+  EXPECT_FALSE(CanDiscard(page_node(), DiscardReason::URGENT));
+  EXPECT_FALSE(CanDiscard(page_node(), DiscardReason::PROACTIVE));
+  EXPECT_TRUE(CanDiscard(page_node(), DiscardReason::EXTERNAL));
 }
 
 TEST_F(PageDiscardingHelperTest,
        TestCannotDiscardPageConnectedToBluetoothDevice) {
   PageLiveStateDecorator::Data::GetOrCreateForPageNode(page_node())
       ->SetIsConnectedToBluetoothDeviceForTesting(true);
-  EXPECT_FALSE(
-      PageDiscardingHelper::GetFromGraph(graph())->CanUrgentlyDiscardForTesting(
-          page_node()));
+  EXPECT_FALSE(CanDiscard(page_node(), DiscardReason::URGENT));
+  EXPECT_FALSE(CanDiscard(page_node(), DiscardReason::PROACTIVE));
+  EXPECT_TRUE(CanDiscard(page_node(), DiscardReason::EXTERNAL));
 }
 
 TEST_F(PageDiscardingHelperTest, TestCannotDiscardIsConnectedToUSBDevice) {
   PageLiveStateDecorator::Data::GetOrCreateForPageNode(page_node())
       ->SetIsConnectedToUSBDeviceForTesting(true);
-  EXPECT_FALSE(
-      PageDiscardingHelper::GetFromGraph(graph())->CanUrgentlyDiscardForTesting(
-          page_node()));
+  EXPECT_FALSE(CanDiscard(page_node(), DiscardReason::URGENT));
+  EXPECT_FALSE(CanDiscard(page_node(), DiscardReason::PROACTIVE));
+  EXPECT_TRUE(CanDiscard(page_node(), DiscardReason::EXTERNAL));
 }
 
 #if !BUILDFLAG(IS_CHROMEOS)
 TEST_F(PageDiscardingHelperTest, TestCannotDiscardPageMultipleTimes) {
   PageLiveStateDecorator::Data::GetOrCreateForPageNode(page_node())
       ->SetWasDiscardedForTesting(true);
-  EXPECT_FALSE(
-      PageDiscardingHelper::GetFromGraph(graph())->CanUrgentlyDiscardForTesting(
-          page_node()));
+  EXPECT_FALSE(CanDiscard(page_node(), DiscardReason::URGENT));
+  EXPECT_FALSE(CanDiscard(page_node(), DiscardReason::PROACTIVE));
+  EXPECT_TRUE(CanDiscard(page_node(), DiscardReason::EXTERNAL));
 }
 #endif
 
 TEST_F(PageDiscardingHelperTest, TestCannotDiscardPageWithFormInteractions) {
   frame_node()->SetHadFormInteraction();
-  EXPECT_FALSE(
-      PageDiscardingHelper::GetFromGraph(graph())->CanUrgentlyDiscardForTesting(
-          page_node()));
+  EXPECT_FALSE(CanDiscard(page_node(), DiscardReason::URGENT));
+  EXPECT_FALSE(CanDiscard(page_node(), DiscardReason::PROACTIVE));
+  EXPECT_TRUE(CanDiscard(page_node(), DiscardReason::EXTERNAL));
 }
 
 TEST_F(PageDiscardingHelperTest, TestCannotDiscardPageWithUserEdits) {
   frame_node()->SetHadUserEdits();
-  EXPECT_FALSE(
-      PageDiscardingHelper::GetFromGraph(graph())->CanUrgentlyDiscardForTesting(
-          page_node()));
+  EXPECT_FALSE(CanDiscard(page_node(), DiscardReason::URGENT));
+  EXPECT_FALSE(CanDiscard(page_node(), DiscardReason::PROACTIVE));
+  EXPECT_TRUE(CanDiscard(page_node(), DiscardReason::EXTERNAL));
 }
 
-TEST_F(PageDiscardingHelperTest, TestCannotDiscardIsActiveTab) {
+TEST_F(PageDiscardingHelperTest, TestCannotDiscardActiveTab) {
   PageLiveStateDecorator::Data::GetOrCreateForPageNode(page_node())
       ->SetIsActiveTabForTesting(true);
-  EXPECT_FALSE(
-      PageDiscardingHelper::GetFromGraph(graph())->CanUrgentlyDiscardForTesting(
-          page_node()));
+  EXPECT_FALSE(CanDiscard(page_node(), DiscardReason::URGENT));
+  EXPECT_FALSE(CanDiscard(page_node(), DiscardReason::PROACTIVE));
+  EXPECT_TRUE(CanDiscard(page_node(), DiscardReason::EXTERNAL));
 }
 
-TEST_F(PageDiscardingHelperTest, TestCannotDiscardWithNotificationPermission) {
+TEST_F(PageDiscardingHelperTest,
+       TestCannotProactivelyDiscardWithNotificationPermission) {
   // The page is discardable if notifications are blocked.
   PageLiveStateDecorator::Data::GetOrCreateForPageNode(page_node())
       ->SetContentSettingsForTesting({
           {ContentSettingsType::NOTIFICATIONS, CONTENT_SETTING_BLOCK},
       });
-  EXPECT_TRUE(
-      PageDiscardingHelper::GetFromGraph(graph())->CanUrgentlyDiscardForTesting(
-          page_node()));
+  EXPECT_TRUE(CanDiscard(page_node(), DiscardReason::URGENT));
+  EXPECT_TRUE(CanDiscard(page_node(), DiscardReason::PROACTIVE));
+  EXPECT_TRUE(CanDiscard(page_node(), DiscardReason::EXTERNAL));
 
   // The page is discardable if notifications aren't found in its permissions
   // list.
@@ -238,18 +260,18 @@
       ->SetContentSettingsForTesting({
           {ContentSettingsType::AUTO_SELECT_CERTIFICATE, CONTENT_SETTING_ALLOW},
       });
-  EXPECT_TRUE(
-      PageDiscardingHelper::GetFromGraph(graph())->CanUrgentlyDiscardForTesting(
-          page_node()));
+  EXPECT_TRUE(CanDiscard(page_node(), DiscardReason::URGENT));
+  EXPECT_TRUE(CanDiscard(page_node(), DiscardReason::PROACTIVE));
+  EXPECT_TRUE(CanDiscard(page_node(), DiscardReason::EXTERNAL));
 
-  // The page is not discardable if it can send notifications.
+  // The page is not proactively discardable if it can send notifications.
   PageLiveStateDecorator::Data::GetOrCreateForPageNode(page_node())
       ->SetContentSettingsForTesting({
           {ContentSettingsType::NOTIFICATIONS, CONTENT_SETTING_ALLOW},
       });
-  EXPECT_FALSE(
-      PageDiscardingHelper::GetFromGraph(graph())->CanUrgentlyDiscardForTesting(
-          page_node()));
+  EXPECT_TRUE(CanDiscard(page_node(), DiscardReason::URGENT));
+  EXPECT_FALSE(CanDiscard(page_node(), DiscardReason::PROACTIVE));
+  EXPECT_TRUE(CanDiscard(page_node(), DiscardReason::EXTERNAL));
 }
 
 TEST_F(PageDiscardingHelperTest, TestCannotDiscardPageOnNoDiscardList) {
@@ -263,60 +285,60 @@
       static_cast<PageNode*>(page_node())->GetBrowserContextID(),
       {"youtube.com"});
   frame_node()->OnNavigationCommitted(GURL("https://www.youtube.com"), false);
-  EXPECT_FALSE(
-      PageDiscardingHelper::GetFromGraph(graph())->CanUrgentlyDiscardForTesting(
-          page_node()));
+  EXPECT_FALSE(CanDiscard(page_node(), DiscardReason::URGENT));
+  EXPECT_FALSE(CanDiscard(page_node(), DiscardReason::PROACTIVE));
+  EXPECT_TRUE(CanDiscard(page_node(), DiscardReason::EXTERNAL));
 
   frame_node()->OnNavigationCommitted(GURL("https://www.example.com"), false);
-  EXPECT_TRUE(
-      PageDiscardingHelper::GetFromGraph(graph())->CanUrgentlyDiscardForTesting(
-          page_node()));
+  EXPECT_TRUE(CanDiscard(page_node(), DiscardReason::URGENT));
+  EXPECT_TRUE(CanDiscard(page_node(), DiscardReason::PROACTIVE));
+  EXPECT_TRUE(CanDiscard(page_node(), DiscardReason::EXTERNAL));
 
   // Changing the no discard list rebuilds the matcher
   PageDiscardingHelper::GetFromGraph(graph())->SetNoDiscardPatternsForProfile(
       static_cast<PageNode*>(page_node())->GetBrowserContextID(),
       {"google.com"});
   frame_node()->OnNavigationCommitted(GURL("https://www.youtube.com"), false);
-  EXPECT_TRUE(
-      PageDiscardingHelper::GetFromGraph(graph())->CanUrgentlyDiscardForTesting(
-          page_node()));
+  EXPECT_TRUE(CanDiscard(page_node(), DiscardReason::URGENT));
+  EXPECT_TRUE(CanDiscard(page_node(), DiscardReason::PROACTIVE));
+  EXPECT_TRUE(CanDiscard(page_node(), DiscardReason::EXTERNAL));
   frame_node()->OnNavigationCommitted(GURL("https://www.google.com"), false);
-  EXPECT_FALSE(
-      PageDiscardingHelper::GetFromGraph(graph())->CanUrgentlyDiscardForTesting(
-          page_node()));
+  EXPECT_FALSE(CanDiscard(page_node(), DiscardReason::URGENT));
+  EXPECT_FALSE(CanDiscard(page_node(), DiscardReason::PROACTIVE));
+  EXPECT_TRUE(CanDiscard(page_node(), DiscardReason::EXTERNAL));
 
   // Setting the no discard list to empty makes all URLs discardable again.
   PageDiscardingHelper::GetFromGraph(graph())->SetNoDiscardPatternsForProfile(
       static_cast<PageNode*>(page_node())->GetBrowserContextID(), {});
   frame_node()->OnNavigationCommitted(GURL("https://www.google.com"), false);
-  EXPECT_TRUE(
-      PageDiscardingHelper::GetFromGraph(graph())->CanUrgentlyDiscardForTesting(
-          page_node()));
+  EXPECT_TRUE(CanDiscard(page_node(), DiscardReason::URGENT));
+  EXPECT_TRUE(CanDiscard(page_node(), DiscardReason::PROACTIVE));
+  EXPECT_TRUE(CanDiscard(page_node(), DiscardReason::EXTERNAL));
 }
 
-TEST_F(PageDiscardingHelperTest, TestCannotDiscardIsPinnedTab) {
+TEST_F(PageDiscardingHelperTest, TestCannotDiscardPinnedTab) {
   PageLiveStateDecorator::Data::GetOrCreateForPageNode(page_node())
       ->SetIsPinnedTabForTesting(true);
-  EXPECT_FALSE(
-      PageDiscardingHelper::GetFromGraph(graph())->CanUrgentlyDiscardForTesting(
-          page_node()));
+  EXPECT_FALSE(CanDiscard(page_node(), DiscardReason::URGENT));
+  EXPECT_FALSE(CanDiscard(page_node(), DiscardReason::PROACTIVE));
+  EXPECT_TRUE(CanDiscard(page_node(), DiscardReason::EXTERNAL));
 }
 
-TEST_F(PageDiscardingHelperTest, TestCannotDiscardIsDevToolsOpen) {
+TEST_F(PageDiscardingHelperTest, TestCannotDiscardWithDevToolsOpen) {
   PageLiveStateDecorator::Data::GetOrCreateForPageNode(page_node())
       ->SetIsDevToolsOpenForTesting(true);
-  EXPECT_FALSE(
-      PageDiscardingHelper::GetFromGraph(graph())->CanUrgentlyDiscardForTesting(
-          page_node()));
+  EXPECT_FALSE(CanDiscard(page_node(), DiscardReason::URGENT));
+  EXPECT_FALSE(CanDiscard(page_node(), DiscardReason::PROACTIVE));
+  EXPECT_TRUE(CanDiscard(page_node(), DiscardReason::EXTERNAL));
 }
 
 TEST_F(PageDiscardingHelperTest,
-       TestCannotDiscardUpdatedTitleOrFaviconInBackground) {
+       TestCannotProactivelyDiscardAfterUpdatedTitleOrFaviconInBackground) {
   PageLiveStateDecorator::Data::GetOrCreateForPageNode(page_node())
       ->SetUpdatedTitleOrFaviconInBackgroundForTesting(true);
-  EXPECT_FALSE(
-      PageDiscardingHelper::GetFromGraph(graph())->CanUrgentlyDiscardForTesting(
-          page_node()));
+  EXPECT_TRUE(CanDiscard(page_node(), DiscardReason::URGENT));
+  EXPECT_FALSE(CanDiscard(page_node(), DiscardReason::PROACTIVE));
+  EXPECT_TRUE(CanDiscard(page_node(), DiscardReason::EXTERNAL));
 }
 
 // Tests DiscardMultiplePages.
@@ -329,7 +351,7 @@
       /*reclaim_target_kb*/ 1024,
       /*discard_protected_tabs*/ false,
       base::BindOnce([](bool success) { EXPECT_FALSE(success); }),
-      ::mojom::LifecycleUnitDiscardReason::URGENT);
+      DiscardReason::URGENT);
   ::testing::Mock::VerifyAndClearExpectations(discarder());
 }
 
@@ -344,7 +366,7 @@
       /*reclaim_target_kb*/ 1024,
       /*discard_protected_tabs*/ true,
       base::BindOnce([](bool success) { EXPECT_TRUE(success); }),
-      ::mojom::LifecycleUnitDiscardReason::URGENT);
+      DiscardReason::URGENT);
   ::testing::Mock::VerifyAndClearExpectations(discarder());
 }
 
@@ -356,9 +378,7 @@
   main_frame_node2->SetIsCurrent(true);
   testing::MakePageNodeDiscardable(page_node2.get(), task_env());
 
-  EXPECT_TRUE(
-      PageDiscardingHelper::GetFromGraph(graph())->CanUrgentlyDiscardForTesting(
-          page_node2.get()));
+  EXPECT_TRUE(CanDiscard(page_node2.get(), DiscardReason::URGENT));
 
   process_node()->set_resident_set_kb(1024);
   process_node2->set_resident_set_kb(1024);
@@ -373,7 +393,7 @@
       /*reclaim_target_kb*/ 2048,
       /*discard_protected_tabs*/ true,
       base::BindOnce([](bool success) { EXPECT_TRUE(success); }),
-      ::mojom::LifecycleUnitDiscardReason::URGENT);
+      DiscardReason::URGENT);
   ::testing::Mock::VerifyAndClearExpectations(discarder());
 }
 
@@ -388,9 +408,7 @@
   main_frame_node2->SetIsCurrent(true);
   testing::MakePageNodeDiscardable(page_node2.get(), task_env());
 
-  EXPECT_TRUE(
-      PageDiscardingHelper::GetFromGraph(graph())->CanUrgentlyDiscardForTesting(
-          page_node2.get()));
+  EXPECT_TRUE(CanDiscard(page_node2.get(), DiscardReason::URGENT));
 
   process_node()->set_resident_set_kb(1024);
   process_node2->set_resident_set_kb(1024);
@@ -404,7 +422,7 @@
       /*reclaim_target_kb*/ 1000000,
       /*discard_protected_tabs*/ false,
       base::BindOnce([](bool success) { EXPECT_TRUE(success); }),
-      ::mojom::LifecycleUnitDiscardReason::URGENT);
+      DiscardReason::URGENT);
   ::testing::Mock::VerifyAndClearExpectations(discarder());
 }
 
@@ -448,7 +466,7 @@
       /*reclaim_target_kb*/ 1500,
       /*discard_protected_tabs*/ true,
       base::BindOnce([](bool success) { EXPECT_TRUE(success); }),
-      ::mojom::LifecycleUnitDiscardReason::URGENT);
+      DiscardReason::URGENT);
   ::testing::Mock::VerifyAndClearExpectations(discarder());
   histogram_tester()->ExpectBucketCount("Discarding.DiscardCandidatesCount", 3,
                                         1);
@@ -498,7 +516,7 @@
       /*reclaim_target_kb*/ 1500,
       /*discard_protected_tabs*/ true,
       base::BindOnce([](bool success) { EXPECT_TRUE(success); }),
-      ::mojom::LifecycleUnitDiscardReason::URGENT);
+      DiscardReason::URGENT);
   ::testing::Mock::VerifyAndClearExpectations(discarder());
 }
 
@@ -524,7 +542,7 @@
       /*reclaim_target_kb*/ 10240,
       /*discard_protected_tabs*/ true,
       base::BindOnce([](bool success) { EXPECT_FALSE(success); }),
-      ::mojom::LifecycleUnitDiscardReason::URGENT);
+      DiscardReason::URGENT);
   ::testing::Mock::VerifyAndClearExpectations(discarder());
 }
 
@@ -534,7 +552,7 @@
   page_node()->SetIsVisible(true);
   PageDiscardingHelper::GetFromGraph(graph())->DiscardAPage(
       base::BindOnce([](bool success) { EXPECT_FALSE(success); }),
-      ::mojom::LifecycleUnitDiscardReason::URGENT);
+      DiscardReason::URGENT);
   ::testing::Mock::VerifyAndClearExpectations(discarder());
 }
 
@@ -543,7 +561,7 @@
       .WillOnce(Return(true));
   PageDiscardingHelper::GetFromGraph(graph())->DiscardAPage(
       base::BindOnce([](bool success) { EXPECT_TRUE(success); }),
-      ::mojom::LifecycleUnitDiscardReason::URGENT);
+      DiscardReason::URGENT);
   ::testing::Mock::VerifyAndClearExpectations(discarder());
   histogram_tester()->ExpectBucketCount("Discarding.DiscardCandidatesCount", 1,
                                         1);
@@ -554,7 +572,7 @@
       .WillOnce(Return(false));
   PageDiscardingHelper::GetFromGraph(graph())->DiscardAPage(
       base::BindOnce([](bool success) { EXPECT_FALSE(success); }),
-      ::mojom::LifecycleUnitDiscardReason::URGENT);
+      DiscardReason::URGENT);
   ::testing::Mock::VerifyAndClearExpectations(discarder());
   // There should be 2 discard attempts, during the first one an attempt will be
   // made to discard |page_node()|, on the second attempt no discard candidate
@@ -579,9 +597,7 @@
   AdvanceClock(base::Minutes(30));
   page_node2->SetIsVisible(false);
   AdvanceClock(base::Minutes(30));
-  EXPECT_TRUE(
-      PageDiscardingHelper::GetFromGraph(graph())->CanUrgentlyDiscardForTesting(
-          page_node2.get()));
+  EXPECT_TRUE(CanDiscard(page_node2.get(), DiscardReason::URGENT));
   EXPECT_GT(page_node()->TimeSinceLastVisibilityChange(),
             page_node2->TimeSinceLastVisibilityChange());
 
@@ -593,7 +609,7 @@
 
     PageDiscardingHelper::GetFromGraph(graph())->DiscardAPage(
         base::BindOnce([](bool success) { EXPECT_TRUE(success); }),
-        ::mojom::LifecycleUnitDiscardReason::URGENT);
+        DiscardReason::URGENT);
     ::testing::Mock::VerifyAndClearExpectations(discarder());
 
   histogram_tester()->ExpectBucketCount("Discarding.DiscardCandidatesCount", 2,
@@ -622,7 +638,7 @@
 
   PageDiscardingHelper::GetFromGraph(graph())->DiscardAPage(
       base::BindOnce([](bool success) { EXPECT_TRUE(success); }),
-      ::mojom::LifecycleUnitDiscardReason::URGENT);
+      DiscardReason::URGENT);
   ::testing::Mock::VerifyAndClearExpectations(discarder());
 }
 
@@ -648,7 +664,7 @@
 
   PageDiscardingHelper::GetFromGraph(graph())->DiscardAPage(
       base::BindOnce([](bool success) { EXPECT_TRUE(success); }),
-      ::mojom::LifecycleUnitDiscardReason::URGENT);
+      DiscardReason::URGENT);
   ::testing::Mock::VerifyAndClearExpectations(discarder());
 }
 
@@ -665,9 +681,7 @@
   AdvanceClock(base::Minutes(30));
   page_node()->SetIsVisible(false);
   AdvanceClock(base::Minutes(30));
-  EXPECT_TRUE(
-      PageDiscardingHelper::GetFromGraph(graph())->CanUrgentlyDiscardForTesting(
-          page_node()));
+  EXPECT_TRUE(CanDiscard(page_node(), DiscardReason::URGENT));
   EXPECT_GT(page_node2->TimeSinceLastVisibilityChange(),
             page_node()->TimeSinceLastVisibilityChange());
 
@@ -678,7 +692,7 @@
 
   PageDiscardingHelper::GetFromGraph(graph())->DiscardAPage(
       base::BindOnce([](bool success) { EXPECT_TRUE(success); }),
-      ::mojom::LifecycleUnitDiscardReason::URGENT);
+      DiscardReason::URGENT);
   ::testing::Mock::VerifyAndClearExpectations(discarder());
 }
 
@@ -697,9 +711,7 @@
   AdvanceClock(base::Minutes(30));
   page_node()->SetIsVisible(false);
   AdvanceClock(base::Minutes(30));
-  EXPECT_TRUE(
-      PageDiscardingHelper::GetFromGraph(graph())->CanUrgentlyDiscardForTesting(
-          page_node()));
+  EXPECT_TRUE(CanDiscard(page_node(), DiscardReason::URGENT));
   EXPECT_GT(page_node2->TimeSinceLastVisibilityChange(),
             page_node()->TimeSinceLastVisibilityChange());
 
@@ -712,7 +724,7 @@
       /*reclaim_target_kb*/ absl::nullopt,
       /*discard_protected_tabs*/ true,
       base::BindOnce([](bool success) { EXPECT_TRUE(success); }),
-      ::mojom::LifecycleUnitDiscardReason::URGENT);
+      DiscardReason::URGENT);
   ::testing::Mock::VerifyAndClearExpectations(discarder());
 }
 
diff --git a/chrome/browser/performance_manager/policies/urgent_page_discarding_policy.cc b/chrome/browser/performance_manager/policies/urgent_page_discarding_policy.cc
index 1b32b87..61e97b6 100644
--- a/chrome/browser/performance_manager/policies/urgent_page_discarding_policy.cc
+++ b/chrome/browser/performance_manager/policies/urgent_page_discarding_policy.cc
@@ -64,7 +64,7 @@
             policy->handling_memory_pressure_notification_ = false;
           },
           base::Unretained(this)),
-      ::mojom::LifecycleUnitDiscardReason::URGENT);
+      PageDiscardingHelper::DiscardReason::URGENT);
 }
 #endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
 
@@ -107,7 +107,7 @@
           // won't ever run after the destruction of this class and so it's safe
           // to use Unretained.
           base::Unretained(this)),
-      ::mojom::LifecycleUnitDiscardReason::URGENT);
+      PageDiscardingHelper::DiscardReason::URGENT);
 #endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
 }
 
diff --git a/chrome/browser/performance_manager/test_support/page_discarding_utils.cc b/chrome/browser/performance_manager/test_support/page_discarding_utils.cc
index 1ff7a290..c6489815 100644
--- a/chrome/browser/performance_manager/test_support/page_discarding_utils.cc
+++ b/chrome/browser/performance_manager/test_support/page_discarding_utils.cc
@@ -91,6 +91,9 @@
 
 void MakePageNodeDiscardable(PageNodeImpl* page_node,
                              content::BrowserTaskEnvironment& task_env) {
+  using CanDiscardResult = policies::PageDiscardingHelper::CanDiscardResult;
+  using DiscardReason = policies::PageDiscardingHelper::DiscardReason;
+
   page_node->SetIsVisible(false);
   page_node->SetIsAudible(false);
   const auto kUrl = GURL("https://foo.com");
@@ -98,8 +101,14 @@
                                             kUrl, "text/html");
   (*page_node->main_frame_nodes().begin())->OnNavigationCommitted(kUrl, false);
   task_env.FastForwardBy(base::Minutes(10));
-  DCHECK(policies::PageDiscardingHelper::GetFromGraph(page_node->graph())
-             ->CanUrgentlyDiscardForTesting(page_node));
+  const auto* helper =
+      policies::PageDiscardingHelper::GetFromGraph(page_node->graph());
+  CHECK_EQ(helper->CanDiscard(page_node, DiscardReason::URGENT),
+           CanDiscardResult::kEligible);
+  CHECK_EQ(helper->CanDiscard(page_node, DiscardReason::PROACTIVE),
+           CanDiscardResult::kEligible);
+  CHECK_EQ(helper->CanDiscard(page_node, DiscardReason::EXTERNAL),
+           CanDiscardResult::kEligible);
 }
 
 }  // namespace testing
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu.cc b/chrome/browser/renderer_context_menu/render_view_context_menu.cc
index 56f143c..6ffc3f0 100644
--- a/chrome/browser/renderer_context_menu/render_view_context_menu.cc
+++ b/chrome/browser/renderer_context_menu/render_view_context_menu.cc
@@ -1046,7 +1046,8 @@
     AppendLinkToTextItems();
   }
 
-  if (user_notes::IsUserNotesEnabled()) {
+  if (user_notes::IsUserNotesEnabled() && GetBrowser() &&
+      GetBrowser()->is_type_normal()) {
     AppendUserNotesItems();
   }
 
diff --git a/chrome/browser/resources/history/BUILD.gn b/chrome/browser/resources/history/BUILD.gn
index e31d6e35..2232bb6 100644
--- a/chrome/browser/resources/history/BUILD.gn
+++ b/chrome/browser/resources/history/BUILD.gn
@@ -68,4 +68,6 @@
       "lazy_load.js",
     ]
   }
+
+  enable_source_maps = enable_webui_inline_sourcemaps
 }
diff --git a/chrome/browser/resources/new_tab_page/lazy_load.ts b/chrome/browser/resources/new_tab_page/lazy_load.ts
index 4d88fce..984abe9d 100644
--- a/chrome/browser/resources/new_tab_page/lazy_load.ts
+++ b/chrome/browser/resources/new_tab_page/lazy_load.ts
@@ -16,6 +16,7 @@
 import './modules/module_descriptors.js';
 import 'chrome://resources/cr_components/most_visited/most_visited.js';
 
+export {ImageServiceBrowserProxy} from 'chrome://resources/cr_components/image_service/browser_proxy.js';
 export {CustomizeBackgroundsElement} from './customize_backgrounds.js';
 export {CustomizeDialogElement} from './customize_dialog.js';
 export {CustomizeModulesElement} from './customize_modules.js';
diff --git a/chrome/browser/resources/new_tab_page/modules/history_clusters/tile.ts b/chrome/browser/resources/new_tab_page/modules/history_clusters/tile.ts
index 52da3c4..b881613 100644
--- a/chrome/browser/resources/new_tab_page/modules/history_clusters/tile.ts
+++ b/chrome/browser/resources/new_tab_page/modules/history_clusters/tile.ts
@@ -72,8 +72,11 @@
           await ImageServiceBrowserProxy.getInstance().handler.getPageImageUrl(
               ImageServiceClientId.NtpQuests, visitUrl,
               {suggestImages: false, optimizationGuideImages: true});
-      if (result && result.result) {
-        this.imageUrl_ = result.result.imageUrl;
+      const success = !!(result && result.result);
+      chrome.metricsPrivate.recordBoolean(
+          'NewTabPage.HistoryClusters.ImageLoadSuccess', success);
+      if (success) {
+        this.imageUrl_ = result!.result!.imageUrl;
         return;
       }
     }
diff --git a/chrome/browser/resources/settings/about_page/about_page.html b/chrome/browser/resources/settings/about_page/about_page.html
index 06e0736..13262ca 100644
--- a/chrome/browser/resources/settings/about_page/about_page.html
+++ b/chrome/browser/resources/settings/about_page/about_page.html
@@ -130,6 +130,12 @@
           button-aria-description="$i18n{opensInNewTab}"
           label="$i18n{aboutReportAnIssue}" external></cr-link-row>
 </if>
+      <cr-link-row class="hr" id="getTheMostOutOfProgram"
+          on-click="onGetTheMostOutOfProgramTap_"
+          label="$i18n{getTheMostOutOfProgram}"
+          sub-label="$i18n{getTheMostOutOfProgramDescription}"
+          role-description="$i18n{subpageArrowRoleDescription}"
+          hidden$="[[!showGetTheMostOutOfProgramSection_]]"></cr-link-row>
       <cr-link-row class="hr" on-click="onManagementPageTap_"
           start-icon="cr:domain" label="$i18n{managementPage}"
           role-description="$i18n{subpageArrowRoleDescription}"
diff --git a/chrome/browser/resources/settings/about_page/about_page.ts b/chrome/browser/resources/settings/about_page/about_page.ts
index 33ffbcbe..cf1a524 100644
--- a/chrome/browser/resources/settings/about_page/about_page.ts
+++ b/chrome/browser/resources/settings/about_page/about_page.ts
@@ -34,10 +34,11 @@
 
 import {getTemplate} from './about_page.html.js';
 import {AboutPageBrowserProxy, AboutPageBrowserProxyImpl, UpdateStatus, UpdateStatusChangedEvent} from './about_page_browser_proxy.js';
+// clang-format off
 // <if expr="_google_chrome and is_macosx">
 import {PromoteUpdaterStatus} from './about_page_browser_proxy.js';
-
 // </if>
+// clang-format on
 
 const SettingsAboutPageElementBase =
     RelaunchMixin(WebUiListenerMixin(I18nMixin(PolymerElement)));
@@ -74,6 +75,16 @@
         },
       },
 
+      /**
+       * Whether to show the "Get the most out of Chrome" section.
+       */
+      showGetTheMostOutOfProgramSection_: {
+        type: Boolean,
+        value() {
+          return loadTimeData.getBoolean('showGetTheMostOutOfProgramSection');
+        },
+      },
+
       // <if expr="_google_chrome and is_macosx">
       promoteUpdaterStatus_: Object,
       // </if>
@@ -117,6 +128,7 @@
 
   private currentUpdateStatusEvent_: UpdateStatusChangedEvent|null;
   private isManaged_: boolean;
+  private showGetTheMostOutOfProgramSection_: boolean;
 
   // <if expr="_google_chrome and is_macosx">
   private promoteUpdaterStatus_: PromoteUpdaterStatus;
@@ -201,6 +213,10 @@
     this.performRestart(RestartType.RELAUNCH);
   }
 
+  private onGetTheMostOutOfProgramTap_() {
+    // TODO(crbug.com/1423278): implement.
+  }
+
   // <if expr="not chromeos_ash">
   private updateShowUpdateStatus_() {
     if (this.obsoleteSystemInfo_.endOfLine) {
diff --git a/chrome/browser/resources/settings/chromeos/device_page/device_page.html b/chrome/browser/resources/settings/chromeos/device_page/device_page.html
index 6f25c7d..59c6b7b 100644
--- a/chrome/browser/resources/settings/chromeos/device_page/device_page.html
+++ b/chrome/browser/resources/settings/chromeos/device_page/device_page.html
@@ -157,9 +157,8 @@
   <template is="dom-if"
       route-path="/per-device-keyboard/remap-keys">
     <os-settings-subpage id="perDeviceKeyboardRemapKeysRow"
-        page-title="$i18n{remapKeyboardKeysRowLabel}"
-        keyboards="[[keyboards]]">
-      <settings-per-device-keyboard-remap-keys>
+        page-title="$i18n{remapKeyboardKeysRowLabel}">
+      <settings-per-device-keyboard-remap-keys keyboards="[[keyboards]]">
       </settings-per-device-keyboard-remap-keys>
     </os-settings-subpage>
   </template>
diff --git a/chrome/browser/resources/settings/chromeos/device_page/per_device_keyboard_remap_keys.html b/chrome/browser/resources/settings/chromeos/device_page/per_device_keyboard_remap_keys.html
index c780cd4..7092255 100644
--- a/chrome/browser/resources/settings/chromeos/device_page/per_device_keyboard_remap_keys.html
+++ b/chrome/browser/resources/settings/chromeos/device_page/per_device_keyboard_remap_keys.html
@@ -20,39 +20,46 @@
       default-remappings="[[defaultRemappings]]"
       key="[[modifierKey.kMeta]]" id="metaKey"
       meta-key="[[keyboard.metaKey]]"
-      pref="{{fakeMetaPref}}"></keyboard-remap-modifier-key-row>
+      pref="{{fakeMetaPref}}">
+  </keyboard-remap-modifier-key-row>
   <keyboard-remap-modifier-key-row
       default-remappings="[[defaultRemappings]]"
       key="[[modifierKey.kControl]]" id="ctrlKey"
       meta-key="[[keyboard.metaKey]]"
-      pref="{{fakeCtrlPref}}"></keyboard-remap-modifier-key-row>
+      pref="{{fakeCtrlPref}}">
+  </keyboard-remap-modifier-key-row>
   <keyboard-remap-modifier-key-row
       default-remappings="[[defaultRemappings]]"
       key="[[modifierKey.kAlt]]" id="altKey"
       meta-key="[[keyboard.metaKey]]"
-      pref="{{fakeAltPref}}"></keyboard-remap-modifier-key-row>
+      pref="{{fakeAltPref}}">
+  </keyboard-remap-modifier-key-row>
   <keyboard-remap-modifier-key-row
       default-remappings="[[defaultRemappings]]"
       key="[[modifierKey.kEscape]]" id="escapeKey"
       meta-key="[[keyboard.metaKey]]"
-      pref="{{fakeEscPref}}"></keyboard-remap-modifier-key-row>
+      pref="{{fakeEscPref}}">
+  </keyboard-remap-modifier-key-row>
   <keyboard-remap-modifier-key-row
       default-remappings="[[defaultRemappings]]"
       key="[[modifierKey.kBackspace]]" id="backspaceKey"
       meta-key="[[keyboard.metaKey]]"
-      pref="{{fakeBackspacePref}}"></keyboard-remap-modifier-key-row>
-  <template is="dom-if" if="[[hasAssistantKey_]]" restamp>
+      pref="{{fakeBackspacePref}}">
+  </keyboard-remap-modifier-key-row>
+  <template is="dom-if" if="[[hasAssistantKey]]" restamp>
     <keyboard-remap-modifier-key-row
       default-remappings="[[defaultRemappings]]"
       key="[[modifierKey.kAssistant]]" id="assistantKey"
       meta-key="[[keyboard.metaKey]]"
-      pref="{{fakeAssistantPref}}"></keyboard-remap-modifier-key-row>
+      pref="{{fakeAssistantPref}}">
+  </keyboard-remap-modifier-key-row>
   </template>
   <template is="dom-if" if="[[hasCapsLockKey]]" restamp>
     <keyboard-remap-modifier-key-row
       default-remappings="[[defaultRemappings]]"
       key="[[modifierKey.kCapsLock]]" id="capsLockKey"
       meta-key="[[keyboard.metaKey]]"
-      pref="{{fakeCapsLockPref}}"></keyboard-remap-modifier-key-row>
+      pref="{{fakeCapsLockPref}}">
+  </keyboard-remap-modifier-key-row>
   </template>
 </div>
diff --git a/chrome/browser/resources/settings/chromeos/device_page/per_device_keyboard_remap_keys.ts b/chrome/browser/resources/settings/chromeos/device_page/per_device_keyboard_remap_keys.ts
index b73c9cf6..f523a0b 100644
--- a/chrome/browser/resources/settings/chromeos/device_page/per_device_keyboard_remap_keys.ts
+++ b/chrome/browser/resources/settings/chromeos/device_page/per_device_keyboard_remap_keys.ts
@@ -19,12 +19,10 @@
 import {PolymerElementProperties} from 'chrome://resources/polymer/v3_0/polymer/interfaces.js';
 import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
-import {KeyboardSettingsObserverReceiver} from '../mojom-webui/input_device_settings_provider.mojom-webui.js';
 import {routes} from '../os_settings_routes.js';
 import {RouteObserverMixin, RouteObserverMixinInterface} from '../route_observer_mixin.js';
 import {Route, Router} from '../router.js';
 
-import {FakeInputDeviceSettingsProvider} from './fake_input_device_settings_provider.js';
 import {getInputDeviceSettingsProvider} from './input_device_mojo_interface_provider.js';
 import {InputDeviceSettingsProviderInterface, Keyboard, MetaKey, ModifierKey} from './input_device_settings_types.js';
 import {getTemplate} from './per_device_keyboard_remap_keys.html.js';
@@ -139,6 +137,9 @@
 
       keyboards: {
         type: Array,
+        // Prevents the `onKeyboardListUpdated` observer from firing
+        // when the page is first initialized.
+        value: undefined,
       },
 
       metaKeyLabel: {
@@ -159,6 +160,11 @@
         type: Boolean,
         value: false,
       },
+
+      keyboardId: {
+        type: Number,
+        value: -1,
+      },
     };
   }
 
@@ -171,6 +177,7 @@
           'fakeBackspacePref.value,' +
           'fakeAssistantPref.value,' +
           'fakeCapsLockPref.value)',
+      'onKeyboardListUpdated(keyboards.*)',
     ];
   }
 
@@ -180,7 +187,7 @@
 
   protected keyboard: Keyboard;
   private keyboards: Keyboard[];
-  private keyboardSettingsObserverReceiver: KeyboardSettingsObserverReceiver;
+  protected keyboardId: number;
   protected defaultRemappings: {[key: number]: ModifierKey} = {
     [ModifierKey.kMeta]: ModifierKey.kMeta,
     [ModifierKey.kControl]: ModifierKey.kControl,
@@ -204,17 +211,15 @@
   private metaKeyLabel: string;
   private isInitialized: boolean;
 
-  constructor() {
-    super();
-    this.observeKeyboardSettings();
-  }
-
   override currentRouteChanged(route: Route): void {
     // Does not apply to this page.
     if (route !== routes.PER_DEVICE_KEYBOARD_REMAP_KEYS) {
       return;
     }
-    this.getKeyboard();
+    if (this.hasKeyboards() &&
+        this.keyboardId !== this.getKeyboardIdFromUrl()) {
+      this.initializeKeyboard();
+    }
   }
 
   private computeModifierRemappings(): Map<ModifierKey, ModifierKey> {
@@ -236,19 +241,13 @@
    * Get the keyboard to display according to the keyboardId in the url query,
    * initializing the page and pref with the keyboard data.
    */
-  private async getKeyboard(): Promise<void> {
+  private initializeKeyboard(): void {
     // Set isInitialized to false to prevent calling update keyboard settings
     // api while the prefs are initializing.
     this.isInitialized = false;
-
-    const urlSearchQuery =
-        Router.getInstance().getQueryParameters().get('keyboardId');
-    assert(!!urlSearchQuery);
-
-    // Get the correct keyboard from inputDeviceSettingsProvider with the id.
-    const keyboardId = Number(urlSearchQuery);
-    const searchedKeyboard =
-        this.keyboards.find((keyboard: Keyboard) => keyboard.id === keyboardId);
+    this.keyboardId = this.getKeyboardIdFromUrl();
+    const searchedKeyboard = this.keyboards.find(
+        (keyboard: Keyboard) => keyboard.id === this.keyboardId);
     assert(!!searchedKeyboard);
     this.keyboard = searchedKeyboard;
     this.updateDefaultRemapping();
@@ -269,35 +268,23 @@
     this.isInitialized = true;
   }
 
-  private observeKeyboardSettings(): void {
-    if (this.inputDeviceSettingsProvider instanceof
-        FakeInputDeviceSettingsProvider) {
-      this.inputDeviceSettingsProvider.observeKeyboardSettings(this);
-      return;
-    }
-
-    this.keyboardSettingsObserverReceiver =
-        new KeyboardSettingsObserverReceiver(this);
-
-    this.inputDeviceSettingsProvider.observeKeyboardSettings(
-        this.keyboardSettingsObserverReceiver.$.bindNewPipeAndPassRemote());
+  private keyboardWasDisconnected(id: number): boolean {
+    return !this.keyboards.find(keyboard => keyboard.id === id);
   }
 
-  onKeyboardListUpdated(keyboards: Keyboard[]): void {
-    this.keyboards = keyboards;
+  onKeyboardListUpdated(): void {
     if (Router.getInstance().currentRoute !==
         routes.PER_DEVICE_KEYBOARD_REMAP_KEYS) {
       return;
     }
 
-    const keyboardFound = this.keyboard?.id &&
-        keyboards.find(keyboard => keyboard.id === this.keyboard.id);
-
-    // If the keyboard is disconnected in remapping page, go back to
-    // per_device_keyboard page.
-    if (!keyboardFound) {
+    if (!this.hasKeyboards() ||
+        this.keyboardWasDisconnected(this.getKeyboardIdFromUrl())) {
+      this.keyboardId = -1;
       Router.getInstance().navigateTo(routes.PER_DEVICE_KEYBOARD);
+      return;
     }
+    this.initializeKeyboard();
   }
 
   private defaultInitializePrefs(): void {
@@ -415,6 +402,14 @@
                                                        ModifierKey.kControl,
     };
   }
+
+  private getKeyboardIdFromUrl(): number {
+    return Number(Router.getInstance().getQueryParameters().get('keyboardId'));
+  }
+
+  private hasKeyboards(): boolean {
+    return this.keyboards?.length > 0;
+  }
 }
 
 declare global {
diff --git a/chrome/browser/resources/settings/chromeos/lazy_load.ts b/chrome/browser/resources/settings/chromeos/lazy_load.ts
index f3afdc9b..f427534 100644
--- a/chrome/browser/resources/settings/chromeos/lazy_load.ts
+++ b/chrome/browser/resources/settings/chromeos/lazy_load.ts
@@ -127,3 +127,4 @@
 export {OsResetBrowserProxyImpl} from './os_reset_page/os_reset_browser_proxy.js';
 export {GoogleAssistantBrowserProxyImpl} from './os_search_page/google_assistant_browser_proxy.js';
 export {ConsentStatus, DspHotwordState} from './os_search_page/google_assistant_subpage.js';
+export {SettingsSearchSubpageElement} from './os_search_page/search_subpage.js';
diff --git a/chrome/browser/resources/settings/chromeos/os_search_page/search_subpage.ts b/chrome/browser/resources/settings/chromeos/os_search_page/search_subpage.ts
index b2038d2..95676b6 100644
--- a/chrome/browser/resources/settings/chromeos/os_search_page/search_subpage.ts
+++ b/chrome/browser/resources/settings/chromeos/os_search_page/search_subpage.ts
@@ -37,7 +37,8 @@
 const SettingsSearchSubpageElementBase =
     DeepLinkingMixin(RouteObserverMixin(PrefsMixin(I18nMixin(PolymerElement))));
 
-class SettingsSearchSubpageElement extends SettingsSearchSubpageElementBase {
+export class SettingsSearchSubpageElement extends
+    SettingsSearchSubpageElementBase {
   static get is() {
     return 'settings-search-subpage';
   }
diff --git a/chrome/browser/resources/settings/chromeos/os_settings.ts b/chrome/browser/resources/settings/chromeos/os_settings.ts
index 771c54e9..eb4a21f 100644
--- a/chrome/browser/resources/settings/chromeos/os_settings.ts
+++ b/chrome/browser/resources/settings/chromeos/os_settings.ts
@@ -98,7 +98,10 @@
 export {getNearbyShareSettings, observeNearbyShareSettings, setNearbyShareSettingsForTesting} from '/shared/nearby_share_settings.js';
 export {NearbySettings, NearbyShareSettingsMixin} from '/shared/nearby_share_settings_mixin.js';
 export {CrButtonElement} from 'chrome://resources/cr_elements/cr_button/cr_button.js';
+export {CrToggleElement} from 'chrome://resources/cr_elements/cr_toggle/cr_toggle.js';
 export {OpenWindowProxyImpl} from 'chrome://resources/js/open_window_proxy.js';
+export {SettingsDropdownMenuElement} from '../controls/settings_dropdown_menu.js';
+export {SettingsSliderElement} from '../controls/settings_slider.js';
 export {SettingsToggleButtonElement} from '../controls/settings_toggle_button.js';
 export {LifetimeBrowserProxyImpl} from '../lifetime_browser_proxy.js';
 export {ProfileInfoBrowserProxyImpl} from '../people_page/profile_info_browser_proxy.js';
@@ -112,7 +115,7 @@
 export {fakeKeyboards, fakeKeyboards2, fakeMice, fakeMice2, fakePointingSticks, fakePointingSticks2, fakeTouchpads, fakeTouchpads2} from './device_page/fake_input_device_data.js';
 export {FakeInputDeviceSettingsProvider} from './device_page/fake_input_device_settings_provider.js';
 export {getInputDeviceSettingsProvider, setInputDeviceSettingsProviderForTesting, setupFakeInputDeviceSettingsProvider} from './device_page/input_device_mojo_interface_provider.js';
-export {MetaKey, ModifierKey} from './device_page/input_device_settings_types.js';
+export {MetaKey, ModifierKey, Mouse} from './device_page/input_device_settings_types.js';
 export {KeyboardRemapModifierKeyRowElement} from './device_page/keyboard_remap_modifier_key_row.js';
 export {SettingsPerDeviceKeyboardElement} from './device_page/per_device_keyboard.js';
 export {SettingsPerDeviceKeyboardRemapKeysElement} from './device_page/per_device_keyboard_remap_keys.js';
diff --git a/chrome/browser/resources/settings/images/iban.svg b/chrome/browser/resources/settings/images/iban.svg
index 36168fc..d0645210 100644
--- a/chrome/browser/resources/settings/images/iban.svg
+++ b/chrome/browser/resources/settings/images/iban.svg
@@ -1 +1 @@
-<svg width="32" height="20" viewBox="0 0 32 20" fill="none" xmlns="http://www.w3.org/2000/svg"><g clip-path="url(#a)"><rect x=".5" y=".5" width="31" height="19" rx="1.5" fill="#fff" stroke="#DADCE0"/><path d="m9.938 13.102 1.04-6.026h1.029l-1.04 6.026H9.938ZM14.91 13.061a3.362 3.362 0 0 1-.762.082h-2.395l.99-6.025h2.417c.608.01 1.018.225 1.23.641.13.257.177.563.143.92-.038.368-.147.664-.33.886-.098.125-.347.24-.53.343.25.112.428.29.535.531.108.242.143.538.106.884-.04.357-.153.677-.34.96a1.901 1.901 0 0 1-.432.476 1.574 1.574 0 0 1-.633.302h.002Zm-.446-2.57h-1.282l-.223 1.605h1.284c.23 0 .412-.036.548-.106.248-.131.396-.382.44-.752.038-.314-.049-.528-.256-.646-.117-.066-.286-.1-.51-.102h-.001Zm.778-1.15c.15-.098.239-.273.269-.527.03-.28-.041-.465-.211-.556a1.572 1.572 0 0 0-.593-.094h-1.105l-.175 1.328h1.252c.223 0 .412-.05.564-.15h-.001ZM20.341 7.118l.81 6.025h-1.295l-.12-1.238h-2.104l-.643 1.239H15.74l3.25-6.026H20.342Zm-.964 1.377-1.213 2.371h1.449l-.236-2.371ZM24.877 13.143l-1.803-4.28-.625 4.28h-1.112l.978-6.025h1.252l1.755 4.206.566-4.206H27l-.93 6.025h-1.195.002Z" fill="#002F70"/><path d="M7.064 13.045c-.556-.124-1.023-.278-1.388-.479C6.512 14.024 8.04 15 9.786 15c.762 0 1.482-.187 2.122-.518-.442.15-.914.234-1.404.234-1.38 0-2.614-.65-3.44-1.671ZM8.189 12.963a6.534 6.534 0 0 0 1.754-.104l.066-.389c-.404.045-.85.074-1.335.074-.258 0-.49-.014-.702-.038a6.002 6.002 0 0 1-.438-1.713c.001 0-.024-.285-.03-.434a5.52 5.52 0 0 1-.004-.216v-.105c0-.704.134-1.379.37-1.985a5.824 5.824 0 0 1 1.869-2.6 5.045 5.045 0 0 0-2.265 2.664 6.296 6.296 0 0 1-.965-.282c.752-1.504 2.258-2.53 3.995-2.53a4.32 4.32 0 0 1 1.518.274A4.612 4.612 0 0 0 9.786 5C7.855 5 6.19 6.195 5.434 7.916c.235.164.522.31.853.434v-.004c.304.118.65.213 1.023.282a5.355 5.355 0 0 0-.163 1.655A5.34 5.34 0 0 1 6 9.883c.011-.427.077-.839.19-1.23-.341-.12-.64-.265-.883-.422a5.188 5.188 0 0 0-.291 1.352c.206.304.553.585 1.008.798.341.146.734.266 1.169.352.087.626.284 1.215.567 1.745a4.062 4.062 0 0 1-1.324-.442 4.79 4.79 0 0 1-.375-1.249c-.456-.185-.816-.433-1.061-.71.01.642.134 1.253.354 1.815.32.338.833.632 1.47.83-.002-.002.526.163 1.223.228l.142.011v.002Z" fill="#0490F9"/><path d="M8.094 8.222c-.051.16-.096.322-.136.49.228.02.461.035.703.035.753 0 1.454-.106 2.041-.286l.048-.282a9.123 9.123 0 0 1-2.657.043ZM7.8 10.405c.007.145.018.287.035.427a8.92 8.92 0 0 0 2.468-.063l.065-.374c-.415.064-.86.098-1.32.098a8.54 8.54 0 0 1-1.247-.09l-.001.002Z" fill="#0490F9"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h32v20H0z"/></clipPath></defs></svg>
\ No newline at end of file
+<svg width="32" height="20" viewBox="0 0 32 20" fill="none" xmlns="http://www.w3.org/2000/svg"><g clip-path="url(#a)"><rect x=".5" y=".5" width="31" height="19" rx="1.5" fill="#fff" stroke="#DADCE0"/><path d="M5.494 13V6.556H6.71V13H5.494Zm2.981 0V6.556h2.52c.373 0 .706.078 1 .234.3.15.537.351.71.603.175.252.261.534.261.846 0 .336-.083.621-.252.855a1.577 1.577 0 0 1-.612.531v.072c.33.114.595.303.793.567.204.258.306.573.306.945 0 .366-.097.684-.289.954a1.876 1.876 0 0 1-.764.621 2.545 2.545 0 0 1-1.063.216h-2.61Zm1.215-2.772v1.665h1.35a1.1 1.1 0 0 0 .514-.108.754.754 0 0 0 .314-.297.837.837 0 0 0 .108-.423.855.855 0 0 0-.108-.432.744.744 0 0 0-.324-.297 1.202 1.202 0 0 0-.54-.108H9.69Zm0-1.044h1.216a1 1 0 0 0 .467-.099.724.724 0 0 0 .298-.288.74.74 0 0 0 .107-.387.758.758 0 0 0-.098-.387.697.697 0 0 0-.288-.27.892.892 0 0 0-.45-.108H9.69v1.539ZM13.903 13l2.412-6.444h1.395L20.131 13h-1.34l-.54-1.539h-2.467L15.244 13h-1.34Zm3.951-2.655-.567-1.62-.234-.774h-.072l-.234.774-.567 1.62h1.674ZM21.294 13V6.556h1.412l2.592 4.32h.072l-.072-1.242V6.556h1.206V13h-1.278L22.49 8.437h-.072l.072 1.242V13h-1.197Z" fill="#5F6368"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h32v20H0z"/></clipPath></defs></svg>
\ No newline at end of file
diff --git a/chrome/browser/safe_browsing/extension_telemetry/extension_telemetry_service.cc b/chrome/browser/safe_browsing/extension_telemetry/extension_telemetry_service.cc
index 8a334a7..bc2e688 100644
--- a/chrome/browser/safe_browsing/extension_telemetry/extension_telemetry_service.cc
+++ b/chrome/browser/safe_browsing/extension_telemetry/extension_telemetry_service.cc
@@ -678,19 +678,86 @@
     const RepeatedPtrField<ExtensionTelemetryReportRequest_SignalInfo>&
         signals = report_pb.signals();
     for (const auto& signal_pb : signals) {
-      const auto& tabs_execute_script_info_pb =
-          signal_pb.tabs_execute_script_info();
-      const RepeatedPtrField<
-          ExtensionTelemetryReportRequest_SignalInfo_TabsExecuteScriptInfo_ScriptInfo>&
-          scripts = tabs_execute_script_info_pb.scripts();
-      if (!scripts.empty()) {
-        ss << "  Signal: TabsExecuteScript\n";
-        for (const auto& script_pb : scripts) {
-          ss << "    Script hash: "
-             << base::HexEncode(script_pb.hash().c_str(),
-                                script_pb.hash().size())
-             << " count: " << script_pb.execution_count() << "\n";
+      // Tabs Execute Script
+      if (signal_pb.has_tabs_execute_script_info()) {
+        const auto& tabs_execute_script_info_pb =
+            signal_pb.tabs_execute_script_info();
+        const RepeatedPtrField<
+            ExtensionTelemetryReportRequest_SignalInfo_TabsExecuteScriptInfo_ScriptInfo>&
+            scripts = tabs_execute_script_info_pb.scripts();
+        if (!scripts.empty()) {
+          ss << "  Signal: TabsExecuteScript\n";
+          for (const auto& script_pb : scripts) {
+            ss << "    Script hash: "
+               << base::HexEncode(script_pb.hash().c_str(),
+                                  script_pb.hash().size())
+               << " count: " << script_pb.execution_count() << "\n";
+          }
         }
+        continue;
+      }
+
+      // Remote Host Contacted
+      if (signal_pb.has_remote_host_contacted_info()) {
+        const auto& remote_host_contacted_info_pb =
+            signal_pb.remote_host_contacted_info();
+        const RepeatedPtrField<
+            ExtensionTelemetryReportRequest_SignalInfo_RemoteHostContactedInfo_RemoteHostInfo>&
+            remote_host_infos = remote_host_contacted_info_pb.remote_host();
+        if (!remote_host_infos.empty()) {
+          ss << "  Signal: RemoteHostContacted\n";
+          for (const auto& remote_host_info_pb : remote_host_infos) {
+            ss << "    RemoteHostInfo:\n"
+               << "      URL: " << remote_host_info_pb.url() << "\n"
+               << "      ConnectionProtocal: "
+               << remote_host_info_pb.connection_protocol() << "\n"
+               << "      count: " << remote_host_info_pb.contact_count()
+               << "\n";
+          }
+        }
+        continue;
+      }
+
+      // Cookies Get All
+      if (signal_pb.has_cookies_get_all_info()) {
+        const auto& cookies_get_all_info_pb = signal_pb.cookies_get_all_info();
+        const RepeatedPtrField<
+            ExtensionTelemetryReportRequest_SignalInfo_CookiesGetAllInfo_GetAllArgsInfo>&
+            get_all_args_infos = cookies_get_all_info_pb.get_all_args_info();
+        if (!get_all_args_infos.empty()) {
+          ss << "  Signal: CookiesGetAll\n";
+          for (const auto& get_all_args_pb : get_all_args_infos) {
+            ss << "    GetAllArgsInfo:\n"
+               << "      Domain: " << get_all_args_pb.domain() << "\n"
+               << "      Name: " << get_all_args_pb.name() << "\n"
+               << "      Path: " << get_all_args_pb.path() << "\n"
+               << "      Secure: " << get_all_args_pb.secure() << "\n"
+               << "      StoreId: " << get_all_args_pb.store_id() << "\n"
+               << "      URL: " << get_all_args_pb.url() << "\n"
+               << "      IsSession: " << get_all_args_pb.is_session() << "\n"
+               << "      count: " << get_all_args_pb.count() << "\n";
+          }
+        }
+        continue;
+      }
+
+      // Cookies Get
+      if (signal_pb.has_cookies_get_info()) {
+        const auto& cookies_get_info_pb = signal_pb.cookies_get_info();
+        const RepeatedPtrField<
+            ExtensionTelemetryReportRequest_SignalInfo_CookiesGetInfo_GetArgsInfo>&
+            get_args_infos = cookies_get_info_pb.get_args_info();
+        if (!get_args_infos.empty()) {
+          ss << "  Signal: CookiesGet\n";
+          for (const auto& get_args_pb : get_args_infos) {
+            ss << "    GetArgsInfo:\n"
+               << "      Name: " << get_args_pb.name() << "\n"
+               << "      URL: " << get_args_pb.url() << "\n"
+               << "      StoreId: " << get_args_pb.store_id() << "\n"
+               << "      count: " << get_args_pb.count() << "\n";
+          }
+        }
+        continue;
       }
     }
   }
diff --git a/chrome/browser/safe_browsing/extension_telemetry/extension_telemetry_service.h b/chrome/browser/safe_browsing/extension_telemetry/extension_telemetry_service.h
index 73c8ce9..21edf21 100644
--- a/chrome/browser/safe_browsing/extension_telemetry/extension_telemetry_service.h
+++ b/chrome/browser/safe_browsing/extension_telemetry/extension_telemetry_service.h
@@ -120,6 +120,7 @@
   // signal processors.
   std::unique_ptr<ExtensionTelemetryReportRequest> CreateReport();
 
+  // Dumps a telemetry report in logs for testing.
   void DumpReportForTest(const ExtensionTelemetryReportRequest& report);
 
   // Collects extension information for reporting.
diff --git a/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/share_sheet/ChromeProvidedSharingOptionsProviderTest.java b/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/share_sheet/ChromeProvidedSharingOptionsProviderTest.java
index 0b8e118..e79695f1 100644
--- a/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/share_sheet/ChromeProvidedSharingOptionsProviderTest.java
+++ b/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/share_sheet/ChromeProvidedSharingOptionsProviderTest.java
@@ -11,21 +11,16 @@
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyString;
 
-import android.app.Activity;
-import android.content.Context;
-import android.content.ContextWrapper;
-import android.content.pm.PackageManager;
+import android.content.res.Resources;
 import android.view.View;
 
-import androidx.test.filters.MediumTest;
+import androidx.test.ext.junit.rules.ActivityScenarioRule;
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 
 import org.junit.After;
 import org.junit.Before;
-import org.junit.BeforeClass;
-import org.junit.ClassRule;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.TestRule;
@@ -33,20 +28,16 @@
 import org.mockito.Mock;
 import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
 
-import org.chromium.base.BuildInfo;
-import org.chromium.base.ContextUtils;
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.supplier.Supplier;
-import org.chromium.base.test.util.Batch;
-import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.util.JniMocker;
-import org.chromium.base.test.util.PackageManagerWrapper;
 import org.chromium.base.test.util.UserActionTester;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.feature_engagement.TrackerFactory;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
-import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.share.ChromeShareExtras.DetailedContentType;
 import org.chromium.chrome.browser.share.ShareContentTypeHelper;
@@ -57,8 +48,7 @@
 import org.chromium.chrome.browser.share.share_sheet.ShareSheetLinkToggleCoordinator.LinkToggleState;
 import org.chromium.chrome.browser.share.share_sheet.ShareSheetLinkToggleMetricsHelper.LinkToggleMetricsDetails;
 import org.chromium.chrome.browser.tab.Tab;
-import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
-import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
+import org.chromium.chrome.test.AutomotiveContextWrapperTestRule;
 import org.chromium.chrome.test.util.browser.Features;
 import org.chromium.chrome.test.util.browser.Features.DisableFeatures;
 import org.chromium.chrome.test.util.browser.Features.EnableFeatures;
@@ -69,10 +59,12 @@
 import org.chromium.components.user_prefs.UserPrefs;
 import org.chromium.components.user_prefs.UserPrefsJni;
 import org.chromium.content_public.browser.WebContents;
-import org.chromium.content_public.browser.test.util.TestThreadUtils;
+import org.chromium.ui.base.TestActivity;
 import org.chromium.ui.base.WindowAndroid;
 import org.chromium.ui.modelutil.PropertyModel;
 import org.chromium.url.GURL;
+import org.chromium.url.JUnitTestGURLs;
+import org.chromium.url.ShadowGURL;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -80,24 +72,23 @@
 /**
  * Instrumentation Unit tests {@link ChromeProvidedSharingOptionsProvider}.
  */
-@Batch(Batch.PER_CLASS)
-@RunWith(ChromeJUnit4ClassRunner.class)
+@RunWith(BaseRobolectricTestRunner.class)
 @EnableFeatures({ChromeFeatureList.WEBNOTES_STYLIZE})
 @DisableFeatures({ChromeFeatureList.UPCOMING_SHARING_FEATURES,
         ChromeFeatureList.SEND_TAB_TO_SELF_SIGNIN_PROMO})
-@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
+@Config(shadows = ShadowGURL.class)
 public class ChromeProvidedSharingOptionsProviderTest {
-    @ClassRule
-    public static ChromeTabbedActivityTestRule sActivityTestRule =
-            new ChromeTabbedActivityTestRule();
-
+    @Rule
+    public ActivityScenarioRule<TestActivity> mActivityScenarioRule =
+            new ActivityScenarioRule<>(TestActivity.class);
     @Rule
     public TestRule mFeatureProcessor = new Features.JUnitProcessor();
-
     @Rule
     public JniMocker mJniMocker = new JniMocker();
+    @Rule
+    public AutomotiveContextWrapperTestRule mAutoTestRule = new AutomotiveContextWrapperTestRule();
 
-    private static final String URL = "http://www.google.com/";
+    private static final String URL = JUnitTestGURLs.EXAMPLE_URL;
 
     @Mock
     private UserPrefs.Natives mUserPrefsNatives;
@@ -124,50 +115,13 @@
     @Mock
     private WindowAndroid mWindowAndroid;
 
-    private Activity mActivity;
+    private TestActivity mActivity;
     private ChromeProvidedSharingOptionsProvider mChromeProvidedSharingOptionsProvider;
     private UserActionTester mActionTester;
 
-    private Context mContextToRestore;
-    private TestContext mTestContext;
-
-    @BeforeClass
-    public static void setUpClass() {
-        sActivityTestRule.startMainActivityOnBlankPage();
-    }
-
-    private class TestContext extends ContextWrapper {
-        private boolean mIsAutomotive;
-
-        public TestContext(Context baseContext) {
-            super(baseContext);
-            mIsAutomotive = false;
-        }
-
-        public void setIsAutomotive(boolean isAutomotive) {
-            this.mIsAutomotive = isAutomotive;
-            TestThreadUtils.runOnUiThreadBlocking(BuildInfo::resetForTesting);
-        }
-
-        @Override
-        public PackageManager getPackageManager() {
-            return new PackageManagerWrapper(super.getPackageManager()) {
-                @Override
-                public boolean hasSystemFeature(String name) {
-                    if (name.equals(PackageManager.FEATURE_AUTOMOTIVE)) {
-                        return mIsAutomotive;
-                    }
-                    return super.hasSystemFeature(name);
-                }
-            };
-        }
-    }
-
     @Before
     public void setUp() {
-        mContextToRestore = ContextUtils.getApplicationContext();
-        mTestContext = new TestContext(mContextToRestore);
-        ContextUtils.initApplicationContextForTests(mTestContext);
+        mActivityScenarioRule.getScenario().onActivity(activity -> mActivity = activity);
 
         MockitoAnnotations.initMocks(this);
         mJniMocker.mock(UserPrefsJni.TEST_HOOKS, mUserPrefsNatives);
@@ -184,23 +138,15 @@
         Mockito.doNothing().when(mBottomSheetController).hideContent(any(), anyBoolean());
 
         TrackerFactory.setTrackerForTests(mTracker);
-        mActivity = sActivityTestRule.getActivity();
     }
 
     @After
     public void tearDown() throws Exception {
-        // DisableAnimationTestRule requires an initialized context to do proper teardown.
-        // This resets to the original context rather than nulling out.
-        if (mContextToRestore != null) {
-            ContextUtils.initApplicationContextForTests(mContextToRestore);
-        }
-        TestThreadUtils.runOnUiThreadBlocking(BuildInfo::resetForTesting);
         TrackerFactory.setTrackerForTests(null);
         if (mActionTester != null) mActionTester.tearDown();
     }
 
     @Test
-    @MediumTest
     public void getPropertyModels_longScreenshotEnabledNoTab_excludesLongScreenshot() {
         Mockito.when(mTabProvider.hasValue()).thenReturn(false);
         setUpChromeProvidedSharingOptionsProviderTest(/*isIncognito=*/false,
@@ -216,7 +162,6 @@
     }
 
     @Test
-    @MediumTest
     public void getPropertyModels_printingEnabledNoTab_excludesPrinting() {
         Mockito.when(mTabProvider.hasValue()).thenReturn(false);
         setUpChromeProvidedSharingOptionsProviderTest(/*isIncognito=*/false,
@@ -232,7 +177,6 @@
     }
 
     @Test
-    @MediumTest
     public void getPropertyModels_printingEnabled_includesPrinting() {
         setUpChromeProvidedSharingOptionsProviderTest(/*isIncognito=*/false,
                 /*printingEnabled=*/true, LinkGeneration.MAX);
@@ -247,7 +191,6 @@
     }
 
     @Test
-    @MediumTest
     public void getPropertyModels_multiWindow_doesNotIncludeScreenshot() {
         setUpChromeProvidedSharingOptionsProviderTest(/*isIncognito=*/false,
                 /*printingEnabled=*/false, LinkGeneration.MAX);
@@ -263,7 +206,6 @@
     }
 
     @Test
-    @MediumTest
     public void getPropertyModels_isIncognito_doesNotIncludeQrCode() {
         setUpChromeProvidedSharingOptionsProviderTest(/*isIncognito=*/true,
                 /*printingEnabled=*/false, LinkGeneration.MAX);
@@ -279,7 +221,6 @@
     }
 
     @Test
-    @MediumTest
     public void getPropertyModels_filtersByContentType() {
         setUpChromeProvidedSharingOptionsProviderTest(/*isIncognito=*/false,
                 /*printingEnabled=*/true, LinkGeneration.MAX);
@@ -297,7 +238,6 @@
     }
 
     @Test
-    @MediumTest
     public void getPropertyModels_multipleTypes_filtersByContentType() {
         setUpChromeProvidedSharingOptionsProviderTest(/*isIncognito=*/false,
                 /*printingEnabled=*/true, LinkGeneration.MAX);
@@ -322,7 +262,6 @@
     }
 
     @Test
-    @MediumTest
     public void getPropertyModels_doesNotFilterByDetailedContentType() {
         setUpChromeProvidedSharingOptionsProviderTest(/*isIncognito=*/false,
                 /*printingEnabled=*/true, LinkGeneration.MAX);
@@ -344,7 +283,6 @@
     }
 
     @Test
-    @MediumTest
     public void getPropertyModels_webnotes_filtersByDetailedContentType() {
         setUpChromeProvidedSharingOptionsProviderTest(/*isIncognito=*/false,
                 /*printingEnabled=*/true, LinkGeneration.MAX);
@@ -363,7 +301,6 @@
     }
 
     @Test
-    @MediumTest
     public void getPropertyModels_onClick_callsOnTargetChosen() {
         setUpChromeProvidedSharingOptionsProviderTest(/*isIncognito=*/false,
                 /*printingEnabled=*/false, LinkGeneration.LINK);
@@ -382,9 +319,8 @@
     }
 
     @Test
-    @MediumTest
     public void getPropertyModels_onlyReturnValidOptionsForAutomotive() {
-        mTestContext.setIsAutomotive(true);
+        mAutoTestRule.setIsAutomotive(true);
         setUpChromeProvidedSharingOptionsProviderTest(/*isIncognito=*/false,
                 /*printingEnabled=*/false, LinkGeneration.MAX);
 
@@ -401,6 +337,72 @@
                         mActivity.getResources().getString(R.string.qr_code_share_icon_label)));
     }
 
+    @Test
+    public void getPropertyModels_linkAndTextShare() {
+        setUpChromeProvidedSharingOptionsProviderTest(/*isIncognito=*/false,
+                /*printingEnabled=*/false, LinkGeneration.MAX);
+
+        List<PropertyModel> propertyModels =
+                mChromeProvidedSharingOptionsProvider.getPropertyModels(
+                        ImmutableSet.of(ContentType.LINK_AND_TEXT,
+                                ContentType.LINK_PAGE_NOT_VISIBLE, ContentType.TEXT),
+                        DetailedContentType.NOT_SPECIFIED,
+                        /*isMultiWindow=*/true);
+
+        assertCorrectModelsAreInTheRightOrder(propertyModels,
+                ImmutableList.of(mActivity.getResources().getString(R.string.sharing_copy),
+                        mActivity.getResources().getString(
+                                R.string.send_tab_to_self_share_activity_title),
+                        mActivity.getResources().getString(R.string.qr_code_share_icon_label)));
+    }
+
+    @Test
+    public void getPropertyModels_linkShare() {
+        setUpChromeProvidedSharingOptionsProviderTest(/*isIncognito=*/false,
+                /*printingEnabled=*/false, LinkGeneration.MAX);
+
+        List<PropertyModel> propertyModels =
+                mChromeProvidedSharingOptionsProvider.getPropertyModels(
+                        ImmutableSet.of(ContentType.LINK_PAGE_NOT_VISIBLE),
+                        DetailedContentType.NOT_SPECIFIED,
+                        /*isMultiWindow=*/true);
+
+        assertCorrectModelsAreInTheRightOrder(propertyModels,
+                ImmutableList.of(mActivity.getResources().getString(R.string.sharing_copy_url),
+                        mActivity.getResources().getString(
+                                R.string.send_tab_to_self_share_activity_title),
+                        mActivity.getResources().getString(R.string.qr_code_share_icon_label)));
+    }
+
+    @Test
+    public void getPropertyModels_textShare() {
+        setUpChromeProvidedSharingOptionsProviderTest(/*isIncognito=*/false,
+                /*printingEnabled=*/false, LinkGeneration.MAX);
+
+        List<PropertyModel> propertyModels =
+                mChromeProvidedSharingOptionsProvider.getPropertyModels(
+                        ImmutableSet.of(ContentType.TEXT), DetailedContentType.NOT_SPECIFIED,
+                        /*isMultiWindow=*/true);
+
+        assertCorrectModelsAreInTheRightOrder(propertyModels,
+                ImmutableList.of(mActivity.getResources().getString(R.string.sharing_copy_text)));
+    }
+
+    @Test
+    public void getShareDetailsMetrics_linkGeneration() {
+        @LinkGeneration
+        int linkGenerationStatus = LinkGeneration.LINK;
+
+        setUpChromeProvidedSharingOptionsProviderTest(/*isIncognito=*/false,
+                /*printingEnabled=*/false, linkGenerationStatus);
+        List<PropertyModel> propertyModels =
+                mChromeProvidedSharingOptionsProvider.getPropertyModels(
+                        ImmutableSet.of(ContentType.HIGHLIGHTED_TEXT),
+                        DetailedContentType.NOT_SPECIFIED, /*isMultiWindow=*/false);
+
+        assertCorrectLinkGenerationMetrics(propertyModels, linkGenerationStatus);
+    }
+
     private void setUpChromeProvidedSharingOptionsProviderTest(boolean isIncognito,
             boolean printingEnabled, @LinkGeneration int linkGenerationStatus) {
         Mockito.when(mPrefService.getBoolean(anyString())).thenReturn(printingEnabled);
@@ -448,17 +450,12 @@
         mActionTester = new UserActionTester();
         View view = Mockito.mock(View.class);
         for (PropertyModel propertyModel : propertyModels) {
-            // There is no link generation for Stylize Cards yet.
-            if (propertyModel.get(ShareSheetItemViewProperties.LABEL)
-                            .equals(mActivity.getResources().getString(
-                                    R.string.sharing_webnotes_create_card))) {
-                continue;
-            }
-
-            // There is no link generation for Screenshots yet either.
-            if (propertyModel.get(ShareSheetItemViewProperties.LABEL)
-                            .equals(mActivity.getResources().getString(
-                                    R.string.sharing_screenshot))) {
+            String label = propertyModel.get(ShareSheetItemViewProperties.LABEL);
+            Resources res = mActivity.getResources();
+            // There is no link generation for Stylize Cards / Screenshots / Long Screenshots.
+            if (label.equals(res.getString(R.string.sharing_webnotes_create_card))
+                    || label.equals(res.getString(R.string.sharing_screenshot))
+                    || label.equals(res.getString(R.string.sharing_long_screenshot))) {
                 continue;
             }
 
diff --git a/chrome/browser/share/android/test_java_sources.gni b/chrome/browser/share/android/test_java_sources.gni
index 565000c..264cd1e 100644
--- a/chrome/browser/share/android/test_java_sources.gni
+++ b/chrome/browser/share/android/test_java_sources.gni
@@ -12,7 +12,6 @@
   "//chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/scroll_capture/ScrollCaptureCallbackRenderTest.java",
   "//chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/send_tab_to_self/SendTabToSelfBottomSheetRenderTest.java",
   "//chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/send_tab_to_self/SendTabToSelfCoordinatorTest.java",
-  "//chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/share_sheet/ChromeProvidedSharingOptionsProviderTest.java",
   "//chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/share_sheet/ShareSheetTest.java",
 ]
 
@@ -38,6 +37,7 @@
   "//chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/scroll_capture/ScrollCaptureManagerTest.java",
   "//chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/send_tab_to_self/NotificationSharedPrefManagerTest.java",
   "//chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/send_tab_to_self/SendTabToSelfAndroidBridgeTest.java",
+  "//chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/share_sheet/ChromeProvidedSharingOptionsProviderTest.java",
   "//chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/share_sheet/ShareSheetCoordinatorTest.java",
   "//chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/share_sheet/ShareSheetLinkToggleCoordinatorTest.java",
   "//chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/share_sheet/ShareSheetUsageRankingHelperTest.java",
diff --git a/chrome/browser/ssl/certificate_reporting_test_utils.cc b/chrome/browser/ssl/certificate_reporting_test_utils.cc
index a6d3acc..1c45b7e 100644
--- a/chrome/browser/ssl/certificate_reporting_test_utils.cc
+++ b/chrome/browser/ssl/certificate_reporting_test_utils.cc
@@ -9,6 +9,7 @@
 #include "base/functional/bind.h"
 #include "base/functional/callback_helpers.h"
 #include "base/metrics/field_trial.h"
+#include "base/metrics/field_trial_params.h"
 #include "base/run_loop.h"
 #include "base/strings/string_number_conversions.h"
 #include "build/build_config.h"
@@ -17,7 +18,6 @@
 #include "components/safe_browsing/core/common/safe_browsing_prefs.h"
 #include "components/security_interstitials/content/cert_report_helper.h"
 #include "components/security_interstitials/content/certificate_error_report.h"
-#include "components/variations/variations_associated_data.h"
 #include "net/url_request/report_sender.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -118,9 +118,9 @@
       CertReportHelper::kFinchExperimentName);
 
   if (group_name == CertReportHelper::kFinchGroupShowPossiblySend) {
-    const std::string param = variations::GetVariationParamValue(
-        CertReportHelper::kFinchExperimentName,
-        CertReportHelper::kFinchParamName);
+    const std::string param =
+        base::GetFieldTrialParamValue(CertReportHelper::kFinchExperimentName,
+                                      CertReportHelper::kFinchParamName);
     double sendingThreshold;
     if (!base::StringToDouble(param, &sendingThreshold))
       return CERT_REPORT_NOT_EXPECTED;
diff --git a/chrome/browser/supervised_user/android/web_content_handler_impl.cc b/chrome/browser/supervised_user/android/web_content_handler_impl.cc
index c0d053e..4d004464c 100644
--- a/chrome/browser/supervised_user/android/web_content_handler_impl.cc
+++ b/chrome/browser/supervised_user/android/web_content_handler_impl.cc
@@ -40,7 +40,6 @@
 void WebContentHandlerImpl::RequestLocalApproval(
     const GURL& url,
     const std::u16string& child_display_name,
-    const gfx::ImageSkia& favicon,
     ApprovalRequestInitiatedCallback callback) {
   supervised_user::SupervisedUserSettingsService* settings_service =
       SupervisedUserSettingsServiceFactory::GetForKey(
diff --git a/chrome/browser/supervised_user/android/web_content_handler_impl.h b/chrome/browser/supervised_user/android/web_content_handler_impl.h
index 608b142..572cc45 100644
--- a/chrome/browser/supervised_user/android/web_content_handler_impl.h
+++ b/chrome/browser/supervised_user/android/web_content_handler_impl.h
@@ -29,7 +29,6 @@
   // supervised_user::WebContentHandler:
   void RequestLocalApproval(const GURL& url,
                             const std::u16string& child_display_name,
-                            const gfx::ImageSkia& favicon,
                             ApprovalRequestInitiatedCallback callback) override;
 
  private:
diff --git a/chrome/browser/supervised_user/chromeos/mock_large_icon_service.cc b/chrome/browser/supervised_user/chromeos/mock_large_icon_service.cc
new file mode 100644
index 0000000..131cc24
--- /dev/null
+++ b/chrome/browser/supervised_user/chromeos/mock_large_icon_service.cc
@@ -0,0 +1,45 @@
+// 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 "chrome/browser/supervised_user/chromeos/mock_large_icon_service.h"
+
+#include "base/task/cancelable_task_tracker.h"
+#include "components/favicon_base/favicon_types.h"
+#include "net/traffic_annotation/network_traffic_annotation.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "ui/gfx/image/image_unittest_util.h"
+
+using testing::_;
+
+namespace {
+const base::CancelableTaskTracker::TaskId kTaskId = 1;
+}  // namespace
+
+MockLargeIconService::MockLargeIconService() {
+  ON_CALL(*this, GetLargeIconOrFallbackStyleFromGoogleServerSkippingLocalCache(
+                     _, _, _, _, _))
+      .WillByDefault(
+          [this](auto, auto, auto, auto,
+                 favicon_base::GoogleFaviconServerCallback callback) {
+            StoreIconInCache();
+            std::move(callback).Run(
+                favicon_base::GoogleFaviconServerRequestStatus::SUCCESS);
+          });
+
+  ON_CALL(*this, GetLargeIconImageOrFallbackStyleForPageUrl(_, _, _, _, _))
+      .WillByDefault([this](auto, auto, auto,
+                            favicon_base::LargeIconImageCallback callback,
+                            auto) {
+        std::move(callback).Run(
+            favicon_base::LargeIconImageResult(gfx::Image(favicon_), kIconUrl));
+        return kTaskId;
+      });
+}
+
+MockLargeIconService::~MockLargeIconService() = default;
+
+void MockLargeIconService::StoreIconInCache() {
+  SkBitmap bitmap = gfx::test::CreateBitmap(1, 2);
+  favicon_ = gfx::ImageSkia::CreateFrom1xBitmap(bitmap);
+}
diff --git a/chrome/browser/supervised_user/chromeos/mock_large_icon_service.h b/chrome/browser/supervised_user/chromeos/mock_large_icon_service.h
new file mode 100644
index 0000000..816479e9
--- /dev/null
+++ b/chrome/browser/supervised_user/chromeos/mock_large_icon_service.h
@@ -0,0 +1,67 @@
+// 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 CHROME_BROWSER_SUPERVISED_USER_CHROMEOS_MOCK_LARGE_ICON_SERVICE_H_
+#define CHROME_BROWSER_SUPERVISED_USER_CHROMEOS_MOCK_LARGE_ICON_SERVICE_H_
+
+#include "components/favicon/core/large_icon_service.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "ui/gfx/image/image_skia.h"
+#include "url/gurl.h"
+
+class MockLargeIconService : public favicon::LargeIconService {
+ public:
+  MockLargeIconService();
+
+  MockLargeIconService(const MockLargeIconService&) = delete;
+  MockLargeIconService& operator=(const MockLargeIconService&) = delete;
+  ~MockLargeIconService() override;
+
+  void StoreIconInCache();
+
+  gfx::ImageSkia favicon() const { return favicon_; }
+
+  // LargeIconService overrides.
+  MOCK_METHOD5(GetLargeIconOrFallbackStyleFromGoogleServerSkippingLocalCache,
+               void(const GURL& page_url,
+                    bool may_page_url_be_private,
+                    bool should_trim_page_url_path,
+                    const net::NetworkTrafficAnnotationTag& traffic_annotation,
+                    favicon_base::GoogleFaviconServerCallback callback));
+  MOCK_METHOD5(GetLargeIconRawBitmapOrFallbackStyleForPageUrl,
+               base::CancelableTaskTracker::TaskId(
+                   const GURL& page_url,
+                   int min_source_size_in_pixel,
+                   int desired_size_in_pixel,
+                   favicon_base::LargeIconCallback callback,
+                   base::CancelableTaskTracker* tracker));
+  MOCK_METHOD5(GetLargeIconImageOrFallbackStyleForPageUrl,
+               base::CancelableTaskTracker::TaskId(
+                   const GURL& page_url,
+                   int min_source_size_in_pixel,
+                   int desired_size_in_pixel,
+                   favicon_base::LargeIconImageCallback callback,
+                   base::CancelableTaskTracker* tracker));
+  MOCK_METHOD5(GetLargeIconRawBitmapOrFallbackStyleForIconUrl,
+               base::CancelableTaskTracker::TaskId(
+                   const GURL& icon_url,
+                   int min_source_size_in_pixel,
+                   int desired_size_in_pixel,
+                   favicon_base::LargeIconCallback callback,
+                   base::CancelableTaskTracker* tracker));
+  MOCK_METHOD4(GetIconRawBitmapOrFallbackStyleForPageUrl,
+               base::CancelableTaskTracker::TaskId(
+                   const GURL& page_url,
+                   int desired_size_in_pixel,
+                   favicon_base::LargeIconCallback callback,
+                   base::CancelableTaskTracker* tracker));
+  MOCK_METHOD1(TouchIconFromGoogleServer, void(const GURL& icon_url));
+
+  const GURL kIconUrl = GURL("https://www.example.com/icon");
+
+ private:
+  gfx::ImageSkia favicon_;
+};
+
+#endif  // CHROME_BROWSER_SUPERVISED_USER_CHROMEOS_MOCK_LARGE_ICON_SERVICE_H_
diff --git a/chrome/browser/supervised_user/chromeos/supervised_user_favicon_request_handler_unittest.cc b/chrome/browser/supervised_user/chromeos/supervised_user_favicon_request_handler_unittest.cc
index 47e68da..52a9dcb 100644
--- a/chrome/browser/supervised_user/chromeos/supervised_user_favicon_request_handler_unittest.cc
+++ b/chrome/browser/supervised_user/chromeos/supervised_user_favicon_request_handler_unittest.cc
@@ -8,6 +8,7 @@
 #include "base/task/cancelable_task_tracker.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/task_environment.h"
+#include "chrome/browser/supervised_user/chromeos/mock_large_icon_service.h"
 #include "components/favicon/core/large_icon_service.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -18,87 +19,6 @@
 
 using testing::_;
 
-namespace {
-const base::CancelableTaskTracker::TaskId kTaskId = 1;
-}  // namespace
-
-class MockLargeIconService : public favicon::LargeIconService {
- public:
-  MockLargeIconService() {
-    ON_CALL(*this,
-            GetLargeIconOrFallbackStyleFromGoogleServerSkippingLocalCache(
-                _, _, _, _, _))
-        .WillByDefault(
-            [this](auto, auto, auto, auto,
-                   favicon_base::GoogleFaviconServerCallback callback) {
-              StoreIconInCache();
-              std::move(callback).Run(
-                  favicon_base::GoogleFaviconServerRequestStatus::SUCCESS);
-            });
-
-    ON_CALL(*this, GetLargeIconImageOrFallbackStyleForPageUrl(_, _, _, _, _))
-        .WillByDefault([this](auto, auto, auto,
-                              favicon_base::LargeIconImageCallback callback,
-                              auto) {
-          std::move(callback).Run(favicon_base::LargeIconImageResult(
-              gfx::Image(favicon_), kIconUrl));
-          return kTaskId;
-        });
-  }
-
-  MockLargeIconService(const MockLargeIconService&) = delete;
-  MockLargeIconService& operator=(const MockLargeIconService&) = delete;
-  ~MockLargeIconService() override = default;
-
-  void StoreIconInCache() {
-    SkBitmap bitmap = gfx::test::CreateBitmap(1, 2);
-    favicon_ = gfx::ImageSkia::CreateFrom1xBitmap(bitmap);
-  }
-
-  gfx::ImageSkia favicon() const { return favicon_; }
-
-  // LargeIconService overrides.
-  MOCK_METHOD5(GetLargeIconOrFallbackStyleFromGoogleServerSkippingLocalCache,
-               void(const GURL& page_url,
-                    bool may_page_url_be_private,
-                    bool should_trim_page_url_path,
-                    const net::NetworkTrafficAnnotationTag& traffic_annotation,
-                    favicon_base::GoogleFaviconServerCallback callback));
-  MOCK_METHOD5(GetLargeIconRawBitmapOrFallbackStyleForPageUrl,
-               base::CancelableTaskTracker::TaskId(
-                   const GURL& page_url,
-                   int min_source_size_in_pixel,
-                   int desired_size_in_pixel,
-                   favicon_base::LargeIconCallback callback,
-                   base::CancelableTaskTracker* tracker));
-  MOCK_METHOD5(GetLargeIconImageOrFallbackStyleForPageUrl,
-               base::CancelableTaskTracker::TaskId(
-                   const GURL& page_url,
-                   int min_source_size_in_pixel,
-                   int desired_size_in_pixel,
-                   favicon_base::LargeIconImageCallback callback,
-                   base::CancelableTaskTracker* tracker));
-  MOCK_METHOD5(GetLargeIconRawBitmapOrFallbackStyleForIconUrl,
-               base::CancelableTaskTracker::TaskId(
-                   const GURL& icon_url,
-                   int min_source_size_in_pixel,
-                   int desired_size_in_pixel,
-                   favicon_base::LargeIconCallback callback,
-                   base::CancelableTaskTracker* tracker));
-  MOCK_METHOD4(GetIconRawBitmapOrFallbackStyleForPageUrl,
-               base::CancelableTaskTracker::TaskId(
-                   const GURL& page_url,
-                   int desired_size_in_pixel,
-                   favicon_base::LargeIconCallback callback,
-                   base::CancelableTaskTracker* tracker));
-  MOCK_METHOD1(TouchIconFromGoogleServer, void(const GURL& icon_url));
-
-  const GURL kIconUrl = GURL("https://www.example.com/icon");
-
- private:
-  gfx::ImageSkia favicon_;
-};
-
 class SupervisedUserFaviconRequestHandlerTest : public ::testing::Test {
  public:
   SupervisedUserFaviconRequestHandlerTest() = default;
diff --git a/chrome/browser/supervised_user/chromeos/web_content_handler_impl.cc b/chrome/browser/supervised_user/chromeos/web_content_handler_impl.cc
index c0ef8684..70e3740 100644
--- a/chrome/browser/supervised_user/chromeos/web_content_handler_impl.cc
+++ b/chrome/browser/supervised_user/chromeos/web_content_handler_impl.cc
@@ -12,9 +12,13 @@
 #include "chrome/browser/ash/crosapi/parent_access_ash.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_key.h"
+#include "chrome/browser/supervised_user/chromeos/supervised_user_favicon_request_handler.h"
 #include "chrome/browser/supervised_user/supervised_user_settings_service_factory.h"
+#include "components/favicon/core/large_icon_service.h"
 #include "components/supervised_user/core/browser/supervised_user_settings_service.h"
+#include "components/supervised_user/core/common/features.h"
 #include "content/public/browser/web_contents.h"
+#include "ui/gfx/image/image_skia.h"
 
 namespace {
 
@@ -57,15 +61,28 @@
 
 }  // namespace
 
-WebContentHandlerImpl::WebContentHandlerImpl(content::WebContents& web_contents)
-    : web_contents_(web_contents) {}
+WebContentHandlerImpl::WebContentHandlerImpl(
+    content::WebContents& web_contents,
+    const GURL& url,
+    favicon::LargeIconService& large_icon_service)
+    : web_contents_(web_contents),
+      favicon_handler_(std::make_unique<SupervisedUserFaviconRequestHandler>(
+          url.GetWithEmptyPath(),
+          &large_icon_service)) {
+  if (supervised_user::IsLocalWebApprovalsEnabled()) {
+    // Prefetch the favicon which will be rendered as part of the web approvals
+    // ParentAccessDialog. Pass in DoNothing() for the favicon fetched callback
+    // because if the favicon is by the time the user triggers the opening of
+    // the ParentAccessDialog, we show the default favicon.
+    favicon_handler_->StartFaviconFetch(base::DoNothing());
+  }
+}
 
 WebContentHandlerImpl::~WebContentHandlerImpl() = default;
 
 void WebContentHandlerImpl::RequestLocalApproval(
     const GURL& url,
     const std::u16string& child_display_name,
-    const gfx::ImageSkia& favicon,
     ApprovalRequestInitiatedCallback callback) {
   supervised_user::SupervisedUserSettingsService* settings_service =
       SupervisedUserSettingsServiceFactory::GetForKey(
@@ -76,6 +93,8 @@
       crosapi::CrosapiManager::Get()->crosapi_ash()->parent_access_ash();
   DCHECK(parent_access);
 
+  gfx::ImageSkia favicon = favicon_handler_->GetFaviconOrFallback();
+
   parent_access->GetWebsiteParentApproval(
       url.GetWithEmptyPath(), child_display_name, favicon,
       base::BindOnce(&WebContentHandlerImpl::OnLocalApprovalRequestCompleted,
diff --git a/chrome/browser/supervised_user/chromeos/web_content_handler_impl.h b/chrome/browser/supervised_user/chromeos/web_content_handler_impl.h
index 7a58ea55..fe6b056 100644
--- a/chrome/browser/supervised_user/chromeos/web_content_handler_impl.h
+++ b/chrome/browser/supervised_user/chromeos/web_content_handler_impl.h
@@ -19,10 +19,18 @@
 class WebContents;
 }  // namespace content
 
+namespace favicon {
+class LargeIconService;
+}  // namespace favicon
+
+class SupervisedUserFaviconRequestHandler;
+
 // Chrome Ash specific implementation of web content handler.
 class WebContentHandlerImpl : public supervised_user::WebContentHandler {
  public:
-  explicit WebContentHandlerImpl(content::WebContents& web_contents);
+  WebContentHandlerImpl(content::WebContents& web_contents,
+                        const GURL& url,
+                        favicon::LargeIconService& large_icon_service);
 
   WebContentHandlerImpl(const WebContentHandlerImpl&) = delete;
   WebContentHandlerImpl& operator=(const WebContentHandlerImpl&) = delete;
@@ -31,7 +39,6 @@
   // supervised_user::WebContentHandler:
   void RequestLocalApproval(const GURL& url,
                             const std::u16string& child_display_name,
-                            const gfx::ImageSkia& favicon,
                             ApprovalRequestInitiatedCallback callback) override;
 
  private:
@@ -52,6 +59,7 @@
                            LocalWebApprovalErrorChromeOSTest);
 
   const raw_ref<content::WebContents> web_contents_;
+  std::unique_ptr<SupervisedUserFaviconRequestHandler> favicon_handler_;
   base::WeakPtrFactory<WebContentHandlerImpl> weak_ptr_factory_{this};
 };
 
diff --git a/chrome/browser/supervised_user/chromeos/web_content_handler_impl_unittest.cc b/chrome/browser/supervised_user/chromeos/web_content_handler_impl_unittest.cc
index 057fbec..3c60589 100644
--- a/chrome/browser/supervised_user/chromeos/web_content_handler_impl_unittest.cc
+++ b/chrome/browser/supervised_user/chromeos/web_content_handler_impl_unittest.cc
@@ -9,6 +9,8 @@
 
 #include "base/test/metrics/histogram_tester.h"
 #include "base/time/time.h"
+#include "chrome/browser/supervised_user/chromeos/mock_large_icon_service.h"
+#include "chrome/browser/supervised_user/chromeos/supervised_user_favicon_request_handler.h"
 #include "chromeos/crosapi/mojom/parent_access.mojom.h"
 #include "components/supervised_user/core/browser/supervised_user_settings_service.h"
 #include "content/public/test/browser_task_environment.h"
@@ -39,9 +41,13 @@
     return task_environment_;
   }
 
+  MockLargeIconService& large_icon_service() { return large_icon_service_; }
+
  private:
   content::BrowserTaskEnvironment task_environment_{
       base::test::TaskEnvironment::TimeSource::MOCK_TIME};
+
+  MockLargeIconService large_icon_service_;
 };
 
 TEST_F(WebContentHandlerImplTest, LocalWebApprovalApprovedChromeOSTest) {
@@ -63,8 +69,11 @@
   const base::TimeDelta approval_duration = base::Seconds(35);
   task_environment().FastForwardBy(approval_duration);
 
+  // TODO(b/273692421): The content will need to be a raw_ptr.
   content::WebContents* content = nullptr;
-  WebContentHandlerImpl web_content_handler = WebContentHandlerImpl(*content);
+  WebContentHandlerImpl web_content_handler(*content, url,
+                                            large_icon_service());
+
   web_content_handler.OnLocalApprovalRequestCompleted(
       supervisedUserSettingsServiceMock, url, start_time, std::move(result));
 
@@ -100,8 +109,11 @@
   const base::TimeDelta approval_duration = base::Seconds(35);
   task_environment().FastForwardBy(approval_duration);
 
+  // TODO(b/273692421): The content will need to be a raw_ptr.
   content::WebContents* content = nullptr;
-  WebContentHandlerImpl web_content_handler = WebContentHandlerImpl(*content);
+  WebContentHandlerImpl web_content_handler(*content, url,
+                                            large_icon_service());
+
   web_content_handler.OnLocalApprovalRequestCompleted(
       supervisedUserSettingsServiceMock, url, start_time, std::move(result));
 
@@ -137,8 +149,11 @@
   const base::TimeDelta approval_duration = base::Seconds(35);
   task_environment().FastForwardBy(approval_duration);
 
+  // TODO(b/273692421): The content will need to be a raw_ptr.
   content::WebContents* content = nullptr;
-  WebContentHandlerImpl web_content_handler = WebContentHandlerImpl(*content);
+  WebContentHandlerImpl web_content_handler(*content, url,
+                                            large_icon_service());
+
   web_content_handler.OnLocalApprovalRequestCompleted(
       supervisedUserSettingsServiceMock, url, start_time, std::move(result));
 
@@ -172,8 +187,11 @@
   const base::TimeDelta approval_duration = base::Seconds(35);
   task_environment().FastForwardBy(approval_duration);
 
+  // TODO(b/273692421): The content will need to be a raw_ptr.
   content::WebContents* content = nullptr;
-  WebContentHandlerImpl web_content_handler = WebContentHandlerImpl(*content);
+  WebContentHandlerImpl web_content_handler(*content, url,
+                                            large_icon_service());
+
   web_content_handler.OnLocalApprovalRequestCompleted(
       supervisedUserSettingsServiceMock, url, start_time, std::move(result));
 
diff --git a/chrome/browser/supervised_user/supervised_user_interstitial.cc b/chrome/browser/supervised_user/supervised_user_interstitial.cc
index 750f3bd..e814095 100644
--- a/chrome/browser/supervised_user/supervised_user_interstitial.cc
+++ b/chrome/browser/supervised_user/supervised_user_interstitial.cc
@@ -38,7 +38,6 @@
 #include "content/public/browser/web_contents_user_data.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/resource/resource_bundle.h"
-#include "ui/gfx/image/image_skia.h"
 
 #if BUILDFLAG(IS_ANDROID)
 #include "chrome/browser/supervised_user/child_accounts/child_account_feedback_reporter_android.h"
@@ -54,10 +53,6 @@
 #include "components/user_manager/user_manager.h"
 #endif
 
-#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
-#include "chrome/browser/supervised_user/chromeos/supervised_user_favicon_request_handler.h"
-#endif
-
 using content::WebContents;
 
 namespace {
@@ -159,7 +154,6 @@
     WebContents* web_contents,
     std::unique_ptr<supervised_user::WebContentHandler> web_content_handler,
     SupervisedUserService& supervised_user_service,
-    favicon::LargeIconService* large_icon_service,
     const GURL& url,
     supervised_user::FilteringBehaviorReason reason,
     int frame_id,
@@ -167,8 +161,7 @@
   std::unique_ptr<SupervisedUserInterstitial> interstitial =
       base::WrapUnique(new SupervisedUserInterstitial(
           web_contents, std::move(web_content_handler), supervised_user_service,
-          large_icon_service, url, reason, frame_id,
-          interstitial_navigation_id));
+          url, reason, frame_id, interstitial_navigation_id));
 
   if (web_contents->GetPrimaryMainFrame()->GetFrameTreeNodeId() == frame_id)
     CleanUpInfoBar(web_contents);
@@ -181,31 +174,17 @@
     WebContents* web_contents,
     std::unique_ptr<supervised_user::WebContentHandler> web_content_handler,
     SupervisedUserService& supervised_user_service,
-    favicon::LargeIconService* large_icon_service,
     const GURL& url,
     supervised_user::FilteringBehaviorReason reason,
     int frame_id,
     int64_t interstitial_navigation_id)
     : supervised_user_service_(supervised_user_service),
-      large_icon_service_(large_icon_service),
       web_content_handler_(std::move(web_content_handler)),
       web_contents_(web_contents),
       url_(url),
       reason_(reason),
       frame_id_(frame_id),
-      interstitial_navigation_id_(interstitial_navigation_id) {
-#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
-  if (supervised_user::IsLocalWebApprovalsEnabled()) {
-    favicon_handler_ = std::make_unique<SupervisedUserFaviconRequestHandler>(
-        url_.GetWithEmptyPath(), large_icon_service_);
-    // Prefetch the favicon which will be rendered as part of the web approvals
-    // ParentAccessDialog. Pass in DoNothing() for the favicon fetched callback
-    // because if the favicon is by the time the user triggers the opening of
-    // the ParentAccessDialog, we show the default favicon.
-    favicon_handler_->StartFaviconFetch(base::DoNothing());
-  }
-#endif
-}
+      interstitial_navigation_id_(interstitial_navigation_id) {}
 
 SupervisedUserInterstitial::~SupervisedUserInterstitial() {}
 
@@ -268,12 +247,8 @@
                             Commands::HISTOGRAM_BOUNDING_VALUE);
   OutputRequestPermissionSourceMetric();
 
-  gfx::ImageSkia favicon;
-#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
-  favicon = favicon_handler_->GetFaviconOrFallback();
-#endif
   web_content_handler_->RequestLocalApproval(url_, GetActiveUserFirstName(),
-                                             favicon, std::move(callback));
+                                             std::move(callback));
 }
 
 void SupervisedUserInterstitial::ShowFeedback() {
diff --git a/chrome/browser/supervised_user/supervised_user_interstitial.h b/chrome/browser/supervised_user/supervised_user_interstitial.h
index 42dd4cd..43ceb38 100644
--- a/chrome/browser/supervised_user/supervised_user_interstitial.h
+++ b/chrome/browser/supervised_user/supervised_user_interstitial.h
@@ -23,17 +23,9 @@
 class WebContentHandler;
 }
 
-namespace favicon {
-class LargeIconService;
-}  // namespace favicon
-
 class PrefService;
 class SupervisedUserService;
 
-#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
-class SupervisedUserFaviconRequestHandler;
-#endif
-
 // This class is used by SupervisedUserNavigationObserver to handle requests
 // from supervised user error page. The error page is shown when a page is
 // blocked because it is on a denylist (in "allow everything" mode), not on any
@@ -87,7 +79,6 @@
       content::WebContents* web_contents,
       std::unique_ptr<supervised_user::WebContentHandler> web_content_handler,
       SupervisedUserService& supervised_user_service,
-      favicon::LargeIconService* large_icon_service,
       const GURL& url,
       supervised_user::FilteringBehaviorReason reason,
       int frame_id,
@@ -118,7 +109,6 @@
       content::WebContents* web_contents,
       std::unique_ptr<supervised_user::WebContentHandler> web_content_handler,
       SupervisedUserService& supervised_user_service,
-      favicon::LargeIconService* large_icon_service,
       const GURL& url,
       supervised_user::FilteringBehaviorReason reason,
       int frame_id,
@@ -133,10 +123,6 @@
 
   const raw_ref<SupervisedUserService> supervised_user_service_;
 
-  // Can be null depending on the Platform. An actual instance
-  // is needed only for local web approvals on Chrome OS.
-  const raw_ptr<favicon::LargeIconService> large_icon_service_;
-
   std::unique_ptr<supervised_user::WebContentHandler> web_content_handler_;
 
   // Owns SupervisedUserNavigationObserver which owns us.
@@ -151,10 +137,6 @@
 
   // The Navigation ID of the navigation that last triggered the interstitial.
   int64_t interstitial_navigation_id_;
-
-#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
-  std::unique_ptr<SupervisedUserFaviconRequestHandler> favicon_handler_;
-#endif
 };
 
 #endif  // CHROME_BROWSER_SUPERVISED_USER_SUPERVISED_USER_INTERSTITIAL_H_
diff --git a/chrome/browser/supervised_user/supervised_user_navigation_observer.cc b/chrome/browser/supervised_user/supervised_user_navigation_observer.cc
index bcd8ef3..7aef1db 100644
--- a/chrome/browser/supervised_user/supervised_user_navigation_observer.cc
+++ b/chrome/browser/supervised_user/supervised_user_navigation_observer.cc
@@ -35,6 +35,7 @@
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/web_contents.h"
+#include "url/gurl.h"
 
 #if BUILDFLAG(IS_ANDROID)
 #include "chrome/browser/supervised_user/android/web_content_handler_impl.h"
@@ -47,9 +48,13 @@
 namespace {
 
 std::unique_ptr<supervised_user::WebContentHandler> CreateWebContentHandler(
-    content::WebContents* web_contents) {
+    content::WebContents* web_contents,
+    GURL url,
+    Profile* profile) {
 #if BUILDFLAG(IS_CHROMEOS_ASH)
-  return std::make_unique<WebContentHandlerImpl>(*web_contents);
+  return std::make_unique<WebContentHandlerImpl>(
+      *web_contents, url,
+      *LargeIconServiceFactory::GetForBrowserContext(profile));
 #elif BUILDFLAG(IS_ANDROID)
   return std::make_unique<WebContentHandlerImpl>(*web_contents);
 #else
@@ -307,10 +312,8 @@
       Profile::FromBrowserContext(web_contents()->GetBrowserContext());
   std::unique_ptr<SupervisedUserInterstitial> interstitial =
       SupervisedUserInterstitial::Create(
-          web_contents(), CreateWebContentHandler(web_contents()),
-          *supervised_user_service_,
-          LargeIconServiceFactory::GetForBrowserContext(profile), url, reason,
-          frame_id, navigation_id);
+          web_contents(), CreateWebContentHandler(web_contents(), url, profile),
+          *supervised_user_service_, url, reason, frame_id, navigation_id);
 
   supervised_user_interstitials_[frame_id] = std::move(interstitial);
 
diff --git a/chrome/browser/ui/android/webid/account_selection_view_android.cc b/chrome/browser/ui/android/webid/account_selection_view_android.cc
index ed6cb1b..77d4297 100644
--- a/chrome/browser/ui/android/webid/account_selection_view_android.cc
+++ b/chrome/browser/ui/android/webid/account_selection_view_android.cc
@@ -155,6 +155,24 @@
   // TODO(crbug.com/1357790): add support on Android.
 }
 
+std::string AccountSelectionViewAndroid::GetTitle() const {
+  JNIEnv* env = AttachCurrentThread();
+  ScopedJavaLocalRef<jstring> title =
+      Java_AccountSelectionBridge_getTitle(env, java_object_internal_);
+  CHECK(title);
+  return ConvertJavaStringToUTF8(title);
+}
+
+absl::optional<std::string> AccountSelectionViewAndroid::GetSubtitle() const {
+  JNIEnv* env = AttachCurrentThread();
+  ScopedJavaLocalRef<jstring> subtitle =
+      Java_AccountSelectionBridge_getSubtitle(env, java_object_internal_);
+  if (!subtitle) {
+    return absl::nullopt;
+  }
+  return ConvertJavaStringToUTF8(subtitle);
+}
+
 void AccountSelectionViewAndroid::OnAccountSelected(
     JNIEnv* env,
     const JavaParamRef<jobject>& idp_config_url,
diff --git a/chrome/browser/ui/android/webid/account_selection_view_android.h b/chrome/browser/ui/android/webid/account_selection_view_android.h
index 8daab35..b7d4403 100644
--- a/chrome/browser/ui/android/webid/account_selection_view_android.h
+++ b/chrome/browser/ui/android/webid/account_selection_view_android.h
@@ -29,6 +29,8 @@
       const std::string& top_frame_for_display,
       const std::string& idp_for_display,
       const content::IdentityProviderMetadata& idp_metadata) override;
+  std::string GetTitle() const override;
+  absl::optional<std::string> GetSubtitle() const override;
 
   void OnAccountSelected(
       JNIEnv* env,
diff --git a/chrome/browser/ui/android/webid/internal/java/src/org/chromium/chrome/browser/ui/android/webid/AccountSelectionBridge.java b/chrome/browser/ui/android/webid/internal/java/src/org/chromium/chrome/browser/ui/android/webid/AccountSelectionBridge.java
index 6cf212de..7d8b6be 100644
--- a/chrome/browser/ui/android/webid/internal/java/src/org/chromium/chrome/browser/ui/android/webid/AccountSelectionBridge.java
+++ b/chrome/browser/ui/android/webid/internal/java/src/org/chromium/chrome/browser/ui/android/webid/AccountSelectionBridge.java
@@ -92,6 +92,16 @@
                 Arrays.asList(accounts), idpMetadata, clientIdMetadata, isAutoReauthn);
     }
 
+    @CalledByNative
+    private String getTitle() {
+        return mAccountSelectionComponent.getTitle();
+    }
+
+    @CalledByNative
+    private String getSubtitle() {
+        return mAccountSelectionComponent.getSubtitle();
+    }
+
     @Override
     public void onDismissed(@IdentityRequestDialogDismissReason int dismissReason) {
         if (mNativeView != 0) {
diff --git a/chrome/browser/ui/android/webid/internal/java/src/org/chromium/chrome/browser/ui/android/webid/AccountSelectionCoordinator.java b/chrome/browser/ui/android/webid/internal/java/src/org/chromium/chrome/browser/ui/android/webid/AccountSelectionCoordinator.java
index 45c4a18..e154acf2 100644
--- a/chrome/browser/ui/android/webid/internal/java/src/org/chromium/chrome/browser/ui/android/webid/AccountSelectionCoordinator.java
+++ b/chrome/browser/ui/android/webid/internal/java/src/org/chromium/chrome/browser/ui/android/webid/AccountSelectionCoordinator.java
@@ -9,6 +9,7 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.LinearLayout;
+import android.widget.TextView;
 
 import androidx.annotation.Px;
 import androidx.recyclerview.widget.LinearLayoutManager;
@@ -125,4 +126,17 @@
     public void close() {
         mMediator.close();
     }
+
+    @Override
+    public String getTitle() {
+        TextView title = mBottomSheetContent.getContentView().findViewById(R.id.header_title);
+        return String.valueOf(title.getText());
+    }
+
+    @Override
+    public String getSubtitle() {
+        TextView subtitle = mBottomSheetContent.getContentView().findViewById(R.id.header_subtitle);
+        if (subtitle == null || subtitle.getText().length() == 0) return null;
+        return String.valueOf(subtitle.getText());
+    }
 }
diff --git a/chrome/browser/ui/android/webid/java/src/org/chromium/chrome/browser/ui/android/webid/AccountSelectionComponent.java b/chrome/browser/ui/android/webid/java/src/org/chromium/chrome/browser/ui/android/webid/AccountSelectionComponent.java
index 33253106..fa35208 100644
--- a/chrome/browser/ui/android/webid/java/src/org/chromium/chrome/browser/ui/android/webid/AccountSelectionComponent.java
+++ b/chrome/browser/ui/android/webid/java/src/org/chromium/chrome/browser/ui/android/webid/AccountSelectionComponent.java
@@ -53,4 +53,13 @@
      * Closes the outstanding bottom sheet.
      */
     void close();
+
+    /**
+     * Gets the sheet's title.
+     */
+    String getTitle();
+    /**
+     * Gets the sheet's subtitle, if any, or null..
+     */
+    String getSubtitle();
 }
diff --git a/chrome/browser/ui/ash/projector/pending_screencast_manager_browsertest.cc b/chrome/browser/ui/ash/projector/pending_screencast_manager_browsertest.cc
index 63f94db..7bb17cf 100644
--- a/chrome/browser/ui/ash/projector/pending_screencast_manager_browsertest.cc
+++ b/chrome/browser/ui/ash/projector/pending_screencast_manager_browsertest.cc
@@ -97,7 +97,8 @@
  public:
   PendingScreencastMangerBrowserTest() {
     scoped_feature_list_.InitWithFeatures(
-        {features::kProjectorUpdateIndexableText}, {});
+        {features::kProjectorUpdateIndexableText},
+        {ash::features::kFilesInlineSyncStatus});
   }
   PendingScreencastMangerBrowserTest(
       const PendingScreencastMangerBrowserTest&) = delete;
@@ -133,8 +134,9 @@
   virtual drive::DriveIntegrationService* CreateDriveIntegrationService(
       Profile* profile) {
     // Ignore non-user profile.
-    if (!ProfileHelper::IsUserProfile(profile))
+    if (!ProfileHelper::IsUserProfile(profile)) {
       return nullptr;
+    }
 
     base::ScopedAllowBlockingForTesting allow_blocking;
     base::FilePath mount_path = profile->GetPath().Append("drivefs");
@@ -336,6 +338,7 @@
   }
 
   base::HistogramTester histogram_tester_;
+  base::test::ScopedFeatureList scoped_feature_list_;
 
  private:
   void WaitBlockingTaskRunnerFinish() {
@@ -344,7 +347,6 @@
         FROM_HERE, run_loop.QuitClosure());
     run_loop.Run();
   }
-  base::test::ScopedFeatureList scoped_feature_list_;
 
   drive::DriveIntegrationServiceFactory::FactoryCallback
       create_drive_integration_service_;
diff --git a/chrome/browser/ui/ash/projector/screencast_manager_browsertest.cc b/chrome/browser/ui/ash/projector/screencast_manager_browsertest.cc
index 7b69ec9..19786ff 100644
--- a/chrome/browser/ui/ash/projector/screencast_manager_browsertest.cc
+++ b/chrome/browser/ui/ash/projector/screencast_manager_browsertest.cc
@@ -4,6 +4,7 @@
 
 #include <memory>
 
+#include "ash/constants/ash_features.h"
 #include "ash/webui/projector_app/buildflags.h"
 #include "ash/webui/projector_app/projector_app_client.h"
 #include "ash/webui/projector_app/projector_screencast.h"
@@ -77,6 +78,11 @@
 
 class ScreencastManagerTestWithDriveFs : public ScreencastManagerTest {
  public:
+  ScreencastManagerTestWithDriveFs() {
+    scoped_feature_list_.InitWithFeatures(
+        {}, {ash::features::kFilesInlineSyncStatus});
+  }
+
   // ScreencastManagerTest:
   void SetUpInProcessBrowserTestFixture() override {
     ScreencastManagerTest::SetUpInProcessBrowserTestFixture();
@@ -120,8 +126,9 @@
     const base::FilePath& absolute_path =
         GetTestFile(title, /*relative=*/false);
     // Writes a file with `kTestFileContents` if path doesn't exist.
-    if (!base::PathExists(absolute_path))
+    if (!base::PathExists(absolute_path)) {
       EXPECT_TRUE(base::WriteFile(absolute_path, kTestFileContents));
+    }
 
     const base::FilePath& relative_path = GetTestFile(title, /*relative=*/true);
     fake->SetMetadata(relative_path, content_type, title, false, false,
@@ -171,6 +178,8 @@
   }
 
  protected:
+  base::test::ScopedFeatureList scoped_feature_list_;
+
   drivefs::FakeDriveFs* GetFakeDriveFsForProfile(Profile* profile) {
     return &fake_drivefs_helpers_[profile]->fake_drivefs();
   }
diff --git a/chrome/browser/ui/popup_browsertest.cc b/chrome/browser/ui/popup_browsertest.cc
index 2724fed..b9cebda 100644
--- a/chrome/browser/ui/popup_browsertest.cc
+++ b/chrome/browser/ui/popup_browsertest.cc
@@ -7,10 +7,13 @@
 #include "base/command_line.h"
 #include "base/memory/raw_ptr.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/test/scoped_feature_list.h"
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/exclusive_access/exclusive_access_manager.h"
+#include "chrome/browser/ui/exclusive_access/fullscreen_controller.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
@@ -19,6 +22,8 @@
 #include "content/public/common/content_switches.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
+#include "third_party/blink/public/common/features.h"
+#include "third_party/blink/public/common/features_generated.h"
 #include "ui/display/display.h"
 #include "ui/display/screen.h"
 #include "ui/display/screen_base.h"
@@ -327,6 +332,11 @@
     : public PopupBrowserTest,
       public ::testing::WithParamInterface<bool> {
  public:
+  WindowManagementPopupBrowserTest() {
+    scoped_feature_list_.InitWithFeatures(
+        {blink::features::kFullscreenPopupWindows}, {});
+  }
+
   void TearDownOnMainThread() override {
 #if BUILDFLAG(IS_MAC)
     virtual_display_util_.reset();
@@ -418,6 +428,8 @@
   }
 
  private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+
 #if BUILDFLAG(IS_MAC)
   std::unique_ptr<display::test::VirtualDisplayMacUtil> virtual_display_util_;
 #endif
@@ -492,4 +504,157 @@
   display::Screen::SetScreenInstance(nullptr);
 #endif  //  !BUILDFLAG(IS_CHROMEOS_ASH) && !BUILDFLAG(IS_MAC)
 }
+
+IN_PROC_BROWSER_TEST_P(WindowManagementPopupBrowserTest, BasicFullscreen) {
+  SetUpWebServer();
+  SetUpWindowManagement();
+  Browser* new_popup =
+      OpenPopup(browser(), "open('/empty.html', '_blank', 'popup,fullscreen')");
+  content::WebContents* new_contents =
+      new_popup->tab_strip_model()->GetActiveWebContents();
+  if (ShouldTestWindowManagement()) {
+    WaitForHTMLFullscreen(new_contents);
+  }
+  EXPECT_EQ(EvalJs(new_contents,
+                   "!!document.fullscreenElement && document.fullscreenElement "
+                   "== document.documentElement")
+                .ExtractBool(),
+            ShouldTestWindowManagement());
+  FullscreenController* fullscreen_controller =
+      new_popup->exclusive_access_manager()->fullscreen_controller();
+  EXPECT_FALSE(fullscreen_controller->IsFullscreenForBrowser());
+  EXPECT_EQ(fullscreen_controller->IsTabFullscreen(),
+            ShouldTestWindowManagement());
+  EXPECT_EQ(EvalJs(new_contents, "document.exitFullscreen()").error.empty(),
+            ShouldTestWindowManagement());
+  EXPECT_FALSE(fullscreen_controller->IsFullscreenForBrowser());
+  EXPECT_FALSE(fullscreen_controller->IsTabFullscreen());
+
+  // Test that a navigation doesn't re-trigger fullscreen.
+  EXPECT_TRUE(EvalJs(new_contents,
+                     "window.location.href = '" +
+                         embedded_test_server()->GetURL("/title1.html").spec() +
+                         "'")
+                  .error.empty());
+  EXPECT_TRUE(content::WaitForLoadStop(new_contents));
+  EXPECT_FALSE(fullscreen_controller->IsFullscreenForBrowser());
+  EXPECT_FALSE(fullscreen_controller->IsTabFullscreen());
+}
+
+IN_PROC_BROWSER_TEST_P(WindowManagementPopupBrowserTest, FullscreenWithBounds) {
+  SetUpWebServer();
+  SetUpWindowManagement();
+  Browser* new_popup =
+      OpenPopup(browser(),
+                "open('/empty.html', '_blank', "
+                "'height=200,width=200,top=100,left=100,fullscreen')");
+  content::WebContents* new_contents =
+      new_popup->tab_strip_model()->GetActiveWebContents();
+  if (ShouldTestWindowManagement()) {
+    WaitForHTMLFullscreen(new_contents);
+  }
+  EXPECT_EQ(EvalJs(new_contents,
+                   "!!document.fullscreenElement && document.fullscreenElement "
+                   "== document.documentElement")
+                .ExtractBool(),
+            ShouldTestWindowManagement());
+  FullscreenController* fullscreen_controller =
+      new_popup->exclusive_access_manager()->fullscreen_controller();
+  EXPECT_FALSE(fullscreen_controller->IsFullscreenForBrowser());
+  EXPECT_EQ(fullscreen_controller->IsTabFullscreen(),
+            ShouldTestWindowManagement());
+}
+
+// Fullscreen should not work if the new window is not specified as a popup.
+IN_PROC_BROWSER_TEST_P(WindowManagementPopupBrowserTest,
+                       FullscreenRequiresPopupWindowFeature) {
+  SetUpWebServer();
+  SetUpWindowManagement();
+
+  // OpenPopup() cannot be used here since it waits for a new browser which
+  // would not open in this case.
+  auto* web_contents = browser()->tab_strip_model()->GetActiveWebContents();
+  EXPECT_TRUE(
+      EvalJs(web_contents, "open('/empty.html', '_blank', 'fullscreen')")
+          .error.empty());
+  EXPECT_EQ(browser()->tab_strip_model()->count(), 2);
+  EXPECT_FALSE(
+      EvalJs(web_contents, "!!document.fullscreenElement").ExtractBool());
+  FullscreenController* fullscreen_controller =
+      browser()->exclusive_access_manager()->fullscreen_controller();
+  EXPECT_FALSE(fullscreen_controller->IsFullscreenForBrowser());
+  EXPECT_FALSE(fullscreen_controller->IsTabFullscreen());
+}
+
+// Tests that the fullscreen flag is ignored if the window.open() does not
+// result in a new window.
+IN_PROC_BROWSER_TEST_P(WindowManagementPopupBrowserTest,
+                       FullscreenRequiresNewWindow) {
+  SetUpWebServer();
+  SetUpWindowManagement();
+
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(
+      browser(), embedded_test_server()->GetURL("/iframe.html")));
+  // OpenPopup() cannot be used here since it waits for a new browser which
+  // would not open in this case. open() targeting a frame named "test" in
+  // "iframe.html" will not create a new window.
+  auto* web_contents = browser()->tab_strip_model()->GetActiveWebContents();
+  EXPECT_TRUE(
+      EvalJs(web_contents, "open('/empty.html', 'test', 'popup,fullscreen')")
+          .error.empty());
+  EXPECT_EQ(browser()->tab_strip_model()->count(), 1);
+  EXPECT_FALSE(
+      EvalJs(web_contents, "!!document.fullscreenElement").ExtractBool());
+  FullscreenController* fullscreen_controller =
+      browser()->exclusive_access_manager()->fullscreen_controller();
+  EXPECT_FALSE(fullscreen_controller->IsFullscreenForBrowser());
+  EXPECT_FALSE(fullscreen_controller->IsTabFullscreen());
+}
+
+IN_PROC_BROWSER_TEST_P(WindowManagementPopupBrowserTest,
+                       FullscreenDifferentScreen) {
+  if (!SetUpVirtualDisplays()) {
+    GTEST_SKIP() << "Virtual displays not supported on this platform.";
+  }
+  SetUpWebServer();
+  SetUpWindowManagement();
+
+  // Falls back to opening a popup on the current screen in testing scenarios
+  // where window management is not granted in SetUpWindowManagement().
+  Browser* new_popup = OpenPopup(browser(), R"JS(
+    (() =>
+          {
+            otherScreen = (!!window.screenDetails && screenDetails.screens
+              .find(s => s != screenDetails.currentScreen)) || window.screen;
+            return open('/empty.html', '_blank',
+                    `top=${otherScreen.availTop},
+                    left=${otherScreen.availLeft},
+                    height=200,
+                    width=200,
+                    popup,
+                    fullscreen`);
+          })()
+  )JS");
+
+  content::WebContents* new_contents =
+      new_popup->tab_strip_model()->GetActiveWebContents();
+  if (ShouldTestWindowManagement()) {
+    WaitForHTMLFullscreen(new_contents);
+  }
+  EXPECT_EQ(EvalJs(new_contents,
+                   "!!document.fullscreenElement && "
+                   "document.fullscreenElement == document.documentElement")
+                .ExtractBool(),
+            ShouldTestWindowManagement());
+  EXPECT_TRUE(EvalJs(new_contents,
+                     "screen.availLeft == opener.otherScreen.availLeft && "
+                     "screen.availTop == opener.otherScreen.availTop")
+                  .ExtractBool());
+  FullscreenController* fullscreen_controller =
+      new_popup->exclusive_access_manager()->fullscreen_controller();
+  EXPECT_FALSE(fullscreen_controller->IsFullscreenForBrowser());
+  EXPECT_EQ(fullscreen_controller->IsTabFullscreen(),
+            ShouldTestWindowManagement());
+}
+
 }  // namespace
diff --git a/chrome/browser/ui/quick_answers/BUILD.gn b/chrome/browser/ui/quick_answers/BUILD.gn
index 137ac06..28817d9 100644
--- a/chrome/browser/ui/quick_answers/BUILD.gn
+++ b/chrome/browser/ui/quick_answers/BUILD.gn
@@ -20,6 +20,8 @@
     "ui/quick_answers_view.h",
     "ui/rich_answers_pre_target_handler.cc",
     "ui/rich_answers_pre_target_handler.h",
+    "ui/rich_answers_translation_view.cc",
+    "ui/rich_answers_translation_view.h",
     "ui/rich_answers_view.cc",
     "ui/rich_answers_view.h",
     "ui/user_consent_view.cc",
diff --git a/chrome/browser/ui/quick_answers/quick_answers_browsertest.cc b/chrome/browser/ui/quick_answers/quick_answers_browsertest.cc
index 6f3bbc8..b2a0a4ca 100644
--- a/chrome/browser/ui/quick_answers/quick_answers_browsertest.cc
+++ b/chrome/browser/ui/quick_answers/quick_answers_browsertest.cc
@@ -16,16 +16,20 @@
 #include "chrome/browser/notifications/notification_display_service.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/quick_answers/quick_answers_browsertest_base.h"
+#include "chrome/browser/ui/quick_answers/quick_answers_controller_impl.h"
 #include "chrome/browser/ui/quick_answers/ui/quick_answers_view.h"
 #include "chrome/browser/ui/quick_answers/ui/rich_answers_view.h"
 #include "chrome/browser/ui/quick_answers/ui/user_consent_view.h"
 #include "chrome/test/base/chrome_test_utils.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chromeos/components/quick_answers/public/cpp/quick_answers_prefs.h"
+#include "chromeos/components/quick_answers/quick_answers_model.h"
 #include "chromeos/constants/chromeos_features.h"
+#include "chromeos/strings/grit/chromeos_strings.h"
 #include "components/prefs/pref_service.h"
 #include "content/public/test/browser_test.h"
 #include "third_party/skia/include/core/SkBitmap.h"
+#include "ui/base/l10n/l10n_util.h"
 #include "ui/events/test/event_generator.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/message_center/public/cpp/notification.h"
@@ -225,6 +229,21 @@
       quick_answers_view_widget_waiter.WaitIfNeededAndGet();
   ASSERT_TRUE(quick_answers_view_widget != nullptr);
 
+  // Simulate having received a valid QuickAnswer response, which is necessary
+  // for triggering the rich answers view.
+  std::unique_ptr<quick_answers::QuickAnswer> quick_answer =
+      std::make_unique<quick_answers::QuickAnswer>();
+  quick_answer->result_type = ResultType::kTranslationResult;
+  quick_answer->title.push_back(
+      std::make_unique<quick_answers::QuickAnswerText>(
+          l10n_util::GetStringFUTF8(IDS_QUICK_ANSWERS_TRANSLATION_TITLE_TEXT,
+                                    u"prodotto", u"Italian")));
+  quick_answer->first_answer_row.push_back(
+      std::make_unique<quick_answers::QuickAnswerResultText>(
+          l10n_util::GetStringUTF8(IDS_QUICK_ANSWERS_TRANSLATION_INTENT)));
+  controller()->GetQuickAnswersDelegate()->OnQuickAnswerReceived(
+      std::move(quick_answer));
+
   // Click on the quick answers view to trigger the rich answers view.
   views::NamedWidgetShownWaiter rich_answers_view_widget_waiter(
       views::test::AnyWidgetTestPasskey(), RichAnswersView::kWidgetName);
@@ -232,11 +251,13 @@
       quick_answers_view_widget->GetWindowBoundsInScreen().CenterPoint());
   event_generator_->ClickLeftButton();
 
-  // Check that the quick answers view closes and the rich answers view pops up.
+  // Check that the quick answers view closes when the rich answers view shows.
   views::Widget* rich_answers_view_widget =
       rich_answers_view_widget_waiter.WaitIfNeededAndGet();
   ASSERT_TRUE(quick_answers_view_widget->IsClosed());
   ASSERT_TRUE(rich_answers_view_widget != nullptr);
+  ASSERT_TRUE(controller()->GetVisibilityForTesting() ==
+              QuickAnswersVisibility::kRichAnswersVisible);
 
   // Click outside the rich answers view window bounds to dismiss it.
   gfx::Rect rich_answers_bounds =
@@ -249,4 +270,37 @@
   ASSERT_TRUE(rich_answers_view_widget->IsClosed());
 }
 
+IN_PROC_BROWSER_TEST_F(RichAnswersBrowserTest,
+                       RichAnswersNotTriggeredOnInvalidResult) {
+  std::unique_ptr<ui::test::EventGenerator> event_generator_ =
+      std::make_unique<ui::test::EventGenerator>(
+          ash::Shell::GetPrimaryRootWindow());
+
+  views::NamedWidgetShownWaiter quick_answers_view_widget_waiter(
+      views::test::AnyWidgetTestPasskey(), QuickAnswersView::kWidgetName);
+
+  ShowMenuParams params;
+  params.selected_text = kTestQuery;
+  params.x = kCursorXToOverlapWithANotification;
+  params.y = kCursorYToOverlapWithANotification;
+  ShowMenu(params);
+
+  views::Widget* quick_answers_view_widget =
+      quick_answers_view_widget_waiter.WaitIfNeededAndGet();
+  ASSERT_TRUE(quick_answers_view_widget != nullptr);
+
+  // Click on the quick answers view. This should not trigger the
+  // rich answers view since no valid QuickAnswer result is provided.
+  views::NamedWidgetShownWaiter rich_answers_view_widget_waiter(
+      views::test::AnyWidgetTestPasskey(), RichAnswersView::kWidgetName);
+  event_generator_->MoveMouseTo(
+      quick_answers_view_widget->GetWindowBoundsInScreen().CenterPoint());
+  event_generator_->ClickLeftButton();
+
+  // Check that all quick answers views are closed.
+  ASSERT_TRUE(quick_answers_view_widget->IsClosed());
+  ASSERT_TRUE(controller()->GetVisibilityForTesting() ==
+              QuickAnswersVisibility::kClosed);
+}
+
 }  // namespace quick_answers
diff --git a/chrome/browser/ui/quick_answers/quick_answers_controller_impl.h b/chrome/browser/ui/quick_answers/quick_answers_controller_impl.h
index 4a11e32..6c4e9a2f 100644
--- a/chrome/browser/ui/quick_answers/quick_answers_controller_impl.h
+++ b/chrome/browser/ui/quick_answers/quick_answers_controller_impl.h
@@ -71,6 +71,8 @@
     return quick_answers_ui_controller_.get();
   }
 
+  quick_answers::QuickAnswer* quick_answer() { return quick_answer_.get(); }
+
  private:
   void HandleQuickAnswerRequest(
       const quick_answers::QuickAnswersRequest& request);
diff --git a/chrome/browser/ui/quick_answers/quick_answers_ui_controller.cc b/chrome/browser/ui/quick_answers/quick_answers_ui_controller.cc
index ad45d1a7..acc5b7a 100644
--- a/chrome/browser/ui/quick_answers/quick_answers_ui_controller.cc
+++ b/chrome/browser/ui/quick_answers/quick_answers_ui_controller.cc
@@ -102,15 +102,16 @@
   // Route dismissal through |controller_| for logging impressions.
   controller_->DismissQuickAnswers(QuickAnswersExitPoint::kQuickAnswersClick);
 
-  if (chromeos::features::IsQuickAnswersRichCardEnabled()) {
+  if (chromeos::features::IsQuickAnswersRichCardEnabled() &&
+      controller_->quick_answer() != nullptr &&
+      controller_->quick_answer()->result_type !=
+          quick_answers::ResultType::kNoResult) {
     auto* const rich_answers_view = new quick_answers::RichAnswersView(
         quick_answers_view_tracker_.view()->bounds(),
-        weak_factory_.GetWeakPtr());
-    rich_answers_view_tracker_.SetView(rich_answers_view);
+        weak_factory_.GetWeakPtr(), *controller_->quick_answer());
     rich_answers_view->GetWidget()->ShowInactive();
 
-    // Temporarily set rich-answers visibility here. Will move after
-    // setting up rich-answers request handling.
+    rich_answers_view_tracker_.SetView(rich_answers_view);
     controller_->SetVisibility(QuickAnswersVisibility::kRichAnswersVisible);
     return;
   }
diff --git a/chrome/browser/ui/quick_answers/ui/rich_answers_translation_view.cc b/chrome/browser/ui/quick_answers/ui/rich_answers_translation_view.cc
new file mode 100644
index 0000000..53fe5be
--- /dev/null
+++ b/chrome/browser/ui/quick_answers/ui/rich_answers_translation_view.cc
@@ -0,0 +1,83 @@
+// 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 "chrome/browser/ui/quick_answers/ui/rich_answers_translation_view.h"
+
+#include "base/functional/bind.h"
+#include "base/memory/raw_ptr.h"
+#include "chrome/browser/ui/color/chrome_color_id.h"
+#include "chrome/browser/ui/quick_answers/quick_answers_ui_controller.h"
+#include "chromeos/components/quick_answers/quick_answers_model.h"
+#include "chromeos/strings/grit/chromeos_strings.h"
+#include "components/vector_icons/vector_icons.h"
+#include "ui/aura/window.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/color/color_id.h"
+#include "ui/color/color_provider.h"
+#include "ui/display/screen.h"
+#include "ui/gfx/paint_vector_icon.h"
+#include "ui/views/accessibility/view_accessibility.h"
+#include "ui/views/background.h"
+#include "ui/views/border.h"
+#include "ui/views/controls/button/button.h"
+#include "ui/views/controls/button/button_controller.h"
+#include "ui/views/controls/button/image_button.h"
+#include "ui/views/controls/image_view.h"
+#include "ui/views/layout/fill_layout.h"
+#include "ui/views/widget/widget.h"
+#include "ui/wm/core/coordinate_conversion.h"
+
+// RichAnswersTranslationView
+// -----------------------------------------------------------
+
+RichAnswersTranslationView::RichAnswersTranslationView(
+    const quick_answers::QuickAnswer& result) {
+  InitLayout();
+
+  // Focus.
+  // We use custom focus behavior for the quick answers views.
+  // TODO (b/274665781): Add unit tests for focus behavior.
+  SetFocusBehavior(views::View::FocusBehavior::ALWAYS);
+  set_suppress_default_focus_handling();
+  focus_search_ = std::make_unique<QuickAnswersFocusSearch>(
+      this, base::BindRepeating(&RichAnswersTranslationView::GetFocusableViews,
+                                base::Unretained(this)));
+}
+
+RichAnswersTranslationView::~RichAnswersTranslationView() = default;
+
+const char* RichAnswersTranslationView::GetClassName() const {
+  return "RichAnswersTranslationView";
+}
+
+void RichAnswersTranslationView::OnFocus() {
+  View* wants_focus = focus_search_->FindNextFocusableView(
+      /* starting_view= */ nullptr,
+      views::FocusSearch::SearchDirection::kForwards,
+      views::FocusSearch::TraversalDirection::kDown,
+      views::FocusSearch::StartingViewPolicy::kCheckStartingView,
+      views::FocusSearch::AnchoredDialogPolicy::kSkipAnchoredDialog,
+      /* focus_traversable= */ nullptr,
+      /* focus_traversable_view= */ nullptr);
+  if (wants_focus != this) {
+    wants_focus->RequestFocus();
+  } else {
+    NotifyAccessibilityEvent(ax::mojom::Event::kFocus, true);
+  }
+}
+
+views::FocusTraversable* RichAnswersTranslationView::GetPaneFocusTraversable() {
+  return focus_search_.get();
+}
+
+void RichAnswersTranslationView::InitLayout() {
+  SetLayoutManager(std::make_unique<views::FillLayout>());
+}
+
+std::vector<views::View*> RichAnswersTranslationView::GetFocusableViews() {
+  std::vector<views::View*> focusable_views;
+  focusable_views.push_back(this);
+
+  return focusable_views;
+}
diff --git a/chrome/browser/ui/quick_answers/ui/rich_answers_translation_view.h b/chrome/browser/ui/quick_answers/ui/rich_answers_translation_view.h
new file mode 100644
index 0000000..30635116
--- /dev/null
+++ b/chrome/browser/ui/quick_answers/ui/rich_answers_translation_view.h
@@ -0,0 +1,52 @@
+// 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 CHROME_BROWSER_UI_QUICK_ANSWERS_UI_RICH_ANSWERS_TRANSLATION_VIEW_H_
+#define CHROME_BROWSER_UI_QUICK_ANSWERS_UI_RICH_ANSWERS_TRANSLATION_VIEW_H_
+
+#include <vector>
+
+#include "base/memory/raw_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/ui/quick_answers/ui/quick_answers_focus_search.h"
+#include "chrome/browser/ui/quick_answers/ui/rich_answers_view.h"
+#include "ui/events/event_handler.h"
+#include "ui/views/focus/focus_manager.h"
+#include "ui/views/view.h"
+
+namespace views {
+class ImageButton;
+}  // namespace views
+
+// A bubble style view to show QuickAnswer.
+class RichAnswersTranslationView : public views::View {
+ public:
+  explicit RichAnswersTranslationView(const quick_answers::QuickAnswer& result);
+
+  RichAnswersTranslationView(const RichAnswersTranslationView&) = delete;
+  RichAnswersTranslationView& operator=(const RichAnswersTranslationView&) =
+      delete;
+
+  ~RichAnswersTranslationView() override;
+
+  // views::View:
+  const char* GetClassName() const override;
+  void OnFocus() override;
+  views::FocusTraversable* GetPaneFocusTraversable() override;
+
+ private:
+  void InitLayout();
+
+  // QuickAnswersFocusSearch::GetFocusableViewsCallback to poll currently
+  // focusable views.
+  std::vector<views::View*> GetFocusableViews();
+
+  raw_ptr<views::View> base_view_ = nullptr;
+  raw_ptr<views::ImageButton> settings_button_ = nullptr;
+
+  std::unique_ptr<QuickAnswersFocusSearch> focus_search_;
+  base::WeakPtrFactory<RichAnswersTranslationView> weak_factory_{this};
+};
+
+#endif  // CHROME_BROWSER_UI_QUICK_ANSWERS_UI_QUICK_ANSWERS_VIEW_H_
diff --git a/chrome/browser/ui/quick_answers/ui/rich_answers_view.cc b/chrome/browser/ui/quick_answers/ui/rich_answers_view.cc
index bc220e7..cd1c3dd19 100644
--- a/chrome/browser/ui/quick_answers/ui/rich_answers_view.cc
+++ b/chrome/browser/ui/quick_answers/ui/rich_answers_view.cc
@@ -8,7 +8,10 @@
 #include "base/memory/raw_ptr.h"
 #include "chrome/browser/ui/color/chrome_color_id.h"
 #include "chrome/browser/ui/quick_answers/quick_answers_ui_controller.h"
+#include "chrome/browser/ui/quick_answers/ui/quick_answers_view.h"
 #include "chrome/browser/ui/quick_answers/ui/rich_answers_pre_target_handler.h"
+#include "chrome/browser/ui/quick_answers/ui/rich_answers_translation_view.h"
+#include "chromeos/components/quick_answers/quick_answers_model.h"
 #include "chromeos/strings/grit/chromeos_strings.h"
 #include "components/vector_icons/vector_icons.h"
 #include "ui/aura/window.h"
@@ -24,6 +27,7 @@
 #include "ui/views/controls/button/button_controller.h"
 #include "ui/views/controls/button/image_button.h"
 #include "ui/views/controls/image_view.h"
+#include "ui/views/controls/label.h"
 #include "ui/views/layout/fill_layout.h"
 #include "ui/views/layout/flex_layout.h"
 #include "ui/views/widget/widget.h"
@@ -31,6 +35,10 @@
 
 namespace {
 
+using quick_answers::QuickAnswer;
+using quick_answers::QuickAnswerResultText;
+using quick_answers::ResultType;
+
 // Buttons view.
 constexpr int kButtonsViewMarginDip = 4;
 constexpr int kButtonsSpacingDip = 4;
@@ -44,11 +52,28 @@
 
 namespace quick_answers {
 
+class RichAnswersTextLabel : public views::Label {
+ public:
+  explicit RichAnswersTextLabel(
+      const std::u16string& text,
+      ui::ColorId color_id = ui::kColorLabelForeground)
+      : Label(text) {
+    SetHorizontalAlignment(gfx::HorizontalAlignment::ALIGN_LEFT);
+    SetEnabledColorId(color_id);
+  }
+
+  RichAnswersTextLabel(const RichAnswersTextLabel&) = delete;
+  RichAnswersTextLabel& operator=(const RichAnswersTextLabel&) = delete;
+
+  ~RichAnswersTextLabel() override = default;
+};
+
 // RichAnswersView -----------------------------------------------------------
 
 RichAnswersView::RichAnswersView(
     const gfx::Rect& anchor_view_bounds,
-    base::WeakPtr<QuickAnswersUiController> controller)
+    base::WeakPtr<QuickAnswersUiController> controller,
+    const quick_answers::QuickAnswer& result)
     : anchor_view_bounds_(anchor_view_bounds),
       controller_(std::move(controller)),
       rich_answers_view_handler_(
@@ -57,7 +82,7 @@
           this,
           base::BindRepeating(&RichAnswersView::GetFocusableViews,
                               base::Unretained(this)))) {
-  InitLayout();
+  InitLayout(result);
   InitWidget();
 
   // Focus.
@@ -110,10 +135,10 @@
   node_data->role = ax::mojom::Role::kDialog;
 
   node_data->SetName(
-      l10n_util::GetStringUTF8(IDS_QUICK_ANSWERS_VIEW_A11Y_NAME_TEXT));
+      l10n_util::GetStringUTF8(IDS_RICH_ANSWERS_VIEW_A11Y_NAME_TEXT));
 }
 
-void RichAnswersView::InitLayout() {
+void RichAnswersView::InitLayout(const quick_answers::QuickAnswer& result) {
   SetLayoutManager(std::make_unique<views::FillLayout>());
 
   // Base view Layout.
@@ -123,10 +148,22 @@
   base_layout->SetOrientation(views::LayoutOrientation::kVertical)
       .SetCrossAxisAlignment(views::LayoutAlignment::kStretch);
 
-  // TODO(b/259440976): Add child views for each result type.
-
   // Add util buttons in the top-right corner.
   AddFrameButtons();
+
+  switch (result.result_type) {
+    case quick_answers::ResultType::kTranslationResult: {
+      content_view_ = base_view_->AddChildView(
+          std::make_unique<RichAnswersTranslationView>(result));
+      return;
+    }
+    case quick_answers::ResultType::kDefinitionResult:
+    case quick_answers::ResultType::kUnitConversionResult:
+    default: {
+      // TODO(b/259440976): Add child views for each result type.
+      return;
+    }
+  }
 }
 
 void RichAnswersView::InitWidget() {
diff --git a/chrome/browser/ui/quick_answers/ui/rich_answers_view.h b/chrome/browser/ui/quick_answers/ui/rich_answers_view.h
index d702d1a..f8d415134 100644
--- a/chrome/browser/ui/quick_answers/ui/rich_answers_view.h
+++ b/chrome/browser/ui/quick_answers/ui/rich_answers_view.h
@@ -22,15 +22,18 @@
 class QuickAnswersUiController;
 
 namespace quick_answers {
+
+struct QuickAnswer;
 class RichAnswersPreTargetHandler;
 
-// A bubble style view to show QuickAnswer.
+// A bubble style view to show RichAnswer.
 class RichAnswersView : public views::View {
  public:
   static constexpr char kWidgetName[] = "RichAnswersViewWidget";
 
   RichAnswersView(const gfx::Rect& anchor_view_bounds,
-                  base::WeakPtr<QuickAnswersUiController> controller);
+                  base::WeakPtr<QuickAnswersUiController> controller,
+                  const quick_answers::QuickAnswer& result);
 
   RichAnswersView(const RichAnswersView&) = delete;
   RichAnswersView& operator=(const RichAnswersView&) = delete;
@@ -45,7 +48,7 @@
   void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
 
  private:
-  void InitLayout();
+  void InitLayout(const quick_answers::QuickAnswer& result);
   void InitWidget();
   void AddFrameButtons();
   void UpdateBounds();
@@ -59,6 +62,7 @@
   base::WeakPtr<QuickAnswersUiController> controller_;
 
   raw_ptr<views::View> base_view_ = nullptr;
+  raw_ptr<views::View> content_view_ = nullptr;
   raw_ptr<views::ImageButton> settings_button_ = nullptr;
 
   std::unique_ptr<quick_answers::RichAnswersPreTargetHandler>
@@ -69,4 +73,4 @@
 
 }  // namespace quick_answers
 
-#endif  // CHROME_BROWSER_UI_QUICK_ANSWERS_UI_QUICK_ANSWERS_VIEW_H_
+#endif  // CHROME_BROWSER_UI_QUICK_ANSWERS_UI_RICH_ANSWERS_VIEW_H_
diff --git a/chrome/browser/ui/startup/default_browser_prompt.cc b/chrome/browser/ui/startup/default_browser_prompt.cc
index eb46357..688e415 100644
--- a/chrome/browser/ui/startup/default_browser_prompt.cc
+++ b/chrome/browser/ui/startup/default_browser_prompt.cc
@@ -10,6 +10,7 @@
 #include "base/functional/bind.h"
 #include "base/location.h"
 #include "base/memory/weak_ptr.h"
+#include "base/metrics/field_trial_params.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/task/single_thread_task_runner.h"
 #include "base/time/time.h"
@@ -28,7 +29,6 @@
 #include "components/infobars/content/content_infobar_manager.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
-#include "components/variations/variations_associated_data.h"
 #include "components/version_info/version_info.h"
 #include "content/public/browser/visibility.h"
 #include "content/public/browser/web_contents.h"
@@ -97,8 +97,8 @@
       profile->GetPrefs()->GetInt64(prefs::kDefaultBrowserLastDeclined);
   if (last_dismissed_value) {
     int period_days = 0;
-    base::StringToInt(variations::GetVariationParamValue(
-                          "DefaultBrowserInfobar", "RefreshPeriodDays"),
+    base::StringToInt(base::GetFieldTrialParamValue("DefaultBrowserInfobar",
+                                                    "RefreshPeriodDays"),
                       &period_days);
     if (period_days <= 0 || period_days == std::numeric_limits<int>::max())
       return false;  // Failed to parse a reasonable period.
diff --git a/chrome/browser/ui/ui_features.cc b/chrome/browser/ui/ui_features.cc
index 503e3d2..6673f990 100644
--- a/chrome/browser/ui/ui_features.cc
+++ b/chrome/browser/ui/ui_features.cc
@@ -72,6 +72,13 @@
              "EvDetailsInPageInfo",
              base::FEATURE_ENABLED_BY_DEFAULT);
 
+// Enables showing the "Get the most out of Chrome" section in settings.
+#if !defined(ANDROID)
+BASE_FEATURE(kGetTheMostOutOfProgram,
+             "GetTheMostOutOfProgram",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+#endif
+
 #if BUILDFLAG(ENABLE_EXTENSIONS)
 // Controls whether we use a different UX for simple extensions overriding
 // settings.
diff --git a/chrome/browser/ui/ui_features.h b/chrome/browser/ui/ui_features.h
index 93618be..19b2a553 100644
--- a/chrome/browser/ui/ui_features.h
+++ b/chrome/browser/ui/ui_features.h
@@ -49,6 +49,10 @@
 
 BASE_DECLARE_FEATURE(kEvDetailsInPageInfo);
 
+#if !defined(ANDROID)
+BASE_DECLARE_FEATURE(kGetTheMostOutOfProgram);
+#endif
+
 #if BUILDFLAG(ENABLE_EXTENSIONS)
 BASE_DECLARE_FEATURE(kLightweightExtensionOverrideConfirmations);
 #endif
diff --git a/chrome/browser/ui/views/global_media_controls/media_dialog_view.cc b/chrome/browser/ui/views/global_media_controls/media_dialog_view.cc
index be0ea4a..3bbb5fc 100644
--- a/chrome/browser/ui/views/global_media_controls/media_dialog_view.cc
+++ b/chrome/browser/ui/views/global_media_controls/media_dialog_view.cc
@@ -13,6 +13,7 @@
 #include "base/observer_list.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/browser_process.h"
 #include "chrome/browser/media/router/media_router_feature.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/global_media_controls/media_item_ui_metrics.h"
@@ -69,13 +70,11 @@
   }
   // The selected language is only shown when Live Caption is enabled.
   if (profile_prefs->GetBoolean(prefs::kLiveCaptionEnabled)) {
-    int language_message_id = speech::GetLanguageDisplayName(
-        prefs::GetLiveCaptionLanguageCode(profile_prefs));
-    if (language_message_id) {
-      std::u16string language = l10n_util::GetStringUTF16(language_message_id);
-      return l10n_util::GetStringFUTF16(
-          IDS_GLOBAL_MEDIA_CONTROLS_LIVE_CAPTION_SHOW_LANGUAGE, language);
-    }
+    std::u16string language = speech::GetLanguageDisplayName(
+        prefs::GetLiveCaptionLanguageCode(profile_prefs),
+        g_browser_process->GetApplicationLocale());
+    return l10n_util::GetStringFUTF16(
+        IDS_GLOBAL_MEDIA_CONTROLS_LIVE_CAPTION_SHOW_LANGUAGE, language);
   }
   return l10n_util::GetStringUTF16(IDS_GLOBAL_MEDIA_CONTROLS_LIVE_CAPTION);
 }
diff --git a/chrome/browser/ui/views/global_media_controls/media_dialog_view_interactive_browsertest.cc b/chrome/browser/ui/views/global_media_controls/media_dialog_view_interactive_browsertest.cc
index e14ae7b..12549bf 100644
--- a/chrome/browser/ui/views/global_media_controls/media_dialog_view_interactive_browsertest.cc
+++ b/chrome/browser/ui/views/global_media_controls/media_dialog_view_interactive_browsertest.cc
@@ -928,7 +928,7 @@
   EXPECT_TRUE(
       browser()->profile()->GetPrefs()->GetBoolean(prefs::kLiveCaptionEnabled));
   EXPECT_TRUE(GetLiveCaptionTitleLabel()->GetVisible());
-  EXPECT_EQ("Live Caption - English",
+  EXPECT_EQ("Live Caption - English (United States)",
             base::UTF16ToUTF8(GetLiveCaptionTitleLabel()->GetText()));
 
   // Click the Live Caption toggle again to toggle it off.
@@ -948,7 +948,7 @@
   EXPECT_TRUE(ui_.WaitForDialogOpened());
   EXPECT_TRUE(ui_.IsDialogVisible());
   EXPECT_TRUE(GetLiveCaptionTitleLabel()->GetVisible());
-  EXPECT_EQ("Live Caption - English",
+  EXPECT_EQ("Live Caption - English (United States)",
             base::UTF16ToUTF8(GetLiveCaptionTitleLabel()->GetText()));
 
   // Click the Live Caption toggle to toggle it off.
@@ -1039,7 +1039,7 @@
             base::UTF16ToUTF8(GetLiveCaptionTitleLabel()->GetText()));
 
   OnSodaInstalled();
-  EXPECT_EQ("Live Caption - English",
+  EXPECT_EQ("Live Caption - English (United States)",
             base::UTF16ToUTF8(GetLiveCaptionTitleLabel()->GetText()));
 }
 
@@ -1071,7 +1071,7 @@
   // When Live Caption is enabled, the title should show the language.
   ClickEnableLiveCaptionOnDialog();
   EXPECT_TRUE(GetLiveCaptionTitleLabel()->GetVisible());
-  EXPECT_EQ("Live Caption - English",
+  EXPECT_EQ("Live Caption - English (United States)",
             base::UTF16ToUTF8(GetLiveCaptionTitleLabel()->GetText()));
 
   // Close dialog and change live caption language. Reopen dialog.
@@ -1085,7 +1085,7 @@
 
   // Live Caption is enabled so the title should show the new language.
   EXPECT_TRUE(GetLiveCaptionTitleLabel()->GetVisible());
-  EXPECT_EQ("Live Caption - German",
+  EXPECT_EQ("Live Caption - German (Germany)",
             base::UTF16ToUTF8(GetLiveCaptionTitleLabel()->GetText()));
 
   // When Live Caption is disabled, the title should not show the language.
diff --git a/chrome/browser/ui/views/user_education/browser_user_education_service.cc b/chrome/browser/ui/views/user_education/browser_user_education_service.cc
index 035a2e4c..19675e11 100644
--- a/chrome/browser/ui/views/user_education/browser_user_education_service.cc
+++ b/chrome/browser/ui/views/user_education/browser_user_education_service.cc
@@ -233,7 +233,7 @@
   registry.RegisterFeature(
       std::move(FeaturePromoSpecification::CreateForTutorialPromo(
                     feature_engagement::kIPHDesktopCustomizeChromeFeature,
-                    kTabStripRegionElementId,
+                    kTopContainerElementId,
                     IDS_TUTORIAL_CUSTOMIZE_CHROME_START_TUTORIAL_IPH,
                     kSidePanelCustomizeChromeTutorialId)
                     .SetBubbleArrow(HelpBubbleArrow::kNone)
@@ -640,9 +640,8 @@
     TutorialDescription::Step success_step(
         IDS_TUTORIAL_GENERIC_SUCCESS_TITLE,
         IDS_TUTORIAL_CUSTOMIZE_CHROME_SUCCESS_BODY,
-        ui::InteractionSequence::StepType::kShown,
-        CustomizeChromeUI::kChangeChromeThemeClassicElementId, std::string(),
-        HelpBubbleArrow::kRightCenter, ui::CustomElementEventType(),
+        ui::InteractionSequence::StepType::kShown, kTopContainerElementId,
+        std::string(), HelpBubbleArrow::kNone, ui::CustomElementEventType(),
         /* must_remain_visible =*/false,
         /* transition_only_on_event =*/false,
         user_education::TutorialDescription::NameElementsCallback(),
diff --git a/chrome/browser/ui/views/web_apps/web_app_integration_browsertest.cc b/chrome/browser/ui/views/web_apps/web_app_integration_browsertest.cc
index 0ec2b7c..66e44695 100644
--- a/chrome/browser/ui/views/web_apps/web_app_integration_browsertest.cc
+++ b/chrome/browser/ui/views/web_apps/web_app_integration_browsertest.cc
@@ -4822,17 +4822,9 @@
   helper_.CheckWindowDisplayTabbed();
 }
 
-// TODO(crbug.com/1427085): Re-enable this test
-#if BUILDFLAG(IS_MAC)
-#define MAYBE_WAI_29StandaloneWindowed_24_12Standalone_7Standalone_112StandaloneNotShown_116StandaloneTabbed_143_117Standalone_34Standalone_24_94_144 \
-  DISABLED_WAI_29StandaloneWindowed_24_12Standalone_7Standalone_112StandaloneNotShown_116StandaloneTabbed_143_117Standalone_34Standalone_24_94_144
-#else
-#define MAYBE_WAI_29StandaloneWindowed_24_12Standalone_7Standalone_112StandaloneNotShown_116StandaloneTabbed_143_117Standalone_34Standalone_24_94_144 \
-  WAI_29StandaloneWindowed_24_12Standalone_7Standalone_112StandaloneNotShown_116StandaloneTabbed_143_117Standalone_34Standalone_24_94_144
-#endif
 IN_PROC_BROWSER_TEST_F(
     WebAppIntegration,
-    MAYBE_WAI_29StandaloneWindowed_24_12Standalone_7Standalone_112StandaloneNotShown_116StandaloneTabbed_143_117Standalone_34Standalone_24_94_144) {
+    WAI_29StandaloneWindowed_24_12Standalone_7Standalone_112StandaloneNotShown_116StandaloneTabbed_143_117Standalone_34Standalone_24_94_144) {
   // Test contents are generated by script. Please do not modify!
   // See `docs/webapps/why-is-this-test-failing.md` or
   // `docs/webapps/integration-testing-framework` for more info.
diff --git a/chrome/browser/ui/views/webid/account_selection_bubble_view.cc b/chrome/browser/ui/views/webid/account_selection_bubble_view.cc
index 89eb96f..f5d2650 100644
--- a/chrome/browser/ui/views/webid/account_selection_bubble_view.cc
+++ b/chrome/browser/ui/views/webid/account_selection_bubble_view.cc
@@ -581,6 +581,21 @@
   idp_images_[image_url] = image;
 }
 
+std::string AccountSelectionBubbleView::GetDialogTitle() const {
+  // We cannot just return title_ because it is not always set
+  // (e.g. by ShowFailureDialog).
+  return base::UTF16ToUTF8(title_label_->GetText());
+}
+
+absl::optional<std::string> AccountSelectionBubbleView::GetDialogSubtitle()
+    const {
+  if (!subtitle_label_) {
+    return absl::nullopt;
+  }
+
+  return base::UTF16ToUTF8(subtitle_label_->GetText());
+}
+
 gfx::Rect AccountSelectionBubbleView::GetBubbleBounds() {
   // The bubble initially looks like this relative to the contents_web_view:
   //                        |--------|
diff --git a/chrome/browser/ui/views/webid/account_selection_bubble_view.h b/chrome/browser/ui/views/webid/account_selection_bubble_view.h
index 3251c7c..7b20bfb 100644
--- a/chrome/browser/ui/views/webid/account_selection_bubble_view.h
+++ b/chrome/browser/ui/views/webid/account_selection_bubble_view.h
@@ -96,6 +96,9 @@
   // Populates `idp_images` when an IDP image has been fetched.
   void AddIdpImage(const GURL& image_url, gfx::ImageSkia idp_image);
 
+  std::string GetDialogTitle() const override;
+  absl::optional<std::string> GetDialogSubtitle() const override;
+
  private:
   gfx::Rect GetBubbleBounds() override;
 
diff --git a/chrome/browser/ui/views/webid/account_selection_bubble_view_interface.h b/chrome/browser/ui/views/webid/account_selection_bubble_view_interface.h
index 757e9fa..d52695f 100644
--- a/chrome/browser/ui/views/webid/account_selection_bubble_view_interface.h
+++ b/chrome/browser/ui/views/webid/account_selection_bubble_view_interface.h
@@ -43,6 +43,9 @@
       const std::u16string& top_frame_for_display,
       const std::u16string& idp_for_display,
       const content::IdentityProviderMetadata& idp_metadata) = 0;
+
+  virtual std::string GetDialogTitle() const = 0;
+  virtual absl::optional<std::string> GetDialogSubtitle() const = 0;
 };
 
 #endif  // CHROME_BROWSER_UI_VIEWS_WEBID_ACCOUNT_SELECTION_BUBBLE_VIEW_INTERFACE_H_
diff --git a/chrome/browser/ui/views/webid/fedcm_account_selection_view_desktop.cc b/chrome/browser/ui/views/webid/fedcm_account_selection_view_desktop.cc
index 0860fe8..d83d380d 100644
--- a/chrome/browser/ui/views/webid/fedcm_account_selection_view_desktop.cc
+++ b/chrome/browser/ui/views/webid/fedcm_account_selection_view_desktop.cc
@@ -166,6 +166,14 @@
   // associated web contents are hidden.
 }
 
+std::string FedCmAccountSelectionView::GetTitle() const {
+  return GetBubbleView()->GetDialogTitle();
+}
+
+absl::optional<std::string> FedCmAccountSelectionView::GetSubtitle() const {
+  return GetBubbleView()->GetDialogSubtitle();
+}
+
 void FedCmAccountSelectionView::OnVisibilityChanged(
     content::Visibility visibility) {
   if (!bubble_widget_)
@@ -245,6 +253,11 @@
       bubble_widget_->widget_delegate());
 }
 
+const AccountSelectionBubbleViewInterface*
+FedCmAccountSelectionView::GetBubbleView() const {
+  return static_cast<const AccountSelectionBubbleView*>(
+      bubble_widget_->widget_delegate());
+}
 void FedCmAccountSelectionView::OnWidgetDestroying(views::Widget* widget) {
   DismissReason dismiss_reason =
       (bubble_widget_->closed_reason() ==
diff --git a/chrome/browser/ui/views/webid/fedcm_account_selection_view_desktop.h b/chrome/browser/ui/views/webid/fedcm_account_selection_view_desktop.h
index 16a5263..c0cd6233e 100644
--- a/chrome/browser/ui/views/webid/fedcm_account_selection_view_desktop.h
+++ b/chrome/browser/ui/views/webid/fedcm_account_selection_view_desktop.h
@@ -57,6 +57,8 @@
       const std::string& top_frame_etld_plus_one,
       const std::string& idp_etld_plus_one,
       const content::IdentityProviderMetadata& idp_metadata) override;
+  std::string GetTitle() const override;
+  absl::optional<std::string> GetSubtitle() const override;
 
   // content::WebContentsObserver
   void OnVisibilityChanged(content::Visibility visibility) override;
@@ -85,6 +87,7 @@
 
   // Returns AccountSelectionBubbleViewInterface for bubble views::Widget.
   virtual AccountSelectionBubbleViewInterface* GetBubbleView();
+  virtual const AccountSelectionBubbleViewInterface* GetBubbleView() const;
 
  private:
   enum class State {
diff --git a/chrome/browser/ui/views/webid/fedcm_account_selection_view_desktop_unittest.cc b/chrome/browser/ui/views/webid/fedcm_account_selection_view_desktop_unittest.cc
index 3aac879..9c6f762 100644
--- a/chrome/browser/ui/views/webid/fedcm_account_selection_view_desktop_unittest.cc
+++ b/chrome/browser/ui/views/webid/fedcm_account_selection_view_desktop_unittest.cc
@@ -81,6 +81,11 @@
     account_ids_ = {};
   }
 
+  std::string GetDialogTitle() const override { return std::string(); }
+  absl::optional<std::string> GetDialogSubtitle() const override {
+    return absl::nullopt;
+  }
+
   bool show_back_button_{false};
   absl::optional<SheetType> sheet_type_;
   std::vector<std::string> account_ids_;
diff --git a/chrome/browser/ui/webid/account_selection_view.h b/chrome/browser/ui/webid/account_selection_view.h
index 95983ea..b8547e4 100644
--- a/chrome/browser/ui/webid/account_selection_view.h
+++ b/chrome/browser/ui/webid/account_selection_view.h
@@ -74,6 +74,9 @@
       const std::string& idp_for_display,
       const content::IdentityProviderMetadata& idp_metadata) = 0;
 
+  virtual std::string GetTitle() const = 0;
+  virtual absl::optional<std::string> GetSubtitle() const = 0;
+
  protected:
   raw_ptr<Delegate> delegate_ = nullptr;
 };
diff --git a/chrome/browser/ui/webid/identity_dialog_controller.cc b/chrome/browser/ui/webid/identity_dialog_controller.cc
index 3390e67..b64df15 100644
--- a/chrome/browser/ui/webid/identity_dialog_controller.cc
+++ b/chrome/browser/ui/webid/identity_dialog_controller.cc
@@ -65,6 +65,14 @@
   NOTIMPLEMENTED();
 }
 
+std::string IdentityDialogController::GetTitle() const {
+  return account_view_->GetTitle();
+}
+
+absl::optional<std::string> IdentityDialogController::GetSubtitle() const {
+  return account_view_->GetSubtitle();
+}
+
 void IdentityDialogController::OnAccountSelected(const GURL& idp_config_url,
                                                  const Account& account) {
   on_dismiss_.Reset();
diff --git a/chrome/browser/ui/webid/identity_dialog_controller.h b/chrome/browser/ui/webid/identity_dialog_controller.h
index ca54642..fe9df3a 100644
--- a/chrome/browser/ui/webid/identity_dialog_controller.h
+++ b/chrome/browser/ui/webid/identity_dialog_controller.h
@@ -54,6 +54,9 @@
                          DismissCallback dismiss_callback) override;
   void ShowIdpSigninFailureDialog(base::OnceClosure dismiss_callback) override;
 
+  std::string GetTitle() const override;
+  absl::optional<std::string> GetSubtitle() const override;
+
   // AccountSelectionView::Delegate:
   void OnAccountSelected(const GURL& idp_config_url,
                          const Account& account) override;
diff --git a/chrome/browser/ui/webui/settings/captions_handler.cc b/chrome/browser/ui/webui/settings/captions_handler.cc
index 6098de7..c36861e 100644
--- a/chrome/browser/ui/webui/settings/captions_handler.cc
+++ b/chrome/browser/ui/webui/settings/captions_handler.cc
@@ -9,12 +9,14 @@
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
 #include "chrome/browser/browser_process.h"
+#include "chrome/browser/translate/chrome_translate_client.h"
 #include "chrome/grit/chromium_strings.h"
 #include "chrome/grit/generated_resources.h"
 #include "components/live_caption/pref_names.h"
 #include "components/prefs/pref_service.h"
 #include "components/soda/constants.h"
 #include "components/soda/soda_installer.h"
+#include "components/translate/core/browser/translate_prefs.h"
 #include "content/public/browser/web_ui.h"
 #include "media/base/media_switches.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -141,7 +143,9 @@
       base::Value::Dict available_language_pack;
       available_language_pack.Set(kCodeKey, config.language_name);
       available_language_pack.Set(
-          kDisplayNameKey, l10n_util::GetStringUTF16(config.display_name));
+          kDisplayNameKey,
+          speech::GetLanguageDisplayName(
+              config.language_name, g_browser_process->GetApplicationLocale()));
       available_language_packs.Append(std::move(available_language_pack));
     }
   }
@@ -159,7 +163,9 @@
     if (config && config->language_code != speech::LanguageCode::kNone) {
       installed_language_pack.Set(kCodeKey, language.GetString());
       installed_language_pack.Set(
-          kDisplayNameKey, l10n_util::GetStringUTF16(config->display_name));
+          kDisplayNameKey, speech::GetLanguageDisplayName(
+                               config->language_name,
+                               g_browser_process->GetApplicationLocale()));
       installed_language_packs.Append(std::move(installed_language_pack));
     }
   }
diff --git a/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
index 38c52fdf..5e3b521 100644
--- a/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
@@ -309,6 +309,9 @@
 #if BUILDFLAG(IS_MAC)
     {"aboutLearnMoreUpdating", IDS_SETTINGS_ABOUT_PAGE_LEARN_MORE_UPDATING},
 #endif
+    {"getTheMostOutOfProgram", IDS_SETTINGS_GET_THE_MOST_OUT_OF_PROGRAM},
+    {"getTheMostOutOfProgramDescription",
+     IDS_SETTINGS_GET_THE_MOST_OUT_OF_PROGRAM_DESCRIPTION},
   };
   html_source->AddLocalizedStrings(kLocalizedStrings);
 
@@ -351,6 +354,10 @@
   html_source->AddLocalizedString("aboutProductTos",
                                   IDS_ABOUT_TERMS_OF_SERVICE);
 #endif
+
+  html_source->AddBoolean(
+      "showGetTheMostOutOfProgramSection",
+      base::FeatureList::IsEnabled(features::kGetTheMostOutOfProgram));
 }
 
 void AddAppearanceStrings(content::WebUIDataSource* html_source,
diff --git a/chrome/browser/ui/webui/settings/shared_settings_localized_strings_provider.cc b/chrome/browser/ui/webui/settings/shared_settings_localized_strings_provider.cc
index 6a095ec..3348e01 100644
--- a/chrome/browser/ui/webui/settings/shared_settings_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/settings/shared_settings_localized_strings_provider.cc
@@ -114,6 +114,11 @@
       {"captionsColorCyan", IDS_SETTINGS_CAPTIONS_COLOR_CYAN},
       {"captionsColorMagenta", IDS_SETTINGS_CAPTIONS_COLOR_MAGENTA},
       {"captionsDefaultSetting", IDS_SETTINGS_CAPTIONS_DEFAULT_SETTING},
+      {"captionsLanguage", IDS_SETTINGS_CAPTIONS_LANGUAGE},
+      {"captionsManageLanguagesTitle",
+       IDS_SETTINGS_CAPTIONS_MANAGE_LANGUAGES_TITLE},
+      {"captionsLiveTranslateTargetLanguage",
+       IDS_SETTINGS_CAPTIONS_LIVE_TRANSLATE_TARGET_LANGUAGE},
   };
   html_source->AddLocalizedStrings(kLocalizedStrings);
 
@@ -124,9 +129,19 @@
   html_source->AddLocalizedString(
       "captionsEnableLiveCaptionTitle",
       IDS_SETTINGS_CAPTIONS_ENABLE_LIVE_CAPTION_TITLE);
+  html_source->AddLocalizedString(
+      "captionsEnableLiveTranslateTitle",
+      IDS_SETTINGS_CAPTIONS_ENABLE_LIVE_TRANSLATE_TITLE);
+  html_source->AddLocalizedString(
+      "captionsEnableLiveTranslateSubtitle",
+      IDS_SETTINGS_CAPTIONS_ENABLE_LIVE_TRANSLATE_SUBTITLE);
 
   const bool liveCaptionMultiLanguageEnabled =
       base::FeatureList::IsEnabled(media::kLiveCaptionMultiLanguage);
+
+  const bool liveTranslateEnabled =
+      base::FeatureList::IsEnabled(media::kLiveTranslate);
+
   const int live_caption_subtitle_message =
       liveCaptionMultiLanguageEnabled
           ? IDS_SETTINGS_CAPTIONS_ENABLE_LIVE_CAPTION_SUBTITLE
@@ -138,41 +153,57 @@
       speech::GetLanguageComponentConfig(speech::LanguageCode::kEnUs);
   html_source->AddString("sodaLanguageCodeEnglish",
                          englishConfig->language_name);
-  html_source->AddLocalizedString("sodaLanguageDisplayNameEnglish",
-                                  englishConfig->display_name);
-  absl::optional<speech::SodaLanguagePackComponentConfig> frenchConfig =
+  html_source->AddString("sodaLanguageDisplayNameEnglish",
+                         speech::GetLanguageDisplayName(
+                             englishConfig->language_name,
+                             g_browser_process->GetApplicationLocale()));
+  absl::optional<speech::SodaLanguagePackComponentConfig> french_config =
       speech::GetLanguageComponentConfig(speech::LanguageCode::kFrFr);
-  html_source->AddString("sodaLanguageCodeFrench", frenchConfig->language_name);
-  html_source->AddLocalizedString("sodaLanguageDisplayNameFrench",
-                                  frenchConfig->display_name);
-  absl::optional<speech::SodaLanguagePackComponentConfig> germanConfig =
+  html_source->AddString("sodaLanguageCodeFrench",
+                         french_config->language_name);
+  html_source->AddString("sodaLanguageDisplayNameFrench",
+                         speech::GetLanguageDisplayName(
+                             french_config->language_name,
+                             g_browser_process->GetApplicationLocale()));
+  absl::optional<speech::SodaLanguagePackComponentConfig> german_config =
       speech::GetLanguageComponentConfig(speech::LanguageCode::kDeDe);
-  html_source->AddString("sodaLanguageCodeGerman", germanConfig->language_name);
-  html_source->AddLocalizedString("sodaLanguageDisplayNameGerman",
-                                  germanConfig->display_name);
-  absl::optional<speech::SodaLanguagePackComponentConfig> italianConfig =
+  html_source->AddString("sodaLanguageCodeGerman",
+                         german_config->language_name);
+  html_source->AddString("sodaLanguageDisplayNameGerman",
+                         speech::GetLanguageDisplayName(
+                             german_config->language_name,
+                             g_browser_process->GetApplicationLocale()));
+  absl::optional<speech::SodaLanguagePackComponentConfig> italian_config =
       speech::GetLanguageComponentConfig(speech::LanguageCode::kItIt);
   html_source->AddString("sodaLanguageCodeItalian",
-                         italianConfig->language_name);
-  html_source->AddLocalizedString("sodaLanguageDisplayNameItalian",
-                                  italianConfig->display_name);
-  absl::optional<speech::SodaLanguagePackComponentConfig> japaneseConfig =
+                         italian_config->language_name);
+  html_source->AddString("sodaLanguageDisplayNameItalian",
+                         speech::GetLanguageDisplayName(
+                             italian_config->language_name,
+                             g_browser_process->GetApplicationLocale()));
+  absl::optional<speech::SodaLanguagePackComponentConfig> japanese_config =
       speech::GetLanguageComponentConfig(speech::LanguageCode::kJaJp);
   html_source->AddString("sodaLanguageCodeJapanese",
-                         japaneseConfig->language_name);
-  html_source->AddLocalizedString("sodaLanguageDisplayNameJapanese",
-                                  japaneseConfig->display_name);
-  absl::optional<speech::SodaLanguagePackComponentConfig> spanishConfig =
+                         japanese_config->language_name);
+  html_source->AddString("sodaLanguageDisplayNameJapanese",
+                         speech::GetLanguageDisplayName(
+                             japanese_config->language_name,
+                             g_browser_process->GetApplicationLocale()));
+  absl::optional<speech::SodaLanguagePackComponentConfig> spanish_config =
       speech::GetLanguageComponentConfig(speech::LanguageCode::kEsEs);
   html_source->AddString("sodaLanguageCodeSpanish",
-                         spanishConfig->language_name);
-  html_source->AddLocalizedString("sodaLanguageDisplayNameSpanish",
-                                  spanishConfig->display_name);
+                         spanish_config->language_name);
+  html_source->AddString("sodaLanguageDisplayNameSpanish",
+                         speech::GetLanguageDisplayName(
+                             spanish_config->language_name,
+                             g_browser_process->GetApplicationLocale()));
 
   html_source->AddBoolean("enableLiveCaption",
                           captions::IsLiveCaptionFeatureSupported());
   html_source->AddBoolean("enableLiveCaptionMultiLanguage",
                           liveCaptionMultiLanguageEnabled);
+
+  html_source->AddBoolean("enableLiveTranslate", liveTranslateEnabled);
 }
 
 #if BUILDFLAG(IS_CHROMEOS)
diff --git a/chrome/browser/ui/webui/side_panel/companion/companion_side_panel_untrusted_ui.cc b/chrome/browser/ui/webui/side_panel/companion/companion_side_panel_untrusted_ui.cc
index 525d7474..e286499 100644
--- a/chrome/browser/ui/webui/side_panel/companion/companion_side_panel_untrusted_ui.cc
+++ b/chrome/browser/ui/webui/side_panel/companion/companion_side_panel_untrusted_ui.cc
@@ -12,6 +12,7 @@
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_ui.h"
 #include "content/public/browser/web_ui_data_source.h"
+#include "url/gurl.h"
 
 CompanionSidePanelUntrustedUI::CompanionSidePanelUntrustedUI(
     content::WebUI* web_ui)
@@ -31,10 +32,15 @@
       network::mojom::CSPDirectiveName::ScriptSrc,
       "script-src chrome-untrusted://resources 'self';");
   // Allow the companion homepage URL to be embedded in this WebUI.
-  std::string frameSrc = std::string("frame-src ") +
-                         features::kHomepageURLForCompanion.Get() + ";";
+  GURL frameSrcUrl =
+      GURL(features::kHomepageURLForCompanion.Get()).GetWithEmptyPath();
+  std::string frameSrcString = frameSrcUrl.is_valid()
+                                   ? frameSrcUrl.spec()
+                                   : features::kHomepageURLForCompanion.Get();
+  std::string frameSrcDirective =
+      std::string("frame-src ") + frameSrcString + ";";
   html_source->OverrideContentSecurityPolicy(
-      network::mojom::CSPDirectiveName::FrameSrc, frameSrc);
+      network::mojom::CSPDirectiveName::FrameSrc, frameSrcDirective);
 }
 
 CompanionSidePanelUntrustedUI::~CompanionSidePanelUntrustedUI() = default;
diff --git a/chrome/browser/usb/usb_blocklist.cc b/chrome/browser/usb/usb_blocklist.cc
index 8494f90..7353ef8 100644
--- a/chrome/browser/usb/usb_blocklist.cc
+++ b/chrome/browser/usb/usb_blocklist.cc
@@ -8,9 +8,9 @@
 #include <string>
 #include <tuple>
 
+#include "base/metrics/field_trial_params.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_split.h"
-#include "components/variations/variations_associated_data.h"
 #include "services/device/public/mojom/usb_device.mojom.h"
 
 namespace {
@@ -148,8 +148,8 @@
 }
 
 void UsbBlocklist::PopulateWithServerProvidedValues() {
-  std::string blocklist_string = variations::GetVariationParamValue(
-      "WebUSBBlocklist", "blocklist_additions");
+  std::string blocklist_string =
+      base::GetFieldTrialParamValue("WebUSBBlocklist", "blocklist_additions");
 
   for (const auto& entry :
        base::SplitStringPiece(blocklist_string, ",", base::TRIM_WHITESPACE,
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index ba55586..be48ad9 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-main-1679551190-05d343a0d20ff9d5301807a9a7330460f0fc9056.profdata
+chrome-linux-main-1679594402-969487b5ff7ba9e94c57aacf3b915841e40ad605.profdata
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt
index a961e4d..efab59c 100644
--- a/chrome/build/mac-arm.pgo.txt
+++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@
-chrome-mac-arm-main-1679587106-e5f9fd8e7367245bbebfa0238f1ab922d38ff078.profdata
+chrome-mac-arm-main-1679601280-1f3de2d387ec0fe09258a51133c012d5b0b9f283.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index 8de7066..11c418d 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-main-1679572553-adcc660610aec677cb528284b6e339c44fc16cbb.profdata
+chrome-mac-main-1679594402-4993277604a6dadd6d6b732fecba08859a8baef0.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index 1043402..8802608 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1679572553-a6596050028c1e98e42c48b3a704a7ddee14c0d1.profdata
+chrome-win32-main-1679594402-64cdf14a13f598989a60336d5a937e641c150871.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index 102dace..5bb4db1 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1679583480-b418af276de905716877ef58e4c2b8ec8b66f8ab.profdata
+chrome-win64-main-1679594402-559f0f55784157947cc49c7bea88715453008fdb.profdata
diff --git a/chrome/common/extensions/api/content_settings.json b/chrome/common/extensions/api/content_settings.json
index f62c827..d7470a2 100644
--- a/chrome/common/extensions/api/content_settings.json
+++ b/chrome/common/extensions/api/content_settings.json
@@ -170,6 +170,11 @@
         ]
       },
       {
+        "id": "AutoVerifyContentSetting",
+        "type": "string",
+        "enum": ["allow", "block"]
+      },
+      {
         "id": "CookiesContentSetting",
         "type": "string",
         "enum": ["allow", "block", "session_only"]
@@ -236,6 +241,14 @@
       }
     ],
     "properties": {
+      "autoVerify": {
+        "$ref": "ContentSetting",
+        "description": "Whether to allow sites to use the <a href='https://developer.chrome.com/docs/privacy-sandbox/trust-tokens/'>Private State Tokens API</a>. One of <br><var>allow</var>: Allow sites to use the Private State Tokens API, <br><var>block</var>: Block sites from using the Private State Tokens API. <br>Default is <var>allow</var>.<br> The primary URL is the URL of the top-level frame. The secondary URL is not used. NOTE: When calling <code>set()</code>, the primary pattern must be <code><all_urls></code>.",
+        "value": [
+          "anti-abuse",
+          {"$ref":"AutoVerifyContentSetting"}
+        ]
+      },
       "cookies": {
         "$ref": "ContentSetting",
         "description": "Whether to allow cookies and other local data to be set by websites. One of<br><var>allow</var>: Accept cookies,<br><var>block</var>: Block cookies,<br><var>session_only</var>: Accept cookies only for the current session. <br>Default is <var>allow</var>.<br>The primary URL is the URL representing the cookie origin. The secondary URL is the URL of the top-level frame.",
diff --git a/chrome/common/extensions/manifest_tests/extension_manifests_side_panel_unittest.cc b/chrome/common/extensions/manifest_tests/extension_manifests_side_panel_unittest.cc
index 8df654f..dcad6ca 100644
--- a/chrome/common/extensions/manifest_tests/extension_manifests_side_panel_unittest.cc
+++ b/chrome/common/extensions/manifest_tests/extension_manifests_side_panel_unittest.cc
@@ -4,10 +4,12 @@
 
 #include "chrome/common/extensions/api/side_panel/side_panel_info.h"
 
+#include "base/command_line.h"
 #include "base/strings/stringprintf.h"
 #include "chrome/common/extensions/manifest_tests/chrome_manifest_test.h"
 #include "content/public/test/browser_test_utils.h"
 #include "extensions/common/manifest.h"
+#include "extensions/common/switches.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace extensions {
@@ -79,13 +81,13 @@
   base::ScopedTempDir temp_dir_;
 };
 
-#if defined(OFFICIAL_BUILD)
-#define MAYBE_FileDoesntExist DISABLED_FileDoesntExist
-#else
-#define MAYBE_FileDoesntExist FileDoesntExist
-#endif  // defined(OFFICIAL_BUILD)
 // Error loading extension when filepath doesn't exist or is empty.
-TEST_F(SidePanelExtensionsTest, MAYBE_FileDoesntExist) {
+TEST_F(SidePanelExtensionsTest, FileDoesntExist) {
+  // This switch is required to make this test pass on official build bots.
+  // TODO(crbug.com/1413908): Remove once side panel is not experimental.
+  base::CommandLine::ForCurrentProcess()->AppendSwitch(
+      extensions::switches::kEnableExperimentalExtensionApis);
+
   for (const auto* default_path : {"", "error"}) {
     std::string error;
     std::vector<InstallWarning> warnings;
diff --git a/chrome/renderer/accessibility/read_anything_app_controller.cc b/chrome/renderer/accessibility/read_anything_app_controller.cc
index 556b079c..ebb2cd5 100644
--- a/chrome/renderer/accessibility/read_anything_app_controller.cc
+++ b/chrome/renderer/accessibility/read_anything_app_controller.cc
@@ -331,15 +331,15 @@
   // distillation request.
   model_.ClearPendingUpdates();
 
+  // TODO(b/1266555): Use v8::Function rather than javascript. If possible,
+  // replace this function call with firing an event.
+  std::string script = "chrome.readAnything.showLoading();";
+  render_frame_->ExecuteJavaScript(base::ASCIIToUTF16(script));
   // When the UI first constructs, this function may be called before tree_id
   // has been added to the tree list in AccessibilityEventReceived. In that
   // case, do not distill.
   if (model_.active_tree_id() != ui::AXTreeIDUnknown() &&
       model_.ContainsTree(model_.active_tree_id())) {
-    // TODO(b/1266555): Use v8::Function rather than javascript. If possible,
-    // replace this function call with firing an event.
-    std::string script = "chrome.readAnything.showLoading();";
-    render_frame_->ExecuteJavaScript(base::ASCIIToUTF16(script));
     Distill();
   }
 }
diff --git a/chrome/services/speech/audio_source_fetcher_impl.cc b/chrome/services/speech/audio_source_fetcher_impl.cc
index 5b4aae1..3419a07 100644
--- a/chrome/services/speech/audio_source_fetcher_impl.cc
+++ b/chrome/services/speech/audio_source_fetcher_impl.cc
@@ -8,6 +8,7 @@
 
 #include "base/check.h"
 #include "base/functional/bind.h"
+#include "base/metrics/histogram_functions.h"
 #include "base/task/bind_post_task.h"
 #include "base/task/sequenced_task_runner.h"
 #include "base/time/time.h"
@@ -36,6 +37,11 @@
 constexpr base::TimeDelta kServerBasedRecognitionAudioBufferSize =
     base::Milliseconds(100);
 
+constexpr char kServerBasedRecognitionSessionLength[] =
+    "Ash.SpeechRecognitionSessionLength.ServerBased";
+constexpr char kOnDeviceRecognitionSessionLength[] =
+    "Ash.SpeechRecognitionSessionLength.OnDevice";
+
 }  // namespace
 
 AudioSourceFetcherImpl::AudioSourceFetcherImpl(
@@ -52,6 +58,10 @@
 AudioSourceFetcherImpl::~AudioSourceFetcherImpl() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   Stop();
+  const auto* session_length_metric_name =
+      is_server_based_ ? kServerBasedRecognitionSessionLength
+                       : kOnDeviceRecognitionSessionLength;
+  base::UmaHistogramLongTimes100(session_length_metric_name, audio_length_);
 }
 
 void AudioSourceFetcherImpl::Create(
@@ -162,6 +172,9 @@
                                      base::TimeTicks audio_capture_time,
                                      double volume,
                                      bool key_pressed) {
+  audio_length_ += media::AudioTimestampHelper::FramesToTime(
+      audio_source->frames(), audio_parameters_.sample_rate());
+
   if (converter_) {
     // Send the audio callback to the main thread to resample.
     std::unique_ptr<media::AudioBus> input =
diff --git a/chrome/services/speech/audio_source_fetcher_impl.h b/chrome/services/speech/audio_source_fetcher_impl.h
index d81807b..6c55181 100644
--- a/chrome/services/speech/audio_source_fetcher_impl.h
+++ b/chrome/services/speech/audio_source_fetcher_impl.h
@@ -10,6 +10,7 @@
 #include "base/memory/raw_ptr.h"
 #include "base/memory/weak_ptr.h"
 #include "base/sequence_checker.h"
+#include "base/time/time.h"
 #include "chrome/services/speech/audio_source_consumer.h"
 #include "media/base/audio_bus.h"
 #include "media/base/audio_capturer_source.h"
@@ -130,6 +131,8 @@
   bool is_multi_channel_supported_;
   bool is_server_based_;
 
+  base::TimeDelta audio_length_ = base::Seconds(0);
+
   // A callback to push audio data into `converter_`.
   SendAudioToResampleCallback resample_callback_;
 
diff --git a/chrome/services/speech/audio_source_fetcher_unittest.cc b/chrome/services/speech/audio_source_fetcher_unittest.cc
index 3d83dea7..c3326fb 100644
--- a/chrome/services/speech/audio_source_fetcher_unittest.cc
+++ b/chrome/services/speech/audio_source_fetcher_unittest.cc
@@ -12,6 +12,7 @@
 #include "base/path_service.h"
 #include "base/run_loop.h"
 #include "base/test/bind.h"
+#include "base/test/metrics/histogram_tester.h"
 #include "base/test/task_environment.h"
 #include "chrome/services/speech/cros_speech_recognition_recognizer_impl.h"
 #include "chrome/services/speech/speech_recognition_service_impl.h"
@@ -34,6 +35,11 @@
 constexpr int kOriginalSampleRate = 48000;
 constexpr int kOriginalFramesPerBuffer = 9600;
 
+constexpr char kServerBasedRecognitionSessionLength[] =
+    "Ash.SpeechRecognitionSessionLength.ServerBased";
+constexpr char kOnDeviceRecognitionSessionLength[] =
+    "Ash.SpeechRecognitionSessionLength.OnDevice";
+
 }  // namespace
 
 class MockStreamFactory : public audio::FakeStreamFactory {
@@ -148,9 +154,11 @@
   void OnLanguageIdentificationEvent(
       media::mojom::LanguageIdentificationEventPtr event) override {}
 
+  std::unique_ptr<AudioSourceFetcherImpl> audio_source_fetcher_;
+  base::HistogramTester histogram_tester_;
+
  private:
   base::test::TaskEnvironment task_environment;
-  std::unique_ptr<AudioSourceFetcherImpl> audio_source_fetcher_;
   MockAudioSourceConsumer* speech_recognition_recognizer_;
   bool is_server_based_;
 };
@@ -190,6 +198,18 @@
     VerifyAudioBuffer(kServerBasedRecognitionAudioSampleRate,
                       kServerBasedRecognitionAudioFramesPerBuffer);
   }
+
+  // Let's destroy the audio source fetcher and ensure that the metric
+  // has been recorded.
+  audio_source_fetcher_.reset();
+  base::TimeDelta length = media::AudioTimestampHelper::FramesToTime(
+      audio_bus->frames(), kOriginalSampleRate);
+
+  const auto* histogram_name = is_server_based()
+                                   ? kServerBasedRecognitionSessionLength
+                                   : kOnDeviceRecognitionSessionLength;
+  histogram_tester_.ExpectTimeBucketCount(histogram_name, length,
+                                          /*count=*/1);
 }
 
 INSTANTIATE_TEST_SUITE_P(All, AudioSourceFetcherImplTest, ::testing::Bool());
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 12a71f9..85956df 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -4463,6 +4463,7 @@
         "//chromeos/ash/services/multidevice_setup/public/cpp:test_support",
         "//chromeos/ash/services/network_config",
         "//chromeos/components/onc:test_support",
+        "//chromeos/components/quick_answers:quick_answers",
         "//chromeos/components/quick_answers/public/cpp:cpp",
         "//chromeos/components/quick_answers/public/cpp:prefs",
         "//chromeos/components/remote_apps/mojom:mojom",
@@ -9097,6 +9098,12 @@
       "../browser/supervised_user/supervised_user_pref_store_unittest.cc",
       "../browser/supervised_user/supervised_user_service_unittest.cc",
     ]
+    if (is_chromeos) {
+      sources += [
+        "../browser/supervised_user/chromeos/mock_large_icon_service.cc",
+        "../browser/supervised_user/chromeos/mock_large_icon_service.h",
+      ]
+    }
     if (is_chromeos_ash) {
       sources += [ "../browser/supervised_user/chromeos/web_content_handler_impl_unittest.cc" ]
       deps += [ "//chrome/browser/ash/crosapi" ]
diff --git a/chrome/test/data/extensions/api_test/content_settings/incognitoisolation/test.js b/chrome/test/data/extensions/api_test/content_settings/incognitoisolation/test.js
index fb7995a..cd0d8a4e 100644
--- a/chrome/test/data/extensions/api_test/content_settings/incognitoisolation/test.js
+++ b/chrome/test/data/extensions/api_test/content_settings/incognitoisolation/test.js
@@ -27,7 +27,10 @@
   'automaticDownloads'
 ];
 
- function expect(expected, message) {
+// Settings that do not support site-specific exceptions.
+var globalOnlySettings = ['autoVerify'];
+
+function expect(expected, message) {
   return chrome.test.callbackPass(function(value) {
     chrome.test.assertEq(expected, value, message);
   });
@@ -50,8 +53,20 @@
       }, chrome.test.callbackPass());
     });
   },
+  function setGlobalContentSettings() {
+    globalOnlySettings.forEach(function(type) {
+      cs[type].set(
+          {
+            'primaryPattern': '<all_urls>',
+            'secondaryPattern': '<all_urls>',
+            'setting': givenPermission,
+            'scope': 'incognito_session_only'
+          },
+          chrome.test.callbackPass());
+    });
+  },
   function getContentSettings() {
-    settings.forEach(function(type) {
+    [...settings, ...globalOnlySettings].forEach(function(type) {
       var message = 'Setting for ' + type + ' should be ' + givenPermission;
       cs[type].get({
         'primaryUrl': 'http://www.example.com',
diff --git a/chrome/test/data/extensions/api_test/content_settings/standard/test.js b/chrome/test/data/extensions/api_test/content_settings/standard/test.js
index 150932b..641c942 100644
--- a/chrome/test/data/extensions/api_test/content_settings/standard/test.js
+++ b/chrome/test/data/extensions/api_test/content_settings/standard/test.js
@@ -7,17 +7,18 @@
 
 var cs = chrome.contentSettings;
 var default_content_settings = {
-  "cookies": "session_only",
-  "images": "allow",
-  "javascript": "block",
-  "popups": "block",
-  "location": "ask",
-  "notifications": "ask",
-  "fullscreen": "ask",
-  "mouselock": "ask",
-  "microphone": "ask",
-  "camera": "ask",
-  "automaticDownloads": "ask"
+  'cookies': 'session_only',
+  'images': 'allow',
+  'javascript': 'block',
+  'popups': 'block',
+  'location': 'ask',
+  'notifications': 'ask',
+  'fullscreen': 'ask',
+  'mouselock': 'ask',
+  'microphone': 'ask',
+  'camera': 'ask',
+  'automaticDownloads': 'ask',
+  'autoVerify': 'allow'
 };
 
 var settings = {
@@ -36,6 +37,9 @@
   'automaticDownloads': 'block'
 };
 
+// Settings that do not support site-specific exceptions.
+var globalOnlySettings = {'autoVerify': 'block'};
+
 // List of settings that are expected to return different values than were
 // written, due to deprecation. For example, "fullscreen" is set to "block" but
 // we expect this to be ignored, and so read back as "allow". Any setting
@@ -51,7 +55,7 @@
 
 // List of deprecated APIs. It is expected to return 'block' to get(), and will
 // be ignored to set() and clear().
-var deprecatedExtenionApis = [ 'plugins', 'unsandboxedPlugins' ];
+var deprecatedExtenionApis = ['plugins', 'unsandboxedPlugins'];
 
 Object.prototype.forEach = function(f) {
   var k;
@@ -103,7 +107,47 @@
       }, expect({'setting':setting}, message));
     });
   },
+  function setGlobalContentSettings() {
+    globalOnlySettings.forEach(function(type, setting) {
+      cs[type].set(
+          {
+            'primaryPattern': '<all_urls>',
+            'secondaryPattern': '<all_urls>',
+            'setting': setting
+          },
+          chrome.test.callbackPass());
+    });
+  },
+  function getGlobalSettings() {
+    globalOnlySettings.forEach(function(type, setting) {
+      var message = 'Setting for ' + type + ' should be ' + setting;
+      cs[type].get(
+          {
+            'primaryUrl': 'http://www.google.com',
+            'secondaryUrl': 'http://www.google.com'
+          },
+          expect({'setting': setting}, message));
+    });
+  },
   function invalidSettings() {
+    cs.autoVerify.set(
+        {
+          'primaryPattern': 'http://example.com/*',
+          'secondaryPattern': '<all_urls>',
+          'setting': 'allow'
+        },
+        chrome.test.callbackFail(
+            'Site-specific settings are not allowed for this type. ' +
+            'The URL pattern must be \'<all_urls>\'.'));
+    cs.autoVerify.set(
+        {
+          'primaryPattern': '<all_urls>',
+          'secondaryPattern': 'http://example.com/*',
+          'setting': 'allow'
+        },
+        chrome.test.callbackFail(
+            'Site-specific settings are not allowed for this type. ' +
+            'The URL pattern must be \'<all_urls>\'.'));
     cs.cookies.get({
       'primaryUrl': 'moo'
     }, chrome.test.callbackFail("The URL \"moo\" is invalid."));
@@ -128,7 +172,6 @@
     }
     chrome.test.assertTrue(caught);
   },
-
   function testDeprecatedApi_SetIgnored() {
     deprecatedExtenionApis.forEach(api => {
       cs[api].set(
diff --git a/chrome/test/data/webui/cr_elements/cr_url_list_item_test.ts b/chrome/test/data/webui/cr_elements/cr_url_list_item_test.ts
index a8b28f6..a6803c71 100644
--- a/chrome/test/data/webui/cr_elements/cr_url_list_item_test.ts
+++ b/chrome/test/data/webui/cr_elements/cr_url_list_item_test.ts
@@ -93,4 +93,17 @@
     const slotElements = slot.assignedElements();
     assertEquals(1, slotElements.length);
   });
+
+  test('DisplaysMaxImageCount', () => {
+    element.imageUrls = [
+      'http://www.first.com',
+      'http://www.second.com',
+      'http://www.third.com',
+    ];
+    flush();
+    const imageElements =
+        element.shadowRoot!.querySelectorAll<HTMLElement>('.folder-image');
+    // No more than two images may be displayed for a folder.
+    assertEquals(2, imageElements.length);
+  });
 });
diff --git a/chrome/test/data/webui/new_tab_page/BUILD.gn b/chrome/test/data/webui/new_tab_page/BUILD.gn
index ac2b492..4d05562 100644
--- a/chrome/test/data/webui/new_tab_page/BUILD.gn
+++ b/chrome/test/data/webui/new_tab_page/BUILD.gn
@@ -45,6 +45,7 @@
     "//ui/webui/resources/cr_components/customize_themes:build_ts",
     "//ui/webui/resources/cr_components/help_bubble:build_ts",
     "//ui/webui/resources/cr_components/history_clusters:build_ts",
+    "//ui/webui/resources/cr_components/image_service:build_ts",
     "//ui/webui/resources/cr_components/most_visited:build_ts",
     "//ui/webui/resources/cr_components/omnibox:build_ts",
     "//ui/webui/resources/cr_elements:build_ts",
diff --git a/chrome/test/data/webui/new_tab_page/modules/history_clusters/tile_test.ts b/chrome/test/data/webui/new_tab_page/modules/history_clusters/tile_test.ts
index 59a06de..d833a86 100644
--- a/chrome/test/data/webui/new_tab_page/modules/history_clusters/tile_test.ts
+++ b/chrome/test/data/webui/new_tab_page/modules/history_clusters/tile_test.ts
@@ -5,33 +5,46 @@
 import 'chrome://webui-test/mojo_webui_test_support.js';
 
 import {URLVisit} from 'chrome://new-tab-page/history_cluster_types.mojom-webui.js';
-import {TileModuleElement} from 'chrome://new-tab-page/lazy_load.js';
+import {ImageServiceBrowserProxy, TileModuleElement} from 'chrome://new-tab-page/lazy_load.js';
 import {$$} from 'chrome://new-tab-page/new_tab_page.js';
+import {ImageServiceHandlerRemote} from 'chrome://resources/cr_components/image_service/image_service.mojom-webui.js';
 import {assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {waitAfterNextRender} from 'chrome://webui-test/polymer_test_util.js';
+import {fakeMetricsPrivate, MetricsTracker} from 'chrome://webui-test/metrics_test_support.js';
+import {flushTasks, waitAfterNextRender} from 'chrome://webui-test/polymer_test_util.js';
+import {TestMock} from 'chrome://webui-test/test_mock.js';
+
+import {installMock} from '../../test_support.js';
 
 import {createVisit} from './test_support.js';
 
 suite('NewTabPageModulesHistoryClustersModuleTileTest', () => {
+  let imageServiceHandler: TestMock<ImageServiceHandlerRemote>;
+  let metrics: MetricsTracker;
+
   setup(() => {
     document.body.innerHTML = window.trustedTypes!.emptyHTML;
+    imageServiceHandler = installMock(
+        ImageServiceHandlerRemote,
+        mock => ImageServiceBrowserProxy.setInstance(
+            new ImageServiceBrowserProxy(mock)));
+    metrics = fakeMetricsPrivate();
   });
 
-  async function initializeModule(visit: URLVisit): Promise<TileModuleElement> {
+  function initializeModule(visit: URLVisit): TileModuleElement {
     const tileElement = new TileModuleElement();
     tileElement.visit = visit;
     document.body.append(tileElement);
-    await waitAfterNextRender(tileElement);
     return tileElement;
   }
 
   test('Tile element populated with correct data', async () => {
     // Arrange.
-    const tileElement = await initializeModule(createVisit(
+    const tileElement = initializeModule(createVisit(
         BigInt(1), 'https://www.test.com/1', 'https://www.test.com/1',
         'Test Title 1', false, '1 min ago'));
 
     // Assert.
+    await waitAfterNextRender(tileElement);
     assertTrue(!!tileElement);
     assertEquals($$(tileElement, '#title')!.innerHTML, 'Test Title 1');
     assertEquals('1 min ago', $$(tileElement, '#date')!.innerHTML);
@@ -40,4 +53,28 @@
               .getPropertyValue('background-image')
               .trim());
   });
+
+  [true, false].forEach(
+      (success) =>
+          test(`Metric sent on success ${success} image load`, async () => {
+            // Arrange.
+            const imageResult = success ?
+                {result: {imageUrl: {url: 'https://example.com/image.png'}}} :
+                null;
+            imageServiceHandler.setResultFor(
+                'getPageImageUrl', Promise.resolve(imageResult));
+            initializeModule(createVisit(
+                BigInt(1), 'https://www.test.com/1', 'https://www.test.com/1',
+                'Test Title 1', true, '1 min ago'));
+
+            await flushTasks();
+
+            // Assert
+            assertEquals(
+                1, imageServiceHandler.getCallCount('getPageImageUrl'));
+            assertEquals(
+                1,
+                metrics.count(
+                    'NewTabPage.HistoryClusters.ImageLoadSuccess', success));
+          }));
 });
diff --git a/chrome/test/data/webui/settings/chromeos/BUILD.gn b/chrome/test/data/webui/settings/chromeos/BUILD.gn
index 15a9763..c0899144 100644
--- a/chrome/test/data/webui/settings/chromeos/BUILD.gn
+++ b/chrome/test/data/webui/settings/chromeos/BUILD.gn
@@ -123,7 +123,6 @@
     "per_device_keyboard_subsection_test.js",
     "per_device_keyboard_remap_keys_test.js",
     "per_device_keyboard_test.js",
-    "per_device_mouse_subsection_test.js",
     "per_device_pointing_stick_subsection_test.js",
     "per_device_touchpad_subsection_test.js",
     "per_device_touchpad_test.js",
@@ -131,7 +130,6 @@
     "privacy_hub_subpage_tests.js",
     "quick_unlock_authenticate_browsertest_chromeos.js",
     "search_engine_test.js",
-    "search_subpage_test.js",
     "select_to_speak_subpage_tests.js",
     "settings_scheduler_slider_test.js",
     "settings_traffic_counters_test.js",
@@ -206,6 +204,7 @@
     "date_time_page/timezone_selector_test.ts",
     "date_time_page/timezone_subpage_test.ts",
 
+    "device_page/per_device_mouse_subsection_test.ts",
     "device_page/per_device_mouse_test.ts",
     "device_page/per_device_pointing_stick_test.ts",
 
@@ -233,6 +232,8 @@
 
     "os_printing_page/os_printing_page_tests.ts",
 
+    "os_search_page/search_subpage_test.ts",
+
     "os_settings_ui/os_settings_ui_about_page_test.js",
     "os_settings_ui/os_settings_ui_menu_test.js",
     "os_settings_ui/os_settings_ui_test.ts",
diff --git a/chrome/test/data/webui/settings/chromeos/device_page/per_device_mouse_subsection_test.ts b/chrome/test/data/webui/settings/chromeos/device_page/per_device_mouse_subsection_test.ts
new file mode 100644
index 0000000..664b81d
--- /dev/null
+++ b/chrome/test/data/webui/settings/chromeos/device_page/per_device_mouse_subsection_test.ts
@@ -0,0 +1,227 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
+
+import {CrToggleElement, FakeInputDeviceSettingsProvider, fakeMice, Mouse, Router, routes, setInputDeviceSettingsProviderForTesting, SettingsDropdownMenuElement, SettingsPerDeviceMouseSubsectionElement, SettingsSliderElement, SettingsToggleButtonElement} from 'chrome://os-settings/chromeos/os_settings.js';
+import {assert} from 'chrome://resources/js/assert_ts.js';
+import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
+import {flushTasks, waitAfterNextRender} from 'chrome://webui-test/polymer_test_util.js';
+import {isVisible} from 'chrome://webui-test/test_util.js';
+
+const MOUSE_ACCELERATION_SETTING_ID = 408;
+
+suite('<settings-per-device-mouse-subsection>', function() {
+  let subsection: SettingsPerDeviceMouseSubsectionElement;
+  let provider: FakeInputDeviceSettingsProvider;
+
+  teardown(() => {
+    subsection.remove();
+  });
+
+  function initializePerDeviceMouseSubsection(): Promise<void> {
+    provider = new FakeInputDeviceSettingsProvider();
+    provider.setFakeMice(fakeMice);
+    setInputDeviceSettingsProviderForTesting(provider);
+    subsection = document.createElement('settings-per-device-mouse-subsection');
+    assert(subsection);
+    subsection.set('mouse', {...fakeMice[0]});
+    subsection.set('allowScrollSettings_', true);
+    document.body.appendChild(subsection);
+    return flushTasks();
+  }
+
+  function changeMouseSubsectionState(
+      mouse: Mouse, allowScrollSettings: boolean): Promise<void> {
+    subsection.set('mouse', mouse);
+    subsection.set('allowScrollSettings_', allowScrollSettings);
+    return flushTasks();
+  }
+
+  /**
+   * Test that API are updated when mouse settings change.
+   */
+  test('Update API when mouse settings change', async () => {
+    await initializePerDeviceMouseSubsection();
+    const mouseSwapButtonDropdown =
+        subsection.shadowRoot!.querySelector<SettingsDropdownMenuElement>(
+            '#mouseSwapButtonDropdown');
+    assert(mouseSwapButtonDropdown);
+    await flushTasks();
+    let updatedMice = await provider.getConnectedMouseSettings();
+    assertEquals(
+        updatedMice[0]!.settings.swapRight,
+        mouseSwapButtonDropdown.pref!.value);
+
+    const mouseAccelerationToggleButton =
+        subsection.shadowRoot!.querySelector<SettingsToggleButtonElement>(
+            '#mouseAcceleration');
+    assert(mouseAccelerationToggleButton);
+    mouseAccelerationToggleButton.click();
+    await flushTasks();
+    updatedMice = await provider.getConnectedMouseSettings();
+    assertEquals(
+        updatedMice[0]!.settings.accelerationEnabled,
+        mouseAccelerationToggleButton.pref!.value);
+
+    const mouseSpeedSlider =
+        subsection.shadowRoot!.querySelector<SettingsSliderElement>(
+            '#mouseSpeedSlider');
+    assert(mouseSpeedSlider);
+    const arrowRightEvent =
+        new KeyboardEvent('keypress', {'key': 'ArrowRight'});
+    mouseSpeedSlider.shadowRoot!.querySelector('cr-slider')!.dispatchEvent(
+        arrowRightEvent);
+    await flushTasks();
+    updatedMice = await provider.getConnectedMouseSettings();
+    assertEquals(
+        updatedMice[0]!.settings.sensitivity, mouseSpeedSlider.pref!.value);
+
+    const mouseReverseScrollToggleButton =
+        subsection.shadowRoot!.querySelector<CrToggleElement>(
+            '#mouseReverseScroll');
+    assert(mouseReverseScrollToggleButton);
+    mouseReverseScrollToggleButton.click();
+    await flushTasks();
+    updatedMice = await provider.getConnectedMouseSettings();
+    assertEquals(
+        updatedMice[0]!.settings.reverseScrolling,
+        mouseReverseScrollToggleButton.checked);
+
+    const mouseScrollAccelerationToggleButton =
+        subsection.shadowRoot!.querySelector<SettingsToggleButtonElement>(
+            '#mouseScrollAcceleration');
+    assert(mouseScrollAccelerationToggleButton);
+    mouseScrollAccelerationToggleButton.click();
+    await flushTasks();
+    updatedMice = await provider.getConnectedMouseSettings();
+    assertEquals(
+        updatedMice[0]!.settings.scrollAcceleration,
+        mouseScrollAccelerationToggleButton.pref!.value);
+
+    const mouseScrollSpeedSlider =
+        subsection.shadowRoot!.querySelector<SettingsSliderElement>(
+            '#mouseScrollSpeedSlider');
+    assert(mouseScrollSpeedSlider);
+    mouseScrollSpeedSlider.shadowRoot!.querySelector('cr-slider')!
+        .dispatchEvent(arrowRightEvent);
+    await flushTasks();
+    updatedMice = await provider.getConnectedMouseSettings();
+    assertEquals(
+        updatedMice[0]!.settings.scrollSensitivity,
+        mouseScrollSpeedSlider.pref!.value);
+  });
+
+  /**
+   * Test that mouse settings data are from the mouse provider.
+   */
+  test('Verify mouse settings data', async () => {
+    await initializePerDeviceMouseSubsection();
+    let mouseSwapButtonDropdown =
+        subsection.shadowRoot!.querySelector<SettingsDropdownMenuElement>(
+            '#mouseSwapButtonDropdown');
+    assertEquals(
+        fakeMice[0]!.settings.swapRight, mouseSwapButtonDropdown!.pref!.value);
+    let mouseAccelerationToggleButton =
+        subsection.shadowRoot!.querySelector<SettingsToggleButtonElement>(
+            '#mouseAcceleration');
+    assertEquals(
+        fakeMice[0]!.settings.accelerationEnabled,
+        mouseAccelerationToggleButton!.pref!.value);
+    let mouseSpeedSlider =
+        subsection.shadowRoot!.querySelector<SettingsSliderElement>(
+            '#mouseSpeedSlider');
+    assertEquals(
+        fakeMice[0]!.settings.sensitivity, mouseSpeedSlider!.pref!.value);
+    assertEquals(
+        fakeMice[0]!.settings.reverseScrolling,
+        subsection.get('reverseScrollValue'));
+    let mouseScrollAccelerationToggleButton =
+        subsection.shadowRoot!.querySelector<SettingsSliderElement>(
+            '#mouseScrollAcceleration');
+    assertTrue(isVisible(mouseScrollAccelerationToggleButton));
+    assertEquals(
+        fakeMice[0]!.settings.scrollAcceleration,
+        mouseScrollAccelerationToggleButton!.pref!.value);
+    let mouseScrollSpeedSlider =
+        subsection.shadowRoot!.querySelector<SettingsSliderElement>(
+            '#mouseScrollSpeedSlider');
+    assert(mouseScrollSpeedSlider);
+    assertTrue(isVisible(mouseScrollSpeedSlider));
+    assertEquals(
+        fakeMice[0]!.settings.scrollSensitivity,
+        mouseScrollSpeedSlider.pref!.value);
+
+    assert(fakeMice[1]);
+    await changeMouseSubsectionState(fakeMice[1], false);
+    mouseSwapButtonDropdown =
+        subsection.shadowRoot!.querySelector('#mouseSwapButtonDropdown');
+    assertEquals(
+        fakeMice[1]!.settings.swapRight, mouseSwapButtonDropdown!.pref!.value);
+    mouseAccelerationToggleButton =
+        subsection.shadowRoot!.querySelector('#mouseAcceleration');
+    assertEquals(
+        fakeMice[1]!.settings.accelerationEnabled,
+        mouseAccelerationToggleButton!.pref!.value);
+    mouseSpeedSlider =
+        subsection.shadowRoot!.querySelector('#mouseSpeedSlider');
+    assertEquals(
+        fakeMice[1]!.settings.sensitivity, mouseSpeedSlider!.pref!.value);
+    assertEquals(
+        fakeMice[1]!.settings.reverseScrolling,
+        subsection.get('reverseScrollValue'));
+    mouseScrollAccelerationToggleButton =
+        subsection.shadowRoot!.querySelector('#mouseScrollAcceleration');
+    assertFalse(isVisible(mouseScrollAccelerationToggleButton));
+    mouseScrollSpeedSlider =
+        subsection.shadowRoot!.querySelector('#mouseScrollSpeedSlider');
+    assertFalse(isVisible(mouseScrollSpeedSlider));
+  });
+
+  /**
+   * Verify entering the page with search tags matched will auto focus the
+   * searched element.
+   */
+  test('deep linking mixin focus on the first searched element', async () => {
+    await initializePerDeviceMouseSubsection();
+    const mouseAccelerationToggle =
+        subsection.shadowRoot!.querySelector<HTMLElement>('#mouseAcceleration');
+    subsection.set('mouseIndex', 0);
+    // Enter the page from auto repeat search tag.
+    const url = new URLSearchParams(
+        'search=mouse+accel&settingId=' +
+        encodeURIComponent(MOUSE_ACCELERATION_SETTING_ID));
+
+    await Router.getInstance().navigateTo(
+        routes.PER_DEVICE_MOUSE,
+        /* dynamicParams= */ url, /* removeSearch= */ true);
+
+    assert(mouseAccelerationToggle);
+    await waitAfterNextRender(mouseAccelerationToggle);
+    assertEquals(subsection.shadowRoot!.activeElement, mouseAccelerationToggle);
+  });
+
+  /**
+   * Verify entering the page with search tags matched wll not auto focus the
+   * searched element if it's not the first keyboard displayed.
+   */
+  test('deep linking mixin does not focus on second element', async () => {
+    await initializePerDeviceMouseSubsection();
+    const mouseAccelerationToggle =
+        subsection.shadowRoot!.querySelector('#mouseAcceleration');
+    subsection.set('mouseIndex', 1);
+    // Enter the page from auto repeat search tag.
+    const url = new URLSearchParams(
+        'search=mouse+accel&settingId=' +
+        encodeURIComponent(MOUSE_ACCELERATION_SETTING_ID));
+
+    await Router.getInstance().navigateTo(
+        routes.PER_DEVICE_MOUSE,
+        /* dynamicParams= */ url, /* removeSearch= */ true);
+    await flushTasks();
+
+    assert(mouseAccelerationToggle);
+    assertEquals(null, subsection.shadowRoot!.activeElement);
+  });
+});
diff --git a/chrome/test/data/webui/settings/chromeos/os_search_page/search_subpage_test.ts b/chrome/test/data/webui/settings/chromeos/os_search_page/search_subpage_test.ts
new file mode 100644
index 0000000..5269d669
--- /dev/null
+++ b/chrome/test/data/webui/settings/chromeos/os_search_page/search_subpage_test.ts
@@ -0,0 +1,291 @@
+// Copyright 2021 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'chrome://os-settings/chromeos/lazy_load.js';
+
+import {SettingsSearchSubpageElement} from 'chrome://os-settings/chromeos/lazy_load.js';
+import {CrSettingsPrefs, Router, routes, SettingsPrefsElement, SettingsToggleButtonElement} from 'chrome://os-settings/chromeos/os_settings.js';
+import {assert} from 'chrome://resources/js/assert_ts.js';
+import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
+import {getDeepActiveElement} from 'chrome://resources/js/util_ts.js';
+import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
+import {waitAfterNextRender} from 'chrome://webui-test/polymer_test_util.js';
+
+suite('<settings-search-subpage>', () => {
+  let page: SettingsSearchSubpageElement;
+  let prefElement: SettingsPrefsElement;
+
+  suiteSetup(() => {
+    loadTimeData.overrideValues({
+      shouldShowQuickAnswersSettings: true,
+      quickAnswersSubToggleEnabled: true,
+    });
+  });
+
+  setup(async () => {
+    prefElement = document.createElement('settings-prefs');
+    document.body.appendChild(prefElement);
+
+    await CrSettingsPrefs.initialized;
+    page = document.createElement('settings-search-subpage');
+    page.prefs = prefElement.prefs;
+    document.body.appendChild(page);
+    flush();
+  });
+
+  teardown(() => {
+    page.remove();
+    prefElement.remove();
+    CrSettingsPrefs.resetForTesting();
+  });
+
+  test('definitionToggleVisibility', () => {
+    let button =
+        page.shadowRoot!.querySelector('#quick-answers-definition-enable');
+    assertEquals(null, button);
+
+    page.setPrefValue('settings.quick_answers.enabled', true);
+    flush();
+
+    button = page.shadowRoot!.querySelector('#quick-answers-definition-enable');
+    assert(button);
+  });
+
+  test('translationToggleVisibility', () => {
+    let button =
+        page.shadowRoot!.querySelector('#quick-answers-translation-enable');
+    assertEquals(null, button);
+
+    page.setPrefValue('settings.quick_answers.enabled', true);
+    flush();
+
+    button =
+        page.shadowRoot!.querySelector('#quick-answers-translation-enable');
+    assert(button);
+  });
+
+  test('unitConversionToggleVisibility', () => {
+    let button =
+        page.shadowRoot!.querySelector('#quick-answers-unit-conversion-enable');
+    assertEquals(null, button);
+
+    page.setPrefValue('settings.quick_answers.enabled', true);
+    flush();
+
+    button =
+        page.shadowRoot!.querySelector('#quick-answers-unit-conversion-enable');
+    assert(button);
+  });
+
+  test('toggleQuickAnswers', () => {
+    flush();
+    const button = page.shadowRoot!.querySelector<SettingsToggleButtonElement>(
+        '#quick-answers-enable');
+    assert(button);
+    assertFalse(button.disabled);
+    assertFalse(button.checked);
+
+    let definitionButton =
+        page.shadowRoot!.querySelector('#quick-answers-definition-enable');
+    let translationButton =
+        page.shadowRoot!.querySelector('#quick-answers-translation-enable');
+    let unitConversionButton =
+        page.shadowRoot!.querySelector('#quick-answers-unit-conversion-enable');
+    assertEquals(null, definitionButton);
+    assertEquals(null, translationButton);
+    assertEquals(null, unitConversionButton);
+
+    // Tap the enable toggle button and ensure the state becomes enabled.
+    button.click();
+    flush();
+    assertTrue(button.checked);
+
+    definitionButton =
+        page.shadowRoot!.querySelector('#quick-answers-definition-enable');
+    translationButton =
+        page.shadowRoot!.querySelector('#quick-answers-translation-enable');
+    unitConversionButton =
+        page.shadowRoot!.querySelector('#quick-answers-unit-conversion-enable');
+    assert(definitionButton);
+    assert(translationButton);
+    assert(unitConversionButton);
+  });
+
+  test('toggleQuickAnswersDefinition', () => {
+    let button = page.shadowRoot!.querySelector<SettingsToggleButtonElement>(
+        '#quick-answers-definition-enable');
+    assertEquals(null, button);
+    page.setPrefValue('settings.quick_answers.enabled', true);
+    page.setPrefValue('settings.quick_answers.definition.enabled', false);
+    flush();
+
+    button = page.shadowRoot!.querySelector('#quick-answers-definition-enable');
+    assert(button);
+    assertFalse(button.disabled);
+    assertFalse(button.checked);
+
+    button.click();
+    flush();
+    assertTrue(button.checked);
+    assertTrue(page.getPref('settings.quick_answers.definition.enabled').value);
+  });
+
+  test('toggleQuickAnswersTranslation', () => {
+    let button = page.shadowRoot!.querySelector<SettingsToggleButtonElement>(
+        '#quick-answers-translation-enable');
+    assertEquals(null, button);
+    page.setPrefValue('settings.quick_answers.enabled', true);
+    page.setPrefValue('settings.quick_answers.translation.enabled', false);
+    flush();
+
+    button =
+        page.shadowRoot!.querySelector('#quick-answers-translation-enable');
+    assert(button);
+    assertFalse(button.disabled);
+    assertFalse(button.checked);
+
+    button.click();
+    flush();
+    assertTrue(button.checked);
+    assertTrue(
+        page.getPref('settings.quick_answers.translation.enabled').value);
+  });
+
+  test('clickLanguageSettingsLink', () => {
+    let button = page.shadowRoot!.querySelector<SettingsToggleButtonElement>(
+        '#quick-answers-translation-enable');
+    assertEquals(null, button);
+    page.setPrefValue('settings.quick_answers.enabled', true);
+    page.setPrefValue('settings.quick_answers.translation.enabled', false);
+    flush();
+
+    button =
+        page.shadowRoot!.querySelector('#quick-answers-translation-enable');
+    assert(button);
+    assertFalse(button.disabled);
+    assertFalse(button.checked);
+
+    const languageSettingsLink =
+        button.shadowRoot!.querySelector(
+                              '#sub-label-text-with-link')!.querySelector('a');
+    assert(languageSettingsLink);
+
+    languageSettingsLink.click();
+    flush();
+    assertFalse(button.checked);
+    assertFalse(
+        page.getPref('settings.quick_answers.translation.enabled').value);
+
+    assertEquals(
+        routes.OS_LANGUAGES_LANGUAGES, Router.getInstance().currentRoute);
+  });
+
+  test('toggleQuickAnswersUnitConversion', () => {
+    let button = page.shadowRoot!.querySelector<SettingsToggleButtonElement>(
+        '#quick-answers-unit-conversion-enable');
+    assertEquals(null, button);
+    page.setPrefValue('settings.quick_answers.enabled', true);
+    page.setPrefValue('settings.quick_answers.unit_conversion.enabled', false);
+    flush();
+
+    button =
+        page.shadowRoot!.querySelector('#quick-answers-unit-conversion-enable');
+    assert(button);
+    assertFalse(button.disabled);
+    assertFalse(button.checked);
+
+    button.click();
+    flush();
+    assertTrue(button.checked);
+    assertTrue(
+        page.getPref('settings.quick_answers.unit_conversion.enabled').value);
+  });
+
+  test('Deep link to Preferred Search Engine', async () => {
+    const params = new URLSearchParams();
+    params.append('settingId', '600');
+    Router.getInstance().navigateTo(routes.SEARCH_SUBPAGE, params);
+
+    const browserSearchSettingsLink =
+        page.shadowRoot!.querySelector('settings-search-engine')!.shadowRoot!
+            .querySelector('#browserSearchSettingsLink');
+    const deepLinkElement =
+        browserSearchSettingsLink!.shadowRoot!.querySelector('cr-icon-button');
+    assert(deepLinkElement);
+    await waitAfterNextRender(deepLinkElement);
+    assertEquals(
+        deepLinkElement, getDeepActiveElement(),
+        'Preferred Search Engine button should be focused for settingId=600.');
+  });
+
+  test('Deep link to Quick Answers On/Off', async () => {
+    const params = new URLSearchParams();
+    params.append('settingId', '608');
+    Router.getInstance().navigateTo(routes.SEARCH_SUBPAGE, params);
+
+    const deepLinkElement =
+        page.shadowRoot!.querySelector('#quick-answers-enable')!.shadowRoot!
+            .querySelector('cr-toggle');
+    assert(deepLinkElement);
+    await waitAfterNextRender(deepLinkElement);
+    assertEquals(
+        deepLinkElement, getDeepActiveElement(),
+        'Quick Answer On/Off toggle should be focused for settingId=608.');
+  });
+
+  test('Deep link to Quick Answers Definition', async () => {
+    page.setPrefValue('settings.quick_answers.enabled', true);
+    flush();
+
+    const params = new URLSearchParams();
+    params.append('settingId', '609');
+    Router.getInstance().navigateTo(routes.SEARCH_SUBPAGE, params);
+
+    const deepLinkElement =
+        page.shadowRoot!.querySelector('#quick-answers-definition-enable')!
+            .shadowRoot!.querySelector('cr-toggle');
+    assert(deepLinkElement);
+    await waitAfterNextRender(deepLinkElement);
+    assertEquals(
+        deepLinkElement, getDeepActiveElement(),
+        'Quick Answer definition toggle should be focused for settingId=609.');
+  });
+
+  test('Deep link to Quick Answers Translation', async () => {
+    page.setPrefValue('settings.quick_answers.enabled', true);
+    flush();
+
+    const params = new URLSearchParams();
+    params.append('settingId', '610');
+    Router.getInstance().navigateTo(routes.SEARCH_SUBPAGE, params);
+
+    const deepLinkElement =
+        page.shadowRoot!.querySelector('#quick-answers-translation-enable')!
+            .shadowRoot!.querySelector('cr-toggle');
+    assert(deepLinkElement);
+    await waitAfterNextRender(deepLinkElement);
+    assertEquals(
+        deepLinkElement, getDeepActiveElement(),
+        'Quick Answer translation toggle should be focused for settingId=610.');
+  });
+
+  test('Deep link to Quick Answers Unit Conversion', async () => {
+    page.setPrefValue('settings.quick_answers.enabled', true);
+    flush();
+
+    const params = new URLSearchParams();
+    params.append('settingId', '611');
+    Router.getInstance().navigateTo(routes.SEARCH_SUBPAGE, params);
+
+    const deepLinkElement =
+        page.shadowRoot!.querySelector('#quick-answers-unit-conversion-enable')!
+            .shadowRoot!.querySelector('cr-toggle');
+    assert(deepLinkElement);
+    await waitAfterNextRender(deepLinkElement);
+    assertEquals(
+        deepLinkElement, getDeepActiveElement(),
+        'Quick Answer unit conversion toggle should be focused for settingId=611.');
+  });
+});
diff --git a/chrome/test/data/webui/settings/chromeos/os_settings_v3_browsertest.js b/chrome/test/data/webui/settings/chromeos/os_settings_v3_browsertest.js
index 1cbb3b1..a52a8c8df 100644
--- a/chrome/test/data/webui/settings/chromeos/os_settings_v3_browsertest.js
+++ b/chrome/test/data/webui/settings/chromeos/os_settings_v3_browsertest.js
@@ -266,6 +266,11 @@
    {enabled: ['ash::features::kInputDeviceSettingsSplit']}
  ],
  [
+   'DevicePagePerDeviceMouseSubsection',
+   'device_page/per_device_mouse_subsection_test.js',
+   {enabled: ['ash::features::kInputDeviceSettingsSplit']}
+ ],
+ [
    'DevicePagePerDevicePointingStick',
    'device_page/per_device_pointing_stick_test.js',
    {enabled: ['ash::features::kInputDeviceSettingsSplit']}
@@ -424,6 +429,7 @@
  ['OsPairedBluetoothListItem', 'os_paired_bluetooth_list_item_tests.js'],
  ['OsPeoplePageAddUserDialog', 'os_people_page/add_user_dialog_tests.js'],
  ['OsPrintingPage', 'os_printing_page/os_printing_page_tests.js'],
+ ['OsSearchPageSearchSubpage', 'os_search_page/search_subpage_test.js'],
  ['OsSettingsPage', 'os_settings_page_test.js'],
  ['OsSettingsUi', 'os_settings_ui/os_settings_ui_test.js'],
  ['OsSettingsUiAboutPage', 'os_settings_ui/os_settings_ui_about_page_test.js'],
@@ -468,10 +474,6 @@
    {enabled: ['ash::features::kInputDeviceSettingsSplit']},
  ],
  [
-   'PerDeviceMouseSubsection', 'per_device_mouse_subsection_test.js',
-   {enabled: ['ash::features::kInputDeviceSettingsSplit']}
- ],
- [
    'PerDevicePointingStickSubsection',
    'per_device_pointing_stick_subsection_test.js',
    {enabled: ['ash::features::kInputDeviceSettingsSplit']}
@@ -497,7 +499,6 @@
  ['PrivacyPage', 'os_privacy_page_test.js'],
  ['ResetPage', 'os_reset_page_test.js'],
  ['SettingsSchedulerSlider', 'settings_scheduler_slider_test.js'],
- ['SearchSubpage', 'search_subpage_test.js'],
  [
    'SelectToSpeakSubpage',
    'select_to_speak_subpage_tests.js',
diff --git a/chrome/test/data/webui/settings/chromeos/per_device_keyboard_remap_keys_test.js b/chrome/test/data/webui/settings/chromeos/per_device_keyboard_remap_keys_test.js
index 5ea449f..a21782c 100644
--- a/chrome/test/data/webui/settings/chromeos/per_device_keyboard_remap_keys_test.js
+++ b/chrome/test/data/webui/settings/chromeos/per_device_keyboard_remap_keys_test.js
@@ -28,13 +28,13 @@
     provider = null;
   });
 
-  async function initializeRemapKeysPage() {
+  async function initializeRemapKeysPage(keyboards = fakeKeyboards) {
     page = document.createElement('settings-per-device-keyboard-remap-keys');
-    assertFalse(page.isInitialized);
+    page.keyboards = keyboards;
     // Set the current route with keyboardId as search param and notify
     // the observer to update keyboard settings.
     const url = new URLSearchParams(
-        'keyboardId=' + encodeURIComponent(fakeKeyboards[0].id));
+        'keyboardId=' + encodeURIComponent(keyboards[0].id));
     await Router.getInstance().setCurrentRoute(
         routes.PER_DEVICE_KEYBOARD_REMAP_KEYS,
         /* dynamicParams= */ url, /* removeSearch= */ true);
@@ -44,6 +44,11 @@
     return flushTasks();
   }
 
+  async function setKeyboards(keyboards) {
+    page.keyboards = keyboards;
+    return flushTasks();
+  }
+
   /**
    * Check that all the prefs are set to default keyboard value.
    */
@@ -63,11 +68,6 @@
     assertEquals(page.fakeMetaPref.value, metaDefaultMapping);
   }
 
-  async function getConnectedKeyboardSettings() {
-    const keyboards = await provider.getConnectedKeyboardSettings();
-    return keyboards;
-  }
-
   /**
    * Verify that the remap subpage is correctly loaded with keyboard data.
    */
@@ -241,8 +241,9 @@
     assertEquals(
         routes.PER_DEVICE_KEYBOARD_REMAP_KEYS,
         Router.getInstance().currentRoute);
+    assertEquals(page.keyboardId, page.keyboards[0].id);
     const updatedKeyboards = [fakeKeyboards[1], fakeKeyboards[2]];
-    page.onKeyboardListUpdated(updatedKeyboards);
+    await setKeyboards(updatedKeyboards);
     assertEquals(routes.PER_DEVICE_KEYBOARD, Router.getInstance().currentRoute);
   });
 
@@ -266,7 +267,7 @@
     page.set('fakeEscPref.value', ModifierKey.kVoid);
 
     // Verify that the keyboard settings in the provider are updated.
-    const keyboards = await getConnectedKeyboardSettings();
+    const keyboards = page.keyboards;
     assertTrue(!!keyboards);
     const updatedRemapping = keyboards[0].settings.modifierRemappings;
     assertTrue(!!updatedRemapping);
diff --git a/chrome/test/data/webui/settings/chromeos/per_device_mouse_subsection_test.js b/chrome/test/data/webui/settings/chromeos/per_device_mouse_subsection_test.js
deleted file mode 100644
index 5f0cc4a..0000000
--- a/chrome/test/data/webui/settings/chromeos/per_device_mouse_subsection_test.js
+++ /dev/null
@@ -1,226 +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.
-
-import 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
-
-import {FakeInputDeviceSettingsProvider, fakeMice, Router, routes, setInputDeviceSettingsProviderForTesting, SettingsPerDeviceMouseSubsectionElement} from 'chrome://os-settings/chromeos/os_settings.js';
-import {assert} from 'chrome://resources/ash/common/assert.js';
-import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {flushTasks, waitAfterNextRender} from 'chrome://webui-test/polymer_test_util.js';
-import {isVisible} from 'chrome://webui-test/test_util.js';
-
-const MOUSE_ACCELERATION_SETTING_ID = 408;
-
-suite('PerDeviceMouseSubsection', function() {
-  /**
-   * @type {?SettingsPerDeviceMouseSubsectionElement}
-   */
-  let subsection = null;
-  /**
-   * @type {?FakeInputDeviceSettingsProvider}
-   */
-  let provider = null;
-
-  setup(() => {
-    PolymerTest.clearBody();
-  });
-
-  teardown(() => {
-    subsection = null;
-    provider = null;
-  });
-
-  /**
-   * @return {!Promise}
-   */
-  function initializePerDeviceMouseSubsection() {
-    provider = new FakeInputDeviceSettingsProvider();
-    provider.setFakeMice(fakeMice);
-    setInputDeviceSettingsProviderForTesting(provider);
-    subsection = document.createElement('settings-per-device-mouse-subsection');
-    assertTrue(subsection != null);
-    subsection.mouse = {...fakeMice[0]};
-    subsection.allowScrollSettings_ = true;
-    document.body.appendChild(subsection);
-    return flushTasks();
-  }
-
-  /**
-   * @param {!Object} mouse
-   * @param {!Boolean} allowScrollSettings
-   * @return {!Promise}
-   */
-  function changeMouseSubsectionState(mouse, allowScrollSettings) {
-    subsection.mouse = mouse;
-    subsection.allowScrollSettings_ = allowScrollSettings;
-    return flushTasks();
-  }
-
-  async function getConnectedMouseSettings() {
-    const mice = await provider.getConnectedMouseSettings();
-    return mice;
-  }
-
-  // Test that API are updated when mouse settings change.
-  test('Update API when mouse settings change', async () => {
-    await initializePerDeviceMouseSubsection();
-    const mouseSwapButtonDropdown =
-        subsection.shadowRoot.querySelector('#mouseSwapButtonDropdown');
-    mouseSwapButtonDropdown.pref = {
-      ...mouseSwapButtonDropdown.pref,
-      value: false,
-    };
-    await flushTasks();
-    let updatedMice = await getConnectedMouseSettings();
-    assertEquals(
-        updatedMice[0].settings.swapRight, mouseSwapButtonDropdown.pref.value);
-
-    const mouseAccelerationToggleButton =
-        subsection.shadowRoot.querySelector('#mouseAcceleration');
-    mouseAccelerationToggleButton.click();
-    await flushTasks();
-    updatedMice = await getConnectedMouseSettings();
-    assertEquals(
-        updatedMice[0].settings.accelerationEnabled,
-        mouseAccelerationToggleButton.pref.value);
-
-    const mouseSpeedSlider =
-        assert(subsection.shadowRoot.querySelector('#mouseSpeedSlider'));
-    MockInteractions.pressAndReleaseKeyOn(
-        mouseSpeedSlider.shadowRoot.querySelector('cr-slider'), 39 /* right */,
-        [], 'ArrowRight');
-    await flushTasks();
-    updatedMice = await getConnectedMouseSettings();
-    assertEquals(
-        updatedMice[0].settings.sensitivity, mouseSpeedSlider.pref.value);
-
-    const mouseReverseScrollToggleButton =
-        subsection.shadowRoot.querySelector('#mouseReverseScroll');
-    mouseReverseScrollToggleButton.click();
-    await flushTasks();
-    updatedMice = await getConnectedMouseSettings();
-    assertEquals(
-        updatedMice[0].settings.reverseScrolling,
-        mouseReverseScrollToggleButton.checked);
-
-    const mouseScrollAccelerationToggleButton =
-        subsection.shadowRoot.querySelector('#mouseScrollAcceleration');
-    mouseScrollAccelerationToggleButton.click();
-    await flushTasks();
-    updatedMice = await getConnectedMouseSettings();
-    assertEquals(
-        updatedMice[0].settings.scrollAcceleration,
-        mouseScrollAccelerationToggleButton.pref.value);
-
-    const mouseScrollSpeedSlider =
-        assert(subsection.shadowRoot.querySelector('#mouseScrollSpeedSlider'));
-    MockInteractions.pressAndReleaseKeyOn(
-        mouseScrollSpeedSlider.shadowRoot.querySelector('cr-slider'),
-        39 /* right */, [], 'ArrowRight');
-    await flushTasks();
-    updatedMice = await getConnectedMouseSettings();
-    assertEquals(
-        updatedMice[0].settings.scrollSensitivity,
-        mouseScrollSpeedSlider.pref.value);
-  });
-
-  // Test that mouse settings data are from the mouse provider.
-  test('Verify mouse settings data', async () => {
-    await initializePerDeviceMouseSubsection();
-    let mouseSwapButtonDropdown =
-        subsection.shadowRoot.querySelector('#mouseSwapButtonDropdown');
-    assertEquals(
-        fakeMice[0].settings.swapRight, mouseSwapButtonDropdown.pref.value);
-    let mouseAccelerationToggleButton =
-        subsection.shadowRoot.querySelector('#mouseAcceleration');
-    assertEquals(
-        fakeMice[0].settings.accelerationEnabled,
-        mouseAccelerationToggleButton.pref.value);
-    let mouseSpeedSlider =
-        assert(subsection.shadowRoot.querySelector('#mouseSpeedSlider'));
-    assertEquals(fakeMice[0].settings.sensitivity, mouseSpeedSlider.pref.value);
-    assertEquals(
-        fakeMice[0].settings.reverseScrolling, subsection.reverseScrollValue);
-    let mouseScrollAccelerationToggleButton =
-        subsection.shadowRoot.querySelector('#mouseScrollAcceleration');
-    assertTrue(isVisible(mouseScrollAccelerationToggleButton));
-    assertEquals(
-        fakeMice[0].settings.scrollAcceleration,
-        mouseScrollAccelerationToggleButton.pref.value);
-    let mouseScrollSpeedSlider =
-        assert(subsection.shadowRoot.querySelector('#mouseScrollSpeedSlider'));
-    assertTrue(isVisible(mouseScrollSpeedSlider));
-    assertEquals(
-        fakeMice[0].settings.scrollSensitivity,
-        mouseScrollSpeedSlider.pref.value);
-
-    await changeMouseSubsectionState(fakeMice[1], false);
-    mouseSwapButtonDropdown =
-        subsection.shadowRoot.querySelector('#mouseSwapButtonDropdown');
-    assertEquals(
-        fakeMice[1].settings.swapRight, mouseSwapButtonDropdown.pref.value);
-    mouseAccelerationToggleButton =
-        subsection.shadowRoot.querySelector('#mouseAcceleration');
-    assertEquals(
-        fakeMice[1].settings.accelerationEnabled,
-        mouseAccelerationToggleButton.pref.value);
-    mouseSpeedSlider =
-        assert(subsection.shadowRoot.querySelector('#mouseSpeedSlider'));
-    assertEquals(fakeMice[1].settings.sensitivity, mouseSpeedSlider.pref.value);
-    assertEquals(
-        fakeMice[1].settings.reverseScrolling, subsection.reverseScrollValue);
-    mouseScrollAccelerationToggleButton =
-        subsection.shadowRoot.querySelector('#mouseScrollAcceleration');
-    assertFalse(isVisible(mouseScrollAccelerationToggleButton));
-    mouseScrollSpeedSlider =
-        subsection.shadowRoot.querySelector('#mouseScrollSpeedSlider');
-    assertFalse(isVisible(mouseScrollSpeedSlider));
-  });
-
-  /**
-   * Verify entering the page with search tags matched will auto focus the
-   * searched element.
-   */
-  test('deep linking mixin focus on the first searched element', async () => {
-    await initializePerDeviceMouseSubsection();
-    const mouseAccelerationToggle =
-        subsection.shadowRoot.querySelector('#mouseAcceleration');
-    subsection.mouseIndex = 0;
-    // Enter the page from auto repeat search tag.
-    const url = new URLSearchParams(
-        'search=mouse+accel&settingId=' +
-        encodeURIComponent(MOUSE_ACCELERATION_SETTING_ID));
-
-    await Router.getInstance().navigateTo(
-        routes.PER_DEVICE_MOUSE,
-        /* dynamicParams= */ url, /* removeSearch= */ true);
-
-    await waitAfterNextRender(mouseAccelerationToggle);
-    assertTrue(!!mouseAccelerationToggle);
-    assertEquals(subsection.shadowRoot.activeElement, mouseAccelerationToggle);
-  });
-
-  /**
-   * Verify entering the page with search tags matched wll not auto focus the
-   * searched element if it's not the first keyboard displayed.
-   */
-  test('deep linkng mixin does not focus on second element', async () => {
-    await initializePerDeviceMouseSubsection();
-    const mouseAccelerationToggle =
-        subsection.shadowRoot.querySelector('#mouseAcceleration');
-    subsection.mouseIndex = 1;
-    // Enter the page from auto repeat search tag.
-    const url = new URLSearchParams(
-        'search=mouse+accel&settingId=' +
-        encodeURIComponent(MOUSE_ACCELERATION_SETTING_ID));
-
-    await Router.getInstance().navigateTo(
-        routes.PER_DEVICE_MOUSE,
-        /* dynamicParams= */ url, /* removeSearch= */ true);
-    await flushTasks();
-
-    assertTrue(!!mouseAccelerationToggle);
-    assertFalse(!!subsection.shadowRoot.activeElement);
-  });
-});
diff --git a/chrome/test/data/webui/settings/chromeos/search_subpage_test.js b/chrome/test/data/webui/settings/chromeos/search_subpage_test.js
deleted file mode 100644
index 6f2b5fd..0000000
--- a/chrome/test/data/webui/settings/chromeos/search_subpage_test.js
+++ /dev/null
@@ -1,286 +0,0 @@
-// Copyright 2021 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import 'chrome://os-settings/chromeos/lazy_load.js';
-
-import {CrSettingsPrefs, Router, routes} from 'chrome://os-settings/chromeos/os_settings.js';
-import {getDeepActiveElement} from 'chrome://resources/ash/common/util.js';
-import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {waitAfterNextRender} from 'chrome://webui-test/polymer_test_util.js';
-
-suite('SearchSubpage', function() {
-  /** @type {SearchSubpageElement} */
-  let page = null;
-
-  suiteSetup(function() {
-    loadTimeData.overrideValues({
-      shouldShowQuickAnswersSettings: true,
-      quickAnswersSubToggleEnabled: true,
-    });
-  });
-
-  setup(function() {
-    PolymerTest.clearBody();
-
-    const prefElement = document.createElement('settings-prefs');
-    document.body.appendChild(prefElement);
-
-    return CrSettingsPrefs.initialized.then(function() {
-      page = document.createElement('settings-search-subpage');
-      page.prefs = prefElement.prefs;
-      document.body.appendChild(page);
-      flush();
-    });
-  });
-
-  teardown(function() {
-    page.remove();
-    CrSettingsPrefs.resetForTesting();
-  });
-
-  test('definitionToggleVisibility', function() {
-    let button =
-        page.shadowRoot.querySelector('#quick-answers-definition-enable');
-    assertFalse(!!button);
-
-    page.setPrefValue('settings.quick_answers.enabled', true);
-    flush();
-
-    button = page.shadowRoot.querySelector('#quick-answers-definition-enable');
-    assertTrue(!!button);
-  });
-
-  test('translationToggleVisibility', function() {
-    let button =
-        page.shadowRoot.querySelector('#quick-answers-translation-enable');
-    assertFalse(!!button);
-
-    page.setPrefValue('settings.quick_answers.enabled', true);
-    flush();
-
-    button = page.shadowRoot.querySelector('#quick-answers-translation-enable');
-    assertTrue(!!button);
-  });
-
-  test('unitConversionToggleVisibility', function() {
-    let button =
-        page.shadowRoot.querySelector('#quick-answers-unit-conversion-enable');
-    assertFalse(!!button);
-
-    page.setPrefValue('settings.quick_answers.enabled', true);
-    flush();
-
-    button =
-        page.shadowRoot.querySelector('#quick-answers-unit-conversion-enable');
-    assertTrue(!!button);
-  });
-
-  test('toggleQuickAnswers', function() {
-    flush();
-    const button = page.shadowRoot.querySelector('#quick-answers-enable');
-    assertTrue(!!button);
-    assertFalse(button.disabled);
-    assertFalse(button.checked);
-
-    let definition_button =
-        page.shadowRoot.querySelector('#quick-answers-definition-enable');
-    let translation_button =
-        page.shadowRoot.querySelector('#quick-answers-translation-enable');
-    let unit_conversion_button =
-        page.shadowRoot.querySelector('#quick-answers-unit-conversion-enable');
-    assertFalse(!!definition_button);
-    assertFalse(!!translation_button);
-    assertFalse(!!unit_conversion_button);
-
-    // Tap the enable toggle button and ensure the state becomes enabled.
-    button.click();
-    flush();
-    assertTrue(button.checked);
-
-    definition_button =
-        page.shadowRoot.querySelector('#quick-answers-definition-enable');
-    translation_button =
-        page.shadowRoot.querySelector('#quick-answers-translation-enable');
-    unit_conversion_button =
-        page.shadowRoot.querySelector('#quick-answers-unit-conversion-enable');
-    assertTrue(!!definition_button);
-    assertTrue(!!translation_button);
-    assertTrue(!!unit_conversion_button);
-  });
-
-  test('toggleQuickAnswersDefinition', function() {
-    let button =
-        page.shadowRoot.querySelector('#quick-answers-definition-enable');
-    assertFalse(!!button);
-    page.setPrefValue('settings.quick_answers.enabled', true);
-    page.setPrefValue('settings.quick_answers.definition.enabled', false);
-    flush();
-
-    button = page.shadowRoot.querySelector('#quick-answers-definition-enable');
-    assertTrue(!!button);
-    assertFalse(button.disabled);
-    assertFalse(button.checked);
-
-    button.click();
-    flush();
-    assertTrue(button.checked);
-    assertTrue(page.getPref('settings.quick_answers.definition.enabled.value'));
-  });
-
-  test('toggleQuickAnswersTranslation', function() {
-    let button =
-        page.shadowRoot.querySelector('#quick-answers-translation-enable');
-    assertFalse(!!button);
-    page.setPrefValue('settings.quick_answers.enabled', true);
-    page.setPrefValue('settings.quick_answers.translation.enabled', false);
-    flush();
-
-    button = page.shadowRoot.querySelector('#quick-answers-translation-enable');
-    assertTrue(!!button);
-    assertFalse(button.disabled);
-    assertFalse(button.checked);
-
-    button.click();
-    flush();
-    assertTrue(button.checked);
-    assertTrue(
-        page.getPref('settings.quick_answers.translation.enabled.value'));
-  });
-
-  test('clickLanguageSettingsLink', function() {
-    let button =
-        page.shadowRoot.querySelector('#quick-answers-translation-enable');
-    assertFalse(!!button);
-    page.setPrefValue('settings.quick_answers.enabled', true);
-    page.setPrefValue('settings.quick_answers.translation.enabled', false);
-    flush();
-
-    button = page.shadowRoot.querySelector('#quick-answers-translation-enable');
-    assertTrue(!!button);
-    assertFalse(button.disabled);
-    assertFalse(button.checked);
-
-    const languageSettingsLink =
-        button.shadowRoot.querySelector('#sub-label-text-with-link')
-            .querySelector('a');
-    assertTrue(!!languageSettingsLink);
-
-    languageSettingsLink.click();
-    flush();
-    assertFalse(button.checked);
-    assertFalse(
-        page.getPref('settings.quick_answers.translation.enabled.value'));
-
-    assertEquals(
-        routes.OS_LANGUAGES_LANGUAGES, Router.getInstance().currentRoute);
-  });
-
-  test('toggleQuickAnswersUnitConversion', function() {
-    let button =
-        page.shadowRoot.querySelector('#quick-answers-unit-conversion-enable');
-    assertFalse(!!button);
-    page.setPrefValue('settings.quick_answers.enabled', true);
-    page.setPrefValue('settings.quick_answers.unit_conversion.enabled', false);
-    flush();
-
-    button =
-        page.shadowRoot.querySelector('#quick-answers-unit-conversion-enable');
-    assertTrue(!!button);
-    assertFalse(button.disabled);
-    assertFalse(button.checked);
-
-    button.click();
-    flush();
-    assertTrue(button.checked);
-    assertTrue(
-        page.getPref('settings.quick_answers.unit_conversion.enabled.value'));
-  });
-
-  test('Deep link to Preferred Search Engine', async () => {
-    const params = new URLSearchParams();
-    params.append('settingId', '600');
-    Router.getInstance().navigateTo(routes.SEARCH_SUBPAGE, params);
-
-    const browserSearchSettingsLink =
-        page.shadowRoot.querySelector('settings-search-engine')
-            .shadowRoot.querySelector('#browserSearchSettingsLink');
-    const deepLinkElement =
-        browserSearchSettingsLink.shadowRoot.querySelector('cr-icon-button');
-    assertTrue(!!deepLinkElement);
-    await waitAfterNextRender(deepLinkElement);
-    assertEquals(
-        deepLinkElement, getDeepActiveElement(),
-        'Preferred Search Engine button should be focused for settingId=600.');
-  });
-
-  test('Deep link to Quick Answers On/Off', async () => {
-    const params = new URLSearchParams();
-    params.append('settingId', '608');
-    Router.getInstance().navigateTo(routes.SEARCH_SUBPAGE, params);
-
-    const deepLinkElement =
-        page.shadowRoot.querySelector('#quick-answers-enable')
-            .shadowRoot.querySelector('cr-toggle');
-    assertTrue(!!deepLinkElement);
-    await waitAfterNextRender(deepLinkElement);
-    assertEquals(
-        deepLinkElement, getDeepActiveElement(),
-        'Quick Answer On/Off toggle should be focused for settingId=608.');
-  });
-
-  test('Deep link to Quick Answers Definition', async () => {
-    page.setPrefValue('settings.quick_answers.enabled', true);
-    flush();
-
-    const params = new URLSearchParams();
-    params.append('settingId', '609');
-    Router.getInstance().navigateTo(routes.SEARCH_SUBPAGE, params);
-
-    const deepLinkElement =
-        page.shadowRoot.querySelector('#quick-answers-definition-enable')
-            .shadowRoot.querySelector('cr-toggle');
-    assertTrue(!!deepLinkElement);
-    await waitAfterNextRender(deepLinkElement);
-    assertEquals(
-        deepLinkElement, getDeepActiveElement(),
-        'Quick Answer definition toggle should be focused for settingId=609.');
-  });
-
-  test('Deep link to Quick Answers Translation', async () => {
-    page.setPrefValue('settings.quick_answers.enabled', true);
-    flush();
-
-    const params = new URLSearchParams();
-    params.append('settingId', '610');
-    Router.getInstance().navigateTo(routes.SEARCH_SUBPAGE, params);
-
-    const deepLinkElement =
-        page.shadowRoot.querySelector('#quick-answers-translation-enable')
-            .shadowRoot.querySelector('cr-toggle');
-    assertTrue(!!deepLinkElement);
-    await waitAfterNextRender(deepLinkElement);
-    assertEquals(
-        deepLinkElement, getDeepActiveElement(),
-        'Quick Answer translation toggle should be focused for settingId=610.');
-  });
-
-  test('Deep link to Quick Answers Unit Conversion', async () => {
-    page.setPrefValue('settings.quick_answers.enabled', true);
-    flush();
-
-    const params = new URLSearchParams();
-    params.append('settingId', '611');
-    Router.getInstance().navigateTo(routes.SEARCH_SUBPAGE, params);
-
-    const deepLinkElement =
-        page.shadowRoot.querySelector('#quick-answers-unit-conversion-enable')
-            .shadowRoot.querySelector('cr-toggle');
-    assertTrue(!!deepLinkElement);
-    await waitAfterNextRender(deepLinkElement);
-    assertEquals(
-        deepLinkElement, getDeepActiveElement(),
-        'Quick Answer unit conversion toggle should be focused for settingId=611.');
-  });
-});
diff --git a/chromeos/ash/components/drivefs/drivefs_pin_manager.cc b/chromeos/ash/components/drivefs/drivefs_pin_manager.cc
index fd05563e..8f93014 100644
--- a/chromeos/ash/components/drivefs/drivefs_pin_manager.cc
+++ b/chromeos/ash/components/drivefs/drivefs_pin_manager.cc
@@ -912,9 +912,12 @@
   using State = mojom::ItemEvent::State;
   switch (event.state) {
     case State::kQueued:
-      // kQueued events come with a bytes_to_transfer field incorrectly set to
-      // zero (b/266462624). So we set it to -1 to ignore it.
-      event.bytes_to_transfer = -1;
+      // (TODO b/266462624) kQueued events come with a bytes_to_transfer field
+      // incorrectly set to zero. So we set it to -1 to ignore it.
+      if (event.bytes_to_transfer == 0) {
+        VLOG(3) << "Zero bytes_to_transfer in " << Quote(event);
+        event.bytes_to_transfer = -1;
+      }
       [[fallthrough]];
 
     case State::kInProgress:
diff --git a/chromeos/ash/services/bluetooth_config/device_pairing_handler_impl_unittest.cc b/chromeos/ash/services/bluetooth_config/device_pairing_handler_impl_unittest.cc
index 348380e..46c376db 100644
--- a/chromeos/ash/services/bluetooth_config/device_pairing_handler_impl_unittest.cc
+++ b/chromeos/ash/services/bluetooth_config/device_pairing_handler_impl_unittest.cc
@@ -94,12 +94,16 @@
                                        true, success_count);
   }
 
-  void CheckPairingFailureReasonHistogram(
+  void CheckPairingFailureHistogram(
       device::ConnectionFailureReason failure_reason,
-      int count) {
+      size_t failure_count,
+      size_t filtered_count) {
     histogram_tester.ExpectBucketCount(
         "Bluetooth.ChromeOS.Pairing.Result.FailureReason", failure_reason,
-        count);
+        failure_count);
+    histogram_tester.ExpectBucketCount(
+        "Bluetooth.ChromeOS.Pairing.Result.FilteredFailureReason",
+        failure_reason, filtered_count);
   }
 
   void CheckDurationHistogramMetrics(base::TimeDelta bucket,
@@ -368,8 +372,8 @@
   CheckPairingHistograms(device::BluetoothTransportType::kClassic,
                          /*type_count=*/1, /*failure_count=*/1,
                          /*success_count=*/0);
-  CheckPairingFailureReasonHistogram(device::ConnectionFailureReason::kFailed,
-                                     /*count=*/1);
+  CheckPairingFailureHistogram(device::ConnectionFailureReason::kFailed,
+                               /*failure_count=*/1, /*filtered_count=*/1);
   CheckDurationHistogramMetrics(GetPairingFailureDelay(), /*success_count=*/0,
                                 /*failure_count=*/1,
                                 /*transport_name=*/"Classic");
@@ -384,8 +388,8 @@
   CheckPairingHistograms(device::BluetoothTransportType::kClassic,
                          /*type_count=*/2, /*failure_count=*/1,
                          /*success_count=*/1);
-  CheckPairingFailureReasonHistogram(device::ConnectionFailureReason::kFailed,
-                                     /*count=*/1);
+  CheckPairingFailureHistogram(device::ConnectionFailureReason::kFailed,
+                               /*failure_count=*/1, /*filtered_count=*/1);
   CheckDurationHistogramMetrics(kTestDuration, /*success_count=*/1,
                                 /*failure_count=*/0,
                                 /*transport_name=*/"Classic");
@@ -409,8 +413,8 @@
   CheckPairingHistograms(device::BluetoothTransportType::kInvalid,
                          /*type_count=*/1, /*failure_count=*/1,
                          /*success_count=*/0);
-  CheckPairingFailureReasonHistogram(device::ConnectionFailureReason::kFailed,
-                                     /*count=*/1);
+  CheckPairingFailureHistogram(device::ConnectionFailureReason::kFailed,
+                               /*failure_count=*/1, /*filtered_count=*/1);
   CheckDurationHistogramMetrics(base::Milliseconds(0), /*success_count=*/0,
                                 /*failure_count=*/1,
                                 /*transport_name=*/"Invalid");
@@ -434,8 +438,8 @@
   CheckPairingHistograms(device::BluetoothTransportType::kClassic,
                          /*type_count=*/1, /*failure_count=*/1,
                          /*success_count=*/0);
-  CheckPairingFailureReasonHistogram(
-      device::ConnectionFailureReason::kAuthCanceled, /*count=*/1);
+  CheckPairingFailureHistogram(device::ConnectionFailureReason::kAuthCanceled,
+                               /*failure_count=*/1, /*filtered_count=*/0);
   CheckDurationHistogramMetrics(kTestDuration, /*success_count=*/0,
                                 /*failure_count=*/1,
                                 /*transport_name=*/"Classic");
@@ -454,8 +458,8 @@
   CheckPairingHistograms(device::BluetoothTransportType::kClassic,
                          /*type_count=*/1, /*failure_count=*/1,
                          /*success_count=*/0);
-  CheckPairingFailureReasonHistogram(
-      device::ConnectionFailureReason::kAuthCanceled, /*count=*/1);
+  CheckPairingFailureHistogram(device::ConnectionFailureReason::kAuthCanceled,
+                               /*failure_count=*/1, /*filtered_count=*/0);
   CheckDurationHistogramMetrics(base::Milliseconds(0), /*success_count=*/0,
                                 /*failure_count=*/1,
                                 /*transport_name=*/"Classic");
@@ -512,8 +516,8 @@
   CheckPairingHistograms(device::BluetoothTransportType::kClassic,
                          /*type_count=*/1, /*failure_count=*/1,
                          /*success_count=*/0);
-  CheckPairingFailureReasonHistogram(
-      device::ConnectionFailureReason::kAuthCanceled, /*count=*/1);
+  CheckPairingFailureHistogram(device::ConnectionFailureReason::kAuthCanceled,
+                               /*failure_count=*/1, /*filtered_count=*/0);
   CheckDurationHistogramMetrics(kTestDuration, /*success_count=*/0,
                                 /*failure_count=*/1,
                                 /*transport_name=*/"Classic");
@@ -530,10 +534,10 @@
   CheckPairingHistograms(device::BluetoothTransportType::kClassic,
                          /*type_count=*/2, /*failure_count=*/2,
                          /*success_count=*/0);
-  CheckPairingFailureReasonHistogram(
-      device::ConnectionFailureReason::kAuthCanceled, /*count=*/1);
-  CheckPairingFailureReasonHistogram(
-      device::ConnectionFailureReason::kAuthFailed, /*count=*/1);
+  CheckPairingFailureHistogram(device::ConnectionFailureReason::kAuthCanceled,
+                               /*failure_count=*/1, /*filtered_count=*/0);
+  CheckPairingFailureHistogram(device::ConnectionFailureReason::kAuthFailed,
+                               /*failure_count=*/1, /*filtered_count=*/1);
   CheckDurationHistogramMetrics(kTestDuration, /*success_count=*/0,
                                 /*failure_count=*/1,
                                 /*transport_name=*/"Classic");
@@ -561,8 +565,8 @@
   CheckPairingHistograms(device::BluetoothTransportType::kInvalid,
                          /*type_count=*/1, /*failure_count=*/1,
                          /*success_count=*/0);
-  CheckPairingFailureReasonHistogram(
-      device::ConnectionFailureReason::kAuthFailed, /*count=*/1);
+  CheckPairingFailureHistogram(device::ConnectionFailureReason::kAuthFailed,
+                               /*failure_count=*/1, /*filtered_count=*/1);
   CheckDurationHistogramMetrics(kTestDuration, /*success_count=*/0,
                                 /*failure_count=*/1,
                                 /*transport_name=*/"Invalid");
@@ -586,8 +590,8 @@
   CheckPairingHistograms(device::BluetoothTransportType::kClassic,
                          /*type_count=*/1, /*failure_count=*/1,
                          /*success_count=*/0);
-  CheckPairingFailureReasonHistogram(device::ConnectionFailureReason::kFailed,
-                                     /*count=*/1);
+  CheckPairingFailureHistogram(device::ConnectionFailureReason::kFailed,
+                               /*failure_count=*/1, /*filtered_count=*/1);
   CheckDurationHistogramMetrics(GetPairingFailureDelay(), /*success_count=*/0,
                                 /*failure_count=*/1,
                                 /*transport_name=*/"Classic");
@@ -601,8 +605,8 @@
   CheckPairingHistograms(device::BluetoothTransportType::kInvalid,
                          /*type_count=*/1, /*failure_count=*/1,
                          /*success_count=*/0);
-  CheckPairingFailureReasonHistogram(device::ConnectionFailureReason::kFailed,
-                                     /*count=*/1);
+  CheckPairingFailureHistogram(device::ConnectionFailureReason::kFailed,
+                               /*failure_count=*/1, /*filtered_count=*/1);
   CheckDurationHistogramMetrics(base::Milliseconds(0), /*success_count=*/0,
                                 /*failure_count=*/1,
                                 /*transport_name=*/"Invalid");
@@ -659,8 +663,8 @@
   CheckPairingHistograms(device::BluetoothTransportType::kClassic,
                          /*type_count=*/1, /*failure_count=*/1,
                          /*success_count=*/0);
-  CheckPairingFailureReasonHistogram(
-      device::ConnectionFailureReason::kAuthRejected, /*count=*/1);
+  CheckPairingFailureHistogram(device::ConnectionFailureReason::kAuthRejected,
+                               /*failure_count=*/1, /*filtered_count=*/0);
   CheckDurationHistogramMetrics(GetPairingFailureDelay(), /*success_count=*/0,
                                 /*failure_count=*/1,
                                 /*transport_name=*/"Classic");
@@ -684,8 +688,8 @@
   CheckPairingHistograms(device::BluetoothTransportType::kInvalid,
                          /*type_count=*/1, /*failure_count=*/1,
                          /*success_count=*/0);
-  CheckPairingFailureReasonHistogram(device::ConnectionFailureReason::kFailed,
-                                     /*count=*/1);
+  CheckPairingFailureHistogram(device::ConnectionFailureReason::kFailed,
+                               /*failure_count=*/1, /*filtered_count=*/1);
   CheckDurationHistogramMetrics(kTestDuration, /*success_count=*/0,
                                 /*failure_count=*/1,
                                 /*transport_name=*/"Invalid");
@@ -706,8 +710,8 @@
   CheckPairingHistograms(device::BluetoothTransportType::kClassic,
                          /*type_count=*/1, /*failure_count=*/1,
                          /*success_count=*/0);
-  CheckPairingFailureReasonHistogram(
-      device::ConnectionFailureReason::kInprogress, /*count=*/1);
+  CheckPairingFailureHistogram(device::ConnectionFailureReason::kInprogress,
+                               /*failure_count=*/1, /*filtered_count=*/1);
   CheckDurationHistogramMetrics(GetPairingFailureDelay(), /*success_count=*/0,
                                 /*failure_count=*/1,
                                 /*transport_name=*/"Classic");
@@ -732,8 +736,8 @@
   CheckPairingHistograms(device::BluetoothTransportType::kInvalid,
                          /*type_count=*/1, /*failure_count=*/1,
                          /*success_count=*/0);
-  CheckPairingFailureReasonHistogram(device::ConnectionFailureReason::kFailed,
-                                     /*count=*/1);
+  CheckPairingFailureHistogram(device::ConnectionFailureReason::kFailed,
+                               /*failure_count=*/1, /*filtered_count=*/1);
   CheckDurationHistogramMetrics(kTestDuration, /*success_count=*/0,
                                 /*failure_count=*/1,
                                 /*transport_name=*/"Invalid");
@@ -756,8 +760,8 @@
   CheckPairingHistograms(device::BluetoothTransportType::kClassic,
                          /*type_count=*/1, /*failure_count=*/1,
                          /*success_count=*/0);
-  CheckPairingFailureReasonHistogram(
-      device::ConnectionFailureReason::kAuthCanceled, /*count=*/1);
+  CheckPairingFailureHistogram(device::ConnectionFailureReason::kAuthCanceled,
+                               /*failure_count=*/1, /*filtered_count=*/0);
   CheckDurationHistogramMetrics(kTestDuration, /*success_count=*/0,
                                 /*failure_count=*/1,
                                 /*transport_name=*/"Classic");
@@ -770,8 +774,8 @@
   CheckPairingHistograms(device::BluetoothTransportType::kClassic,
                          /*type_count=*/1, /*failure_count=*/1,
                          /*success_count=*/0);
-  CheckPairingFailureReasonHistogram(
-      device::ConnectionFailureReason::kAuthCanceled, /*count=*/1);
+  CheckPairingFailureHistogram(device::ConnectionFailureReason::kAuthCanceled,
+                               /*failure_count=*/1, /*filtered_count=*/0);
   CheckDurationHistogramMetrics(kTestDuration, /*success_count=*/0,
                                 /*failure_count=*/1,
                                 /*transport_name=*/"Classic");
@@ -803,8 +807,8 @@
   CheckPairingHistograms(device::BluetoothTransportType::kClassic,
                          /*type_count=*/1, /*failure_count=*/1,
                          /*success_count=*/0);
-  CheckPairingFailureReasonHistogram(
-      device::ConnectionFailureReason::kAuthFailed, /*count=*/1);
+  CheckPairingFailureHistogram(device::ConnectionFailureReason::kAuthFailed,
+                               /*failure_count=*/1, /*filtered_count=*/1);
   CheckDurationHistogramMetrics(GetPairingFailureDelay(), /*success_count=*/0,
                                 /*failure_count=*/1,
                                 /*transport_name=*/"Classic");
@@ -857,8 +861,8 @@
   CheckPairingHistograms(device::BluetoothTransportType::kClassic,
                          /*type_count=*/1, /*failure_count=*/1,
                          /*success_count=*/0);
-  CheckPairingFailureReasonHistogram(
-      device::ConnectionFailureReason::kAuthFailed, /*count=*/1);
+  CheckPairingFailureHistogram(device::ConnectionFailureReason::kAuthFailed,
+                               /*failure_count=*/1, /*filtered_count=*/1);
   CheckDurationHistogramMetrics(GetPairingFailureDelay(), /*success_count=*/0,
                                 /*failure_count=*/1,
                                 /*transport_name=*/"Classic");
@@ -880,8 +884,8 @@
   CheckPairingHistograms(device::BluetoothTransportType::kClassic,
                          /*type_count=*/1, /*failure_count=*/1,
                          /*success_count=*/0);
-  CheckPairingFailureReasonHistogram(
-      device::ConnectionFailureReason::kAuthFailed, /*count=*/1);
+  CheckPairingFailureHistogram(device::ConnectionFailureReason::kAuthFailed,
+                               /*failure_count=*/1, /*filtered_count=*/1);
   CheckDurationHistogramMetrics(GetPairingFailureDelay(), /*success_count=*/0,
                                 /*failure_count=*/1,
                                 /*transport_name=*/"Classic");
@@ -900,8 +904,8 @@
   CheckPairingHistograms(device::BluetoothTransportType::kClassic,
                          /*type_count=*/1, /*failure_count=*/1,
                          /*success_count=*/0);
-  CheckPairingFailureReasonHistogram(
-      device::ConnectionFailureReason::kAuthFailed, /*count=*/1);
+  CheckPairingFailureHistogram(device::ConnectionFailureReason::kAuthFailed,
+                               /*failure_count=*/1, /*filtered_count=*/1);
   CheckDurationHistogramMetrics(GetPairingFailureDelay(), /*success_count=*/0,
                                 /*failure_count=*/1,
                                 /*transport_name=*/"Classic");
@@ -926,8 +930,8 @@
   CheckPairingHistograms(device::BluetoothTransportType::kClassic,
                          /*type_count=*/1, /*failure_count=*/1,
                          /*success_count=*/0);
-  CheckPairingFailureReasonHistogram(
-      device::ConnectionFailureReason::kAuthFailed, /*count=*/1);
+  CheckPairingFailureHistogram(device::ConnectionFailureReason::kAuthFailed,
+                               /*failure_count=*/1, /*filtered_count=*/1);
   CheckDurationHistogramMetrics(GetPairingFailureDelay(), /*success_count=*/0,
                                 /*failure_count=*/1,
                                 /*transport_name=*/"Classic");
@@ -954,10 +958,10 @@
   CheckPairingHistograms(device::BluetoothTransportType::kClassic,
                          /*type_count=*/2, /*failure_count=*/2,
                          /*success_count=*/0);
-  CheckPairingFailureReasonHistogram(
-      device::ConnectionFailureReason::kAuthFailed, /*count=*/1);
-  CheckPairingFailureReasonHistogram(
-      device::ConnectionFailureReason::kAuthCanceled, /*count=*/1);
+  CheckPairingFailureHistogram(device::ConnectionFailureReason::kAuthFailed,
+                               /*failure_count=*/1, /*filtered_count=*/1);
+  CheckPairingFailureHistogram(device::ConnectionFailureReason::kAuthCanceled,
+                               /*failure_count=*/1, /*filtered_count=*/0);
   CheckDurationHistogramMetrics(GetPairingFailureDelay(), /*success_count=*/0,
                                 /*failure_count=*/2,
                                 /*transport_name=*/"Classic");
@@ -984,8 +988,8 @@
   CheckPairingHistograms(device::BluetoothTransportType::kInvalid,
                          /*type_count=*/1, /*failure_count=*/1,
                          /*success_count=*/0);
-  CheckPairingFailureReasonHistogram(device::ConnectionFailureReason::kFailed,
-                                     /*count=*/1);
+  CheckPairingFailureHistogram(device::ConnectionFailureReason::kFailed,
+                               /*failure_count=*/1, /*filtered_count=*/1);
   CheckDurationHistogramMetrics(kTestDuration, /*success_count=*/0,
                                 /*failure_count=*/1,
                                 /*transport_name=*/"Invalid");
@@ -1008,8 +1012,8 @@
   CheckPairingHistograms(device::BluetoothTransportType::kClassic,
                          /*type_count=*/1, /*failure_count=*/1,
                          /*success_count=*/0);
-  CheckPairingFailureReasonHistogram(
-      device::ConnectionFailureReason::kAuthFailed, /*count=*/1);
+  CheckPairingFailureHistogram(device::ConnectionFailureReason::kAuthFailed,
+                               /*failure_count=*/1, /*filtered_count=*/1);
   CheckDurationHistogramMetrics(GetPairingFailureDelay(), /*success_count=*/0,
                                 /*failure_count=*/1,
                                 /*transport_name=*/"Classic");
diff --git a/chromeos/chromeos_strings.grd b/chromeos/chromeos_strings.grd
index f24ef42..2da53294 100644
--- a/chromeos/chromeos_strings.grd
+++ b/chromeos/chromeos_strings.grd
@@ -301,6 +301,11 @@
         Report this query
       </message>
 
+      <!-- Rich Answers -->
+      <message name="IDS_RICH_ANSWERS_VIEW_A11Y_NAME_TEXT" desc="A11y name text for the Rich Answers view.">
+        Additional info related to your selection
+      </message>
+
       <!-- Multitask Menu -->
       <message name="IDS_MULTITASK_MENU_HALF_BUTTON_NAME" desc="Title of the half button on the multitask menu.">
         Half
diff --git a/chromeos/chromeos_strings_grd/IDS_RICH_ANSWERS_VIEW_A11Y_NAME_TEXT.png.sha1 b/chromeos/chromeos_strings_grd/IDS_RICH_ANSWERS_VIEW_A11Y_NAME_TEXT.png.sha1
new file mode 100644
index 0000000..46f3913
--- /dev/null
+++ b/chromeos/chromeos_strings_grd/IDS_RICH_ANSWERS_VIEW_A11Y_NAME_TEXT.png.sha1
@@ -0,0 +1 @@
+9cb8d4b5c369e3b5be33f51039fd837d12ab708e
\ No newline at end of file
diff --git a/components/autofill/core/browser/metrics/form_events/credit_card_form_event_logger.cc b/components/autofill/core/browser/metrics/form_events/credit_card_form_event_logger.cc
index e13fafa9..2346210 100644
--- a/components/autofill/core/browser/metrics/form_events/credit_card_form_event_logger.cc
+++ b/components/autofill/core/browser/metrics/form_events/credit_card_form_event_logger.cc
@@ -67,10 +67,11 @@
 
   suggestion_shown_timestamp_ = AutofillTickClock::NowTicks();
 
-  // Log if metadata is shown for any of the suggestions.
-  if (metadata_logging_context_.IsCardMetadataShown()) {
-    Log(FORM_EVENT_CARD_SUGGESTION_WITH_METADATA_SHOWN, form);
-  }
+  // Log if any of the suggestions had metadata.
+  Log(metadata_logging_context_.card_metadata_available
+          ? FORM_EVENT_CARD_SUGGESTION_WITH_METADATA_SHOWN
+          : FORM_EVENT_CARD_SUGGESTION_WITHOUT_METADATA_SHOWN,
+      form);
 }
 
 void CreditCardFormEventLogger::OnDidSelectCardSuggestion(
@@ -114,12 +115,13 @@
       AutofillTickClock::NowTicks() - suggestion_shown_timestamp_,
       metadata_logging_context_, credit_card);
 
-  // Log if the selected suggestion had metadata shown.
+  // Log if the selected suggestion had metadata.
   metadata_logging_context_ =
       autofill_metrics::GetMetadataLoggingContext({credit_card});
-  if (metadata_logging_context_.IsCardMetadataShown()) {
-    Log(FORM_EVENT_CARD_SUGGESTION_WITH_METADATA_SELECTED, form);
-  }
+  Log(metadata_logging_context_.card_metadata_available
+          ? FORM_EVENT_CARD_SUGGESTION_WITH_METADATA_SELECTED
+          : FORM_EVENT_CARD_SUGGESTION_WITHOUT_METADATA_SELECTED,
+      form);
 }
 
 void CreditCardFormEventLogger::OnDidFillSuggestion(
diff --git a/components/autofill/core/browser/metrics/form_events/form_events.h b/components/autofill/core/browser/metrics/form_events/form_events.h
index 013d1452..f213e08 100644
--- a/components/autofill/core/browser/metrics/form_events/form_events.h
+++ b/components/autofill/core/browser/metrics/form_events/form_events.h
@@ -143,11 +143,15 @@
   // after being autofilled. Recorded once per form.
   FORM_EVENT_AUTOFILLED_FIELD_CLEARED_BY_JAVASCRIPT_AFTER_FILL_ONCE = 57,
 
-  // Suggestions were shown, and they included a credit card that had metadata
-  // shown.
+  // Credit card suggestions were shown, and it included at least one suggestion
+  // with metadata.
   FORM_EVENT_CARD_SUGGESTION_WITH_METADATA_SHOWN = 58,
-  // The selected card suggestion had metadata shown.
-  FORM_EVENT_CARD_SUGGESTION_WITH_METADATA_SELECTED = 59,
+  // Credit card suggestions were shown, and none had metadata.
+  FORM_EVENT_CARD_SUGGESTION_WITHOUT_METADATA_SHOWN = 59,
+  // The selected credit card suggestion had metadata.
+  FORM_EVENT_CARD_SUGGESTION_WITH_METADATA_SELECTED = 60,
+  // The selected credit card suggestion did not have metadata.
+  FORM_EVENT_CARD_SUGGESTION_WITHOUT_METADATA_SELECTED = 61,
 
   NUM_FORM_EVENTS,
 };
diff --git a/components/autofill/core/browser/metrics/payments/card_metadata_metrics_unittest.cc b/components/autofill/core/browser/metrics/payments/card_metadata_metrics_unittest.cc
index 01799a9..0e466c8 100644
--- a/components/autofill/core/browser/metrics/payments/card_metadata_metrics_unittest.cc
+++ b/components/autofill/core/browser/metrics/payments/card_metadata_metrics_unittest.cc
@@ -16,43 +16,20 @@
 
 namespace {
 
-constexpr char kCardWithoutMetadataId[] =
-    "10000000-0000-0000-0000-000000000001";
-constexpr char kCardWithMetadataId[] = "10000000-0000-0000-0000-000000000002";
+constexpr char kCardGuid[] = "10000000-0000-0000-0000-000000000001";
 
 }  // namespace
 
-// Params:
-// 1) Whether card product name feature flag is enabled.
-// 2) whether card art image feature flag is enabled.
-// 3) Whether card metadata (both product name and card art image) are provided.
-// 4) Whether the card has linked virtual card (only card art is provided).
-class CardMetadataMetricsTest
+// Param: Whether card metadata is available.
+class CardMetadataFormEventMetricsTest
     : public AutofillMetricsBaseTest,
       public testing::Test,
-      public testing::WithParamInterface<std::tuple<bool, bool, bool, bool>> {
+      public testing::WithParamInterface<bool> {
  public:
-  CardMetadataMetricsTest()
-      : card_product_name_enabled_(std::get<0>(GetParam())),
-        card_art_image_enabled_(std::get<1>(GetParam())),
-        card_metadata_available_(std::get<2>(GetParam())),
-        card_has_linked_virtual_card_(std::get<3>(GetParam())) {
-    feature_list_card_product_name_.InitWithFeatureState(
-        features::kAutofillEnableCardProductName, card_product_name_enabled_);
-    feature_list_card_art_image_.InitWithFeatureState(
-        features::kAutofillEnableCardArtImage, card_art_image_enabled_);
-  }
-  ~CardMetadataMetricsTest() override = default;
+  CardMetadataFormEventMetricsTest() : card_metadata_available_(GetParam()) {}
+  ~CardMetadataFormEventMetricsTest() override = default;
 
-  bool card_product_name_enabled() { return card_product_name_enabled_; }
-  bool card_art_image_enabled() { return card_art_image_enabled_; }
   bool card_metadata_available() { return card_metadata_available_; }
-  bool card_has_linked_virtual_card() { return card_has_linked_virtual_card_; }
-  bool card_metadata_shown() {
-    return (card_metadata_available_ &&
-            (card_product_name_enabled_ || card_art_image_enabled_)) ||
-           card_has_linked_virtual_card_;
-  }
 
   FormData form() { return form_; }
 
@@ -68,29 +45,134 @@
                                       {.role = CREDIT_CARD_EXP_2_DIGIT_YEAR}},
                            .action = ""});
 
-    // Add 2 masked server cards.
-    CreditCard card_without_metadata =
-        test::GetRandomCreditCard(CreditCard::MASKED_SERVER_CARD);
-    card_without_metadata.set_guid(kCardWithoutMetadataId);
-    CreditCard card_with_metadata =
-        test::GetRandomCreditCard(CreditCard::MASKED_SERVER_CARD);
-    card_with_metadata.set_guid(kCardWithMetadataId);
-    card_with_metadata.set_issuer_id("capitalone");
-    // Set card_with_metadata as the virtual card.
-    if (card_has_linked_virtual_card()) {
-      card_with_metadata.set_virtual_card_enrollment_state(
-          CreditCard::VirtualCardEnrollmentState::ENROLLED);
-      card_with_metadata.set_card_art_url(
-          GURL("https://www.example.com/cardart.png"));
-    }
-    // Set metadata to card_with_metadata.
+    // Add a masked server card.
+    CreditCard card = test::GetRandomCreditCard(CreditCard::MASKED_SERVER_CARD);
+    card.set_guid(kCardGuid);
+    // Set metadata to card.
     if (card_metadata_available()) {
-      card_with_metadata.set_product_description(u"card_description");
-      card_with_metadata.set_card_art_url(
+      card.set_product_description(u"card_description");
+      card.set_card_art_url(GURL("https://www.example.com/cardart.png"));
+    }
+    personal_data().AddServerCreditCard(card);
+    personal_data().Refresh();
+  }
+
+  void TearDown() override { TearDownHelper(); }
+
+ private:
+  const bool card_metadata_available_;
+  FormData form_;
+};
+
+INSTANTIATE_TEST_SUITE_P(All,
+                         CardMetadataFormEventMetricsTest,
+                         testing::Bool());
+
+// Test metadata shown metrics are correctly logged.
+TEST_P(CardMetadataFormEventMetricsTest, LogShownMetrics) {
+  base::HistogramTester histogram_tester;
+
+  // Simulate activating the autofill popup for the credit card field.
+  autofill_manager().OnAskForValuesToFillTest(form(), form().fields.back());
+  autofill_manager().DidShowSuggestions(/*has_autofill_suggestions=*/true,
+                                        form(), form().fields.back());
+
+  // Verify that:
+  // 1. if the suggestion shown had metadata,
+  // `FORM_EVENT_CARD_SUGGESTION_WITH_METADATA_SHOWN` is logged.
+  // 2. if the suggestion shown did not have metadata,
+  // `FORM_EVENT_CARD_SUGGESTION_WITHOUT_METADATA_SHOWN` is logged.
+  EXPECT_THAT(
+      histogram_tester.GetAllSamples("Autofill.FormEvents.CreditCard"),
+      BucketsInclude(Bucket(FORM_EVENT_SUGGESTIONS_SHOWN, 1),
+                     Bucket(FORM_EVENT_CARD_SUGGESTION_WITH_METADATA_SHOWN,
+                            card_metadata_available()),
+                     Bucket(FORM_EVENT_CARD_SUGGESTION_WITHOUT_METADATA_SHOWN,
+                            !card_metadata_available())));
+}
+
+// Test metadata selected metrics are correctly logged.
+TEST_P(CardMetadataFormEventMetricsTest, LogSelectedMetrics) {
+  base::HistogramTester histogram_tester;
+
+  // Simulate selecting the card.
+  autofill_manager().OnAskForValuesToFillTest(form(), form().fields.back());
+  autofill_manager().DidShowSuggestions(/*has_autofill_suggestions=*/true,
+                                        form(), form().fields.back());
+  autofill_manager().FillOrPreviewForm(
+      mojom::RendererFormDataAction::kFill, form(), form().fields.back(),
+      MakeFrontendId({.credit_card_id = kCardGuid}));
+
+  // Verify that:
+  // 1. if the selected card had metadata,
+  // `FORM_EVENT_CARD_SUGGESTION_WITH_METADATA_SELECTED` is logged.
+  // 2. if the selected card did not have metadata,
+  // `FORM_EVENT_CARD_SUGGESTION_WITHOUT_METADATA_SELECTED` is logged.
+  EXPECT_THAT(histogram_tester.GetAllSamples("Autofill.FormEvents.CreditCard"),
+              BucketsInclude(
+                  Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SELECTED, 1),
+                  Bucket(FORM_EVENT_CARD_SUGGESTION_WITH_METADATA_SELECTED,
+                         card_metadata_available()),
+                  Bucket(FORM_EVENT_CARD_SUGGESTION_WITHOUT_METADATA_SELECTED,
+                         !card_metadata_available())));
+}
+
+// Params:
+// 1) Whether card product name feature flag is enabled.
+// 2) whether card art image feature flag is enabled.
+// 3) Whether card metadata (both product name and card art image) are provided.
+// 4) Whether the card has linked virtual card (only card art is provided).
+class CardMetadataLatencyMetricsTest
+    : public AutofillMetricsBaseTest,
+      public testing::Test,
+      public testing::WithParamInterface<std::tuple<bool, bool, bool, bool>> {
+ public:
+  CardMetadataLatencyMetricsTest()
+      : card_product_name_enabled_(std::get<0>(GetParam())),
+        card_art_image_enabled_(std::get<1>(GetParam())),
+        card_metadata_available_(std::get<2>(GetParam())),
+        card_has_linked_virtual_card_(std::get<3>(GetParam())) {
+    feature_list_card_product_name_.InitWithFeatureState(
+        features::kAutofillEnableCardProductName, card_product_name_enabled_);
+    feature_list_card_art_image_.InitWithFeatureState(
+        features::kAutofillEnableCardArtImage, card_art_image_enabled_);
+  }
+  ~CardMetadataLatencyMetricsTest() override = default;
+
+  bool card_product_name_enabled() { return card_product_name_enabled_; }
+  bool card_art_image_enabled() { return card_art_image_enabled_; }
+  bool card_metadata_available() { return card_metadata_available_; }
+  bool card_has_linked_virtual_card() { return card_has_linked_virtual_card_; }
+
+  FormData form() { return form_; }
+
+  void SetUp() override {
+    SetUpHelper();
+    // Set up the form data. Reset form action to skip the IsFormMixedContent
+    // check.
+    form_ =
+        GetAndAddSeenForm({.description_for_logging = "CardMetadata",
+                           .fields = {{.role = CREDIT_CARD_NAME_FULL},
+                                      {.role = CREDIT_CARD_NUMBER},
+                                      {.role = CREDIT_CARD_EXP_MONTH},
+                                      {.role = CREDIT_CARD_EXP_2_DIGIT_YEAR}},
+                           .action = ""});
+
+    CreditCard masked_server_card = test::GetMaskedServerCard();
+    masked_server_card.set_guid(kTestMaskedCardId);
+    masked_server_card.set_issuer_id("capitalone");
+    if (card_has_linked_virtual_card()) {
+      masked_server_card.set_virtual_card_enrollment_state(
+          CreditCard::VirtualCardEnrollmentState::ENROLLED);
+      masked_server_card.set_card_art_url(
           GURL("https://www.example.com/cardart.png"));
     }
-    personal_data().AddServerCreditCard(card_without_metadata);
-    personal_data().AddServerCreditCard(card_with_metadata);
+    if (card_metadata_available()) {
+      masked_server_card.set_product_description(u"card_description");
+      masked_server_card.set_card_art_url(
+          GURL("https://www.example.com/cardart.png"));
+    }
+    personal_data().AddServerCreditCard(masked_server_card);
     personal_data().Refresh();
   }
 
@@ -107,94 +189,15 @@
 };
 
 INSTANTIATE_TEST_SUITE_P(All,
-                         CardMetadataMetricsTest,
+                         CardMetadataLatencyMetricsTest,
                          testing::Combine(testing::Bool(),
                                           testing::Bool(),
                                           testing::Bool(),
                                           testing::Bool()));
 
-// Test to ensure that the FORM_EVENT_CARD_SUGGESTION_WITH_METADATA_SHOWN is
-// correctly logged when any card in the suggestion has metadata.
-TEST_P(CardMetadataMetricsTest, LogCardMetadataShownMetrics) {
-  base::HistogramTester histogram_tester;
-
-  // Simulate activating the autofill popup for the credit card field.
-  autofill_manager().OnAskForValuesToFillTest(form(), form().fields.back());
-  autofill_manager().DidShowSuggestions(/*has_autofill_suggestions=*/true,
-                                        form(), form().fields.back());
-
-  // Verify that if metadata is shown for any of the cards, it is logged.
-  EXPECT_THAT(
-      histogram_tester.GetAllSamples("Autofill.FormEvents.CreditCard"),
-      BucketsInclude(Bucket(FORM_EVENT_SUGGESTIONS_SHOWN, 1),
-                     Bucket(FORM_EVENT_CARD_SUGGESTION_WITH_METADATA_SHOWN,
-                            card_metadata_shown())));
-}
-
-// Test to ensure that the FORM_EVENT_CARD_SUGGESTION_WITH_METADATA_SELECTED is
-// not logged if a card without metadata is selected.
-TEST_P(CardMetadataMetricsTest,
-       LogCardMetadataSelectedMetrics_CardWithoutMetadataSelected) {
-  base::HistogramTester histogram_tester;
-
-  // Simulate selecting card_without_metadata.
-  autofill_manager().OnAskForValuesToFillTest(form(), form().fields.back());
-  autofill_manager().DidShowSuggestions(/*has_autofill_suggestions=*/true,
-                                        form(), form().fields.back());
-  autofill_manager().FillOrPreviewForm(
-      mojom::RendererFormDataAction::kFill, form(), form().fields.back(),
-      MakeFrontendId({.credit_card_id = kCardWithoutMetadataId}));
-
-  // Verify that if metadata is shown, metrics for 'card with metadata shown' is
-  // logged, but metrics for 'card with metadata selected' is not logged.
-  EXPECT_THAT(
-      histogram_tester.GetAllSamples("Autofill.FormEvents.CreditCard"),
-      BucketsInclude(
-          Bucket(FORM_EVENT_SUGGESTIONS_SHOWN, 1),
-          Bucket(FORM_EVENT_CARD_SUGGESTION_WITH_METADATA_SHOWN,
-                 card_metadata_shown()),
-          Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SELECTED, 1),
-          Bucket(FORM_EVENT_CARD_SUGGESTION_WITH_METADATA_SELECTED, 0)));
-}
-
-// Test to ensure that the FORM_EVENT_CARD_SUGGESTION_WITH_METADATA_SELECTED is
-// logged if a card with metadata is selected.
-TEST_P(CardMetadataMetricsTest,
-       LogCardMetadataSelectedMetrics_CardWithMetadataSelected) {
-  base::HistogramTester histogram_tester;
-
-  // Simulate selecting card_with_metadata.
-  autofill_manager().OnAskForValuesToFillTest(form(), form().fields.back());
-  autofill_manager().DidShowSuggestions(/*has_autofill_suggestions=*/true,
-                                        form(), form().fields.back());
-  if (card_has_linked_virtual_card()) {
-    autofill_manager().FillOrPreviewVirtualCardInformation(
-        mojom::RendererFormDataAction::kFill, kCardWithMetadataId, form(),
-        form().fields.back());
-  } else {
-    autofill_manager().FillOrPreviewForm(
-        mojom::RendererFormDataAction::kFill, form(), form().fields.back(),
-        MakeFrontendId({.credit_card_id = kCardWithMetadataId}));
-  }
-
-  // Verify that if metadata is shown, metrics for both 'card with metadata
-  // shown' and 'card with metadata selected' is logged.
-  EXPECT_THAT(
-      histogram_tester.GetAllSamples("Autofill.FormEvents.CreditCard"),
-      BucketsInclude(Bucket(FORM_EVENT_SUGGESTIONS_SHOWN, 1),
-                     Bucket(FORM_EVENT_CARD_SUGGESTION_WITH_METADATA_SHOWN,
-                            card_metadata_shown()),
-                     Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SELECTED,
-                            !card_has_linked_virtual_card()),
-                     Bucket(FORM_EVENT_VIRTUAL_CARD_SUGGESTION_SELECTED,
-                            card_has_linked_virtual_card()),
-                     Bucket(FORM_EVENT_CARD_SUGGESTION_WITH_METADATA_SELECTED,
-                            card_metadata_shown())));
-}
-
 // Test to ensure that we log card metadata related metrics only when card
 // metadata is available.
-TEST_P(CardMetadataMetricsTest, LogCardMetadataLatencyMetrics) {
+TEST_P(CardMetadataLatencyMetricsTest, LogMetrics) {
   base::TimeTicks now = AutofillTickClock::NowTicks();
   TestAutofillTickClock test_clock;
   test_clock.SetNowTicks(now);
diff --git a/components/autofill/core/browser/payments/virtual_card_enrollment_manager.cc b/components/autofill/core/browser/payments/virtual_card_enrollment_manager.cc
index c36b8317..7f86fbc 100644
--- a/components/autofill/core/browser/payments/virtual_card_enrollment_manager.cc
+++ b/components/autofill/core/browser/payments/virtual_card_enrollment_manager.cc
@@ -271,6 +271,13 @@
 void VirtualCardEnrollmentManager::OnDidGetUpdateVirtualCardEnrollmentResponse(
     VirtualCardEnrollmentRequestType type,
     AutofillClient::PaymentsRpcResult result) {
+  // Add a strike if enrollment attempt was not successful.
+  if (type == VirtualCardEnrollmentRequestType::kEnroll &&
+      result != AutofillClient::PaymentsRpcResult::kSuccess) {
+    AddStrikeToBlockOfferingVirtualCardEnrollment(base::NumberToString(
+        state_.virtual_card_enrollment_fields.credit_card.instrument_id()));
+  }
+
   LogUpdateVirtualCardEnrollmentRequestResult(
       state_.virtual_card_enrollment_fields.virtual_card_enrollment_source,
       type, result == AutofillClient::PaymentsRpcResult::kSuccess);
diff --git a/components/autofill/core/browser/payments/virtual_card_enrollment_manager.h b/components/autofill/core/browser/payments/virtual_card_enrollment_manager.h
index 74558965..7bedf37 100644
--- a/components/autofill/core/browser/payments/virtual_card_enrollment_manager.h
+++ b/components/autofill/core/browser/payments/virtual_card_enrollment_manager.h
@@ -267,6 +267,8 @@
   FRIEND_TEST_ALL_PREFIXES(VirtualCardEnrollmentManagerTest,
                            StrikeDatabase_BubbleCanceled);
   FRIEND_TEST_ALL_PREFIXES(VirtualCardEnrollmentManagerTest,
+                           StrikeDatabase_EnrollmentAttemptFailed);
+  FRIEND_TEST_ALL_PREFIXES(VirtualCardEnrollmentManagerTest,
                            StrikeDatabase_SettingsPageNotBlocked);
   FRIEND_TEST_ALL_PREFIXES(VirtualCardEnrollmentManagerTest,
                            VirtualCardEnrollmentFields_LastShow);
diff --git a/components/autofill/core/browser/payments/virtual_card_enrollment_manager_unittest.cc b/components/autofill/core/browser/payments/virtual_card_enrollment_manager_unittest.cc
index 3c22339..54c14f4 100644
--- a/components/autofill/core/browser/payments/virtual_card_enrollment_manager_unittest.cc
+++ b/components/autofill/core/browser/payments/virtual_card_enrollment_manager_unittest.cc
@@ -706,6 +706,44 @@
 }
 
 TEST_F(VirtualCardEnrollmentManagerTest,
+       StrikeDatabase_EnrollmentAttemptFailed) {
+  base::HistogramTester histogram_tester;
+  SetUpStrikeDatabaseTest();
+
+  std::vector<AutofillClient::PaymentsRpcResult> failure_results = {
+      AutofillClient::PaymentsRpcResult::kTryAgainFailure,
+      AutofillClient::PaymentsRpcResult::kPermanentFailure};
+
+  VirtualCardEnrollmentProcessState* state =
+      virtual_card_enrollment_manager_->GetVirtualCardEnrollmentProcessState();
+
+  for (int i = 0; i < static_cast<int>(failure_results.size()); i++) {
+    virtual_card_enrollment_manager_
+        ->OnDidGetUpdateVirtualCardEnrollmentResponse(
+            VirtualCardEnrollmentRequestType::kEnroll, failure_results[i]);
+    histogram_tester.ExpectBucketCount(
+        "Autofill.StrikeDatabase.NthStrikeAdded.VirtualCardEnrollment",
+        /*sample=*/i + 1, /*count=*/1);
+
+    EXPECT_EQ(virtual_card_enrollment_manager_
+                  ->GetVirtualCardEnrollmentStrikeDatabase()
+                  ->GetStrikes(
+                      base::NumberToString(state->virtual_card_enrollment_fields
+                                               .credit_card.instrument_id())),
+              i + 1);
+
+    histogram_tester.ExpectBucketCount(
+        "Autofill.VirtualCardEnrollmentStrikeDatabase." +
+            VirtualCardEnrollmentSourceToMetricSuffix(
+                state->virtual_card_enrollment_fields
+                    .virtual_card_enrollment_source),
+        VirtualCardEnrollmentStrikeDatabaseEvent::
+            VIRTUAL_CARD_ENROLLMENT_STRIKE_DATABASE_STRIKE_LOGGED,
+        i + 1);
+  }
+}
+
+TEST_F(VirtualCardEnrollmentManagerTest,
        StrikeDatabase_SettingsPageNotBlocked) {
   SetUpStrikeDatabaseTest();
   base::HistogramTester histogram_tester;
diff --git a/components/components_strings.grd b/components/components_strings.grd
index d5b3885..2c8c321 100644
--- a/components/components_strings.grd
+++ b/components/components_strings.grd
@@ -326,7 +326,6 @@
       <part file="send_tab_to_self_strings.grdp" />
       <part file="site_settings_strings.grdp" />
       <part file="sms_strings.grdp" />
-      <part file="soda_strings.grdp" />
       <part file="ssl_errors_strings.grdp" />
       <part file="subresource_filter_strings.grdp" />
       <part file="supervised_user_strings.grdp" />
diff --git a/components/contextual_search/core/browser/contextual_search_field_trial.cc b/components/contextual_search/core/browser/contextual_search_field_trial.cc
index b7dac73..cc5ecbe6 100644
--- a/components/contextual_search/core/browser/contextual_search_field_trial.cc
+++ b/components/contextual_search/core/browser/contextual_search_field_trial.cc
@@ -9,7 +9,6 @@
 #include "base/metrics/field_trial_params.h"
 #include "base/strings/string_number_conversions.h"
 #include "components/contextual_search/core/browser/public.h"
-#include "components/variations/variations_associated_data.h"
 
 namespace {
 
@@ -130,6 +129,6 @@
 }
 
 std::string ContextualSearchFieldTrial::GetParam(const std::string& name) {
-  return variations::GetVariationParamValue(
+  return base::GetFieldTrialParamValue(
       contextual_search::kContextualSearchFieldTrialName, name);
 }
diff --git a/components/flags_ui/flags_state_unittest.cc b/components/flags_ui/flags_state_unittest.cc
index 8db9633c..2b64ff74 100644
--- a/components/flags_ui/flags_state_unittest.cc
+++ b/components/flags_ui/flags_state_unittest.cc
@@ -367,7 +367,7 @@
   flags_state_->RegisterAllFeatureVariationParameters(&flags_storage_,
                                                       feature_list.get());
   // No value should be associated.
-  EXPECT_EQ("", variations::GetVariationParamValue(kTestTrial, kTestParam1));
+  EXPECT_EQ("", base::GetFieldTrialParamValue(kTestTrial, kTestParam1));
   // The trial should not be created.
   base::FieldTrial* trial = base::FieldTrialList::Find(kTestTrial);
   EXPECT_EQ(nullptr, trial);
@@ -379,7 +379,7 @@
   flags_state_->RegisterAllFeatureVariationParameters(&flags_storage_,
                                                       feature_list.get());
   // No value should be associated as this is the default option.
-  EXPECT_EQ("", variations::GetVariationParamValue(kTestTrial, kTestParam1));
+  EXPECT_EQ("", base::GetFieldTrialParamValue(kTestTrial, kTestParam1));
 
   // The trial should be created.
   trial = base::FieldTrialList::Find(kTestTrial);
@@ -393,7 +393,7 @@
   flags_state_->RegisterAllFeatureVariationParameters(&flags_storage_,
                                                       feature_list.get());
   // Associating for the second time should not change the value.
-  EXPECT_EQ("", variations::GetVariationParamValue(kTestTrial, kTestParam1));
+  EXPECT_EQ("", base::GetFieldTrialParamValue(kTestTrial, kTestParam1));
 }
 
 TEST_F(FlagsStateTest, RegisterAllFeatureVariationParametersNonDefault) {
@@ -413,7 +413,7 @@
 
   // The param should have the value predefined in this variation.
   EXPECT_EQ(kTestParamValue,
-            variations::GetVariationParamValue(kTestTrial, kTestParam1));
+            base::GetFieldTrialParamValue(kTestTrial, kTestParam1));
 
   // The value should be associated also via the name of the feature.
   EXPECT_EQ(kTestParamValue,
@@ -446,9 +446,9 @@
             base::GetFieldTrialParamValueByFeature(kTestFeature2, kTestParam2));
   // The params are registered in the same trial.
   EXPECT_EQ(kTestParamValue,
-            variations::GetVariationParamValue(kTestTrial, kTestParam1));
+            base::GetFieldTrialParamValue(kTestTrial, kTestParam1));
   EXPECT_EQ(kTestParamValue,
-            variations::GetVariationParamValue(kTestTrial, kTestParam2));
+            base::GetFieldTrialParamValue(kTestTrial, kTestParam2));
 }
 
 base::CommandLine::StringType CreateSwitch(const std::string& value) {
diff --git a/components/history_clusters/core/filter_cluster_processor.cc b/components/history_clusters/core/filter_cluster_processor.cc
index 2f7bcd5..fc486b7 100644
--- a/components/history_clusters/core/filter_cluster_processor.cc
+++ b/components/history_clusters/core/filter_cluster_processor.cc
@@ -28,7 +28,8 @@
 // Returns whether `filter_params` is a filter that would actually filter
 // clusters out.
 bool IsFunctionalFilter(QueryClustersFilterParams filter_params) {
-  return filter_params.min_visits_with_images > 0 ||
+  return filter_params.min_visits > 0 ||
+         filter_params.min_visits_with_images > 0 ||
          !filter_params.categories_allowlist.empty() ||
          !filter_params.categories_blocklist.empty() ||
          filter_params.is_search_initiated ||
@@ -117,7 +118,7 @@
   bool is_search_initiated = false;
   bool has_related_searches = false;
   size_t num_interesting_visits = 0;
-  size_t num_visits = 0;
+  int num_visits = 0;
   bool is_content_visible = true;
 
   for (const auto& visit : cluster.visits) {
@@ -161,6 +162,11 @@
   }
 
   bool matches_filter = true;
+  if (num_visits < filter_params_->min_visits) {
+    RecordClusterFilterReasonHistogram(clustering_request_source_,
+                                       ClusterFilterReason::kNotEnoughVisits);
+    matches_filter = false;
+  }
   if (num_visits_with_images < filter_params_->min_visits_with_images) {
     RecordClusterFilterReasonHistogram(clustering_request_source_,
                                        ClusterFilterReason::kNotEnoughImages);
diff --git a/components/history_clusters/core/filter_cluster_processor.h b/components/history_clusters/core/filter_cluster_processor.h
index 0acb192..9a7a2c5 100644
--- a/components/history_clusters/core/filter_cluster_processor.h
+++ b/components/history_clusters/core/filter_cluster_processor.h
@@ -24,11 +24,12 @@
   kSingleVisit = 6,
   kNotContentVisible = 7,
   kHasBlockedCategory = 8,
+  kNotEnoughVisits = 9,
 
   // Add above here and make sure to keep `ClusterFilterReason` up to date in
   // enums.xml.
 
-  kMaxValue = kHasBlockedCategory
+  kMaxValue = kNotEnoughVisits
 };
 
 // A cluster processor that removes clusters that do not match the filter.
diff --git a/components/history_clusters/core/filter_cluster_processor_unittest.cc b/components/history_clusters/core/filter_cluster_processor_unittest.cc
index 953ac99..2de28f6 100644
--- a/components/history_clusters/core/filter_cluster_processor_unittest.cc
+++ b/components/history_clusters/core/filter_cluster_processor_unittest.cc
@@ -188,6 +188,33 @@
       0);
 }
 
+TEST_F(FilterClusterProcessorTest, OnlyVisitsConstraint) {
+  base::HistogramTester histogram_tester;
+
+  QueryClustersFilterParams params;
+  params.min_visits = 2;
+
+  EXPECT_THAT(GetTestClusterIdsThatPassFilter(params),
+              ElementsAre(2, 3, 4, 5, 6, 7, 9, 10, 11));
+
+  histogram_tester.ExpectUniqueSample(
+      "History.Clusters.Backend.FilterClusterProcessor.NumClusters.PreFilter."
+      "NewTabPage",
+      12, 1);
+  histogram_tester.ExpectUniqueSample(
+      "History.Clusters.Backend.FilterClusterProcessor.NumClusters.PostFilter."
+      "NewTabPage",
+      9, 1);
+  histogram_tester.ExpectBucketCount(
+      "History.Clusters.Backend.FilterClusterProcessor.ClusterFilterReason."
+      "NewTabPage",
+      ClusterFilterReason::kNotFiltered, 9);
+  histogram_tester.ExpectBucketCount(
+      "History.Clusters.Backend.FilterClusterProcessor.ClusterFilterReason."
+      "NewTabPage",
+      ClusterFilterReason::kNotEnoughVisits, 3);
+}
+
 TEST_F(FilterClusterProcessorTest, OnlyImageConstraint) {
   base::HistogramTester histogram_tester;
 
@@ -400,6 +427,7 @@
   base::HistogramTester histogram_tester;
 
   QueryClustersFilterParams params;
+  params.min_visits = 2;
   params.min_visits_with_images = 2;
   params.categories_allowlist = {"category1", "category2"};
   params.categories_blocklist = {"blocked"};
@@ -424,6 +452,10 @@
   histogram_tester.ExpectBucketCount(
       "History.Clusters.Backend.FilterClusterProcessor.ClusterFilterReason."
       "NewTabPage",
+      ClusterFilterReason::kNotEnoughVisits, 3);
+  histogram_tester.ExpectBucketCount(
+      "History.Clusters.Backend.FilterClusterProcessor.ClusterFilterReason."
+      "NewTabPage",
       ClusterFilterReason::kNotEnoughImages, 5);
   histogram_tester.ExpectBucketCount(
       "History.Clusters.Backend.FilterClusterProcessor.ClusterFilterReason."
diff --git a/components/history_clusters/core/history_clusters_types.h b/components/history_clusters/core/history_clusters_types.h
index 0b914ad..afdcb63 100644
--- a/components/history_clusters/core/history_clusters_types.h
+++ b/components/history_clusters/core/history_clusters_types.h
@@ -34,6 +34,11 @@
 
   // Parameters related to the minimum requirements for returned clusters.
 
+  // The minimum number of non-hidden visits that are required for returned
+  // clusters. Note that this also implicitly works as a visit filter such that
+  // if fewer than `min_total_visits` are in a cluster, it will be filtered out.
+  int min_visits = 0;
+
   // The minimum number of visits within a cluster that have associated images.
   // Note that this also implicitly works as a visit filter such that if fewer
   // than `min_visits_with_images` are in a cluster, it will be filtered out.
diff --git a/components/image_service/features.cc b/components/image_service/features.cc
index 045270eb..e4ed9c1 100644
--- a/components/image_service/features.cc
+++ b/components/image_service/features.cc
@@ -14,9 +14,9 @@
              "ImageServiceSuggestPoweredImages",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
-// Disabled by default, usage is approved but we still want to control rollout.
+// Enabled the capability by default, can be used as a killswitch.
 BASE_FEATURE(kImageServiceOptimizationGuideSalientImages,
              "ImageServiceOptimizationGuideSalientImages",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+             base::FEATURE_ENABLED_BY_DEFAULT);
 
 }  // namespace image_service
diff --git a/components/image_service/image_service_unittest.cc b/components/image_service/image_service_unittest.cc
index 6cf51de..26d78f3 100644
--- a/components/image_service/image_service_unittest.cc
+++ b/components/image_service/image_service_unittest.cc
@@ -316,4 +316,36 @@
       << "Expect that making more request restarts the queue.";
 }
 
+class DisabledOptGuideImageServiceTest : public ImageServiceTest {
+ public:
+  DisabledOptGuideImageServiceTest() = default;
+
+  void SetUp() override {
+    scoped_feature_list_.InitWithFeatures(
+        /*enabled_features=*/{kImageService, kImageServiceSuggestPoweredImages},
+        /*disabled_features=*/{kImageServiceOptimizationGuideSalientImages});
+
+    test_opt_guide_ =
+        std::make_unique<optimization_guide::ImageServiceTestOptGuide>();
+    test_sync_service_ = std::make_unique<syncer::TestSyncService>();
+    image_service_ = std::make_unique<ImageService>(
+        nullptr, test_opt_guide_.get(), test_sync_service_.get());
+  }
+};
+
+TEST_F(DisabledOptGuideImageServiceTest, DoesNotFetch) {
+  mojom::Options options;
+  options.suggest_images = false;
+  options.optimization_guide_images = true;
+
+  GURL image_url_response;
+  image_service_->FetchImageFor(
+      mojom::ClientId::Journeys, GURL("https://page-url.com"), options,
+      base::BindOnce(&StoreImageUrlResponse, &image_url_response));
+
+  // Verify that the OptimizationGuide backend did not get called.
+  EXPECT_EQ(test_opt_guide_->requests_received_, 0U);
+  EXPECT_EQ(image_url_response, GURL());
+}
+
 }  // namespace image_service
diff --git a/components/ntp_tiles/popular_sites_impl.cc b/components/ntp_tiles/popular_sites_impl.cc
index 5868b3c..ff1f274 100644
--- a/components/ntp_tiles/popular_sites_impl.cc
+++ b/components/ntp_tiles/popular_sites_impl.cc
@@ -13,6 +13,7 @@
 #include "base/command_line.h"
 #include "base/feature_list.h"
 #include "base/functional/bind.h"
+#include "base/metrics/field_trial_params.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
@@ -130,18 +131,16 @@
 }
 
 std::string GetVariationCountry() {
-  return variations::GetVariationParamValue(kPopularSitesFieldTrialName,
-                                            "country");
+  return base::GetFieldTrialParamValue(kPopularSitesFieldTrialName, "country");
 }
 
 std::string GetVariationVersion() {
-  return variations::GetVariationParamValue(kPopularSitesFieldTrialName,
-                                            "version");
+  return base::GetFieldTrialParamValue(kPopularSitesFieldTrialName, "version");
 }
 
 std::string GetVariationDirectory() {
-  return variations::GetVariationParamValue(kPopularSitesFieldTrialName,
-                                            "directory");
+  return base::GetFieldTrialParamValue(kPopularSitesFieldTrialName,
+                                       "directory");
 }
 
 PopularSites::SitesVector ParseSiteList(const base::Value::List& list) {
diff --git a/components/omnibox/browser/omnibox_field_trial.cc b/components/omnibox/browser/omnibox_field_trial.cc
index 12589c1..e75dd05 100644
--- a/components/omnibox/browser/omnibox_field_trial.cc
+++ b/components/omnibox/browser/omnibox_field_trial.cc
@@ -201,7 +201,7 @@
 }
 
 int OmniboxFieldTrial::GetDisabledProviderTypes() {
-  const std::string& types_string = variations::GetVariationParamValue(
+  const std::string& types_string = base::GetFieldTrialParamValue(
       kBundledExperimentFieldTrialName, kDisableProvidersRule);
   int types = 0;
   if (types_string.empty() || !base::StringToInt(types_string, &types)) {
@@ -375,7 +375,7 @@
 }
 
 float OmniboxFieldTrial::HQPBookmarkValue() {
-  std::string bookmark_value_str = variations::GetVariationParamValue(
+  std::string bookmark_value_str = base::GetFieldTrialParamValue(
       kBundledExperimentFieldTrialName, kHQPBookmarkValueRule);
   if (bookmark_value_str.empty()) {
     return 10;
@@ -389,24 +389,23 @@
 }
 
 bool OmniboxFieldTrial::HQPAllowMatchInTLDValue() {
-  return variations::GetVariationParamValue(kBundledExperimentFieldTrialName,
-                                            kHQPAllowMatchInTLDRule) == "true";
+  return base::GetFieldTrialParamValue(kBundledExperimentFieldTrialName,
+                                       kHQPAllowMatchInTLDRule) == "true";
 }
 
 bool OmniboxFieldTrial::HQPAllowMatchInSchemeValue() {
-  return variations::GetVariationParamValue(kBundledExperimentFieldTrialName,
-                                            kHQPAllowMatchInSchemeRule) ==
-         "true";
+  return base::GetFieldTrialParamValue(kBundledExperimentFieldTrialName,
+                                       kHQPAllowMatchInSchemeRule) == "true";
 }
 
 void OmniboxFieldTrial::GetSuggestPollingStrategy(bool* from_last_keystroke,
                                                   int* polling_delay_ms) {
   *from_last_keystroke =
-      variations::GetVariationParamValue(
+      base::GetFieldTrialParamValue(
           kBundledExperimentFieldTrialName,
           kMeasureSuggestPollingDelayFromLastKeystrokeRule) == "true";
 
-  const std::string& polling_delay_string = variations::GetVariationParamValue(
+  const std::string& polling_delay_string = base::GetFieldTrialParamValue(
       kBundledExperimentFieldTrialName, kSuggestPollingDelayMsRule);
   if (polling_delay_string.empty() ||
       !base::StringToInt(polling_delay_string, polling_delay_ms) ||
@@ -416,12 +415,12 @@
 }
 
 std::string OmniboxFieldTrial::HQPExperimentalScoringBuckets() {
-  return variations::GetVariationParamValue(
-      kBundledExperimentFieldTrialName, kHQPExperimentalScoringBucketsParam);
+  return base::GetFieldTrialParamValue(kBundledExperimentFieldTrialName,
+                                       kHQPExperimentalScoringBucketsParam);
 }
 
 float OmniboxFieldTrial::HQPExperimentalTopicalityThreshold() {
-  std::string topicality_threshold_str = variations::GetVariationParamValue(
+  std::string topicality_threshold_str = base::GetFieldTrialParamValue(
       kBundledExperimentFieldTrialName,
       kHQPExperimentalScoringTopicalityThresholdParam);
 
@@ -456,7 +455,7 @@
 }
 
 size_t OmniboxFieldTrial::HQPMaxVisitsToScore() {
-  std::string max_visits_str = variations::GetVariationParamValue(
+  std::string max_visits_str = base::GetFieldTrialParamValue(
       kBundledExperimentFieldTrialName, kHQPMaxVisitsToScoreRule);
   constexpr size_t kDefaultMaxVisitsToScore = 10;
   static_assert(
@@ -474,7 +473,7 @@
 }
 
 float OmniboxFieldTrial::HQPTypedValue() {
-  std::string typed_value_str = variations::GetVariationParamValue(
+  std::string typed_value_str = base::GetFieldTrialParamValue(
       kBundledExperimentFieldTrialName, kHQPTypedValueRule);
   if (typed_value_str.empty()) {
     return 1.5;
@@ -488,7 +487,7 @@
 }
 
 OmniboxFieldTrial::NumMatchesScores OmniboxFieldTrial::HQPNumMatchesScores() {
-  std::string str = variations::GetVariationParamValue(
+  std::string str = base::GetFieldTrialParamValue(
       kBundledExperimentFieldTrialName, kHQPNumMatchesScoresRule);
   static constexpr char kDefaultNumMatchesScores[] = "1:3,2:2.5,3:2,4:1.5";
   if (str.empty()) {
@@ -519,8 +518,8 @@
   // size_t) containing the number of words.
   size_t num_title_words;
   if (!base::StringToSizeT(
-          variations::GetVariationParamValue(kBundledExperimentFieldTrialName,
-                                             kHQPNumTitleWordsRule),
+          base::GetFieldTrialParamValue(kBundledExperimentFieldTrialName,
+                                        kHQPNumTitleWordsRule),
           &num_title_words)) {
     return 20;
   }
@@ -528,19 +527,18 @@
 }
 
 bool OmniboxFieldTrial::HQPAlsoDoHUPLikeScoring() {
-  return variations::GetVariationParamValue(kBundledExperimentFieldTrialName,
-                                            kHQPAlsoDoHUPLikeScoringRule) ==
-         "true";
+  return base::GetFieldTrialParamValue(kBundledExperimentFieldTrialName,
+                                       kHQPAlsoDoHUPLikeScoringRule) == "true";
 }
 
 bool OmniboxFieldTrial::HUPSearchDatabase() {
-  const std::string& value = variations::GetVariationParamValue(
+  const std::string& value = base::GetFieldTrialParamValue(
       kBundledExperimentFieldTrialName, kHUPSearchDatabaseRule);
   return value.empty() || (value == "true");
 }
 
 int OmniboxFieldTrial::KeywordScoreForSufficientlyCompleteMatch() {
-  std::string value_str = variations::GetVariationParamValue(
+  std::string value_str = base::GetFieldTrialParamValue(
       kBundledExperimentFieldTrialName,
       kKeywordScoreForSufficientlyCompleteMatchRule);
   if (value_str.empty()) {
diff --git a/components/optimization_guide/proto/hints.proto b/components/optimization_guide/proto/hints.proto
index 31dba807..74148b6 100644
--- a/components/optimization_guide/proto/hints.proto
+++ b/components/optimization_guide/proto/hints.proto
@@ -113,7 +113,7 @@
 // Must sync with enum OptimizationType in enums.xml for metric recording.
 enum OptimizationType {
   // Values for obsolete optimizations.
-  reserved 11, 12;
+  reserved 11, 12, 29;
 
   TYPE_UNSPECIFIED = 0;
   // This optimization blocks JavaScript on the page.
diff --git a/components/password_manager/core/browser/ui/credential_ui_entry.cc b/components/password_manager/core/browser/ui/credential_ui_entry.cc
index 9ab1c4a0..c1f7466 100644
--- a/components/password_manager/core/browser/ui/credential_ui_entry.cc
+++ b/components/password_manager/core/browser/ui/credential_ui_entry.cc
@@ -203,6 +203,7 @@
 std::vector<CredentialUIEntry::DomainInfo>
 CredentialUIEntry::GetAffiliatedDomains() const {
   std::vector<CredentialUIEntry::DomainInfo> domains;
+  std::set<std::string> unique_domain;
   for (const auto& facet : facets) {
     CredentialUIEntry::DomainInfo domain;
     domain.signon_realm = facet.signon_realm;
@@ -222,7 +223,9 @@
       domain.name = GetOrigin(url::Origin::Create(facet.url));
       domain.url = facet.url;
     }
-    domains.push_back(std::move(domain));
+    if (unique_domain.insert(domain.name).second) {
+      domains.push_back(std::move(domain));
+    }
   }
   return domains;
 }
diff --git a/components/password_manager/core/browser/ui/credential_ui_entry_unittest.cc b/components/password_manager/core/browser/ui/credential_ui_entry_unittest.cc
index 1e90c04..83db04b 100644
--- a/components/password_manager/core/browser/ui/credential_ui_entry_unittest.cc
+++ b/components/password_manager/core/browser/ui/credential_ui_entry_unittest.cc
@@ -199,4 +199,18 @@
   EXPECT_TRUE(reused_entry.IsReused());
 }
 
+TEST(CredentialUIEntryTest, TestGetAffiliatedDomainsWithDuplicates) {
+  PasswordForm form1;
+  form1.signon_realm = "https://g.com/";
+  form1.url = GURL("https://g.com/");
+
+  PasswordForm form2;
+  form2.signon_realm = "https://g.com/";
+  form2.url = GURL("https://g.com/");
+
+  CredentialUIEntry entry = CredentialUIEntry({form1, form2});
+  EXPECT_THAT(entry.GetAffiliatedDomains(),
+              ElementsAre(ExpectDomain("g.com", form1.url)));
+}
+
 }  // namespace password_manager
diff --git a/components/safe_browsing/android/remote_database_manager.cc b/components/safe_browsing/android/remote_database_manager.cc
index 4c1d7765..196be8e 100644
--- a/components/safe_browsing/android/remote_database_manager.cc
+++ b/components/safe_browsing/android/remote_database_manager.cc
@@ -10,6 +10,7 @@
 
 #include "base/functional/bind.h"
 #include "base/memory/raw_ptr.h"
+#include "base/metrics/field_trial_params.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/strings/string_number_conversions.h"
@@ -20,7 +21,6 @@
 #include "components/safe_browsing/core/browser/db/v4_get_hash_protocol_manager.h"
 #include "components/safe_browsing/core/browser/db/v4_protocol_manager_util.h"
 #include "components/safe_browsing/core/common/features.h"
-#include "components/variations/variations_associated_data.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
@@ -131,7 +131,7 @@
   // The param is expected to be a comma-separated list of ints
   // corresponding to the enum types.  We're keeping this finch
   // control around so we can add back types if they later become dangerous.
-  const std::string ints_str = variations::GetVariationParamValue(
+  const std::string ints_str = base::GetFieldTrialParamValue(
       kAndroidFieldExperiment, kAndroidTypesToCheckParam);
   if (ints_str.empty()) {
     // By default, we check all types except a few.
diff --git a/components/safe_browsing/core/browser/BUILD.gn b/components/safe_browsing/core/browser/BUILD.gn
index 67a2f6a..9053fc30 100644
--- a/components/safe_browsing/core/browser/BUILD.gn
+++ b/components/safe_browsing/core/browser/BUILD.gn
@@ -109,6 +109,7 @@
     "//base/test:test_support",
     "//components/safe_browsing/core/browser/db:v4_protocol_manager_util",
     "//components/safe_browsing/core/browser/db:v4_test_util",
+    "//components/safe_browsing/core/common",
     "//testing/gtest",
   ]
 }
diff --git a/components/safe_browsing/core/browser/safe_browsing_lookup_mechanism_experimenter.cc b/components/safe_browsing/core/browser/safe_browsing_lookup_mechanism_experimenter.cc
index 94061e4..e3c8cb85 100644
--- a/components/safe_browsing/core/browser/safe_browsing_lookup_mechanism_experimenter.cc
+++ b/components/safe_browsing/core/browser/safe_browsing_lookup_mechanism_experimenter.cc
@@ -12,6 +12,7 @@
 #include "components/safe_browsing/core/browser/hash_realtime_mechanism.h"
 #include "components/safe_browsing/core/browser/safe_browsing_lookup_mechanism_runner.h"
 #include "components/safe_browsing/core/browser/url_realtime_mechanism.h"
+#include "components/safe_browsing/core/common/features.h"
 
 namespace safe_browsing {
 SafeBrowsingLookupMechanismExperimenter::
@@ -481,6 +482,9 @@
   }
 }
 void SafeBrowsingLookupMechanismExperimenter::MaybeLogUrlLevelResults() const {
+  if (!safe_browsing::kUrlLevelValidationForHprtExperimentEnabled.Get()) {
+    return;
+  }
   if (checks_to_run_.size() != 1 ||
       !checks_to_run_.back()->would_check_show_warning_if_unsafe.has_value()) {
     DCHECK(false);
diff --git a/components/safe_browsing/core/browser/safe_browsing_lookup_mechanism_experimenter_unittest.cc b/components/safe_browsing/core/browser/safe_browsing_lookup_mechanism_experimenter_unittest.cc
index 93deab1..2cbde05 100644
--- a/components/safe_browsing/core/browser/safe_browsing_lookup_mechanism_experimenter_unittest.cc
+++ b/components/safe_browsing/core/browser/safe_browsing_lookup_mechanism_experimenter_unittest.cc
@@ -8,11 +8,13 @@
 #include "base/memory/scoped_refptr.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/mock_callback.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
 #include "base/time/time.h"
 #include "components/safe_browsing/core/browser/db/v4_protocol_manager_util.h"
 #include "components/safe_browsing/core/browser/db/v4_test_util.h"
 #include "components/safe_browsing/core/browser/safe_browsing_lookup_mechanism_runner.h"
+#include "components/safe_browsing/core/common/features.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/platform_test.h"
 
@@ -280,6 +282,15 @@
     absl::optional<AllInOneResult> delayed_response_result;
   };
 
+  void SetUp() override {
+    std::map<std::string, std::string> params = {
+        {"UrlLevelValidationForHprtExperimentEnabled", "true"}};
+    feature_list_.InitAndEnableFeatureWithParameters(
+        kSafeBrowsingLookupMechanismExperiment, params);
+  }
+
+  void TearDown() override { feature_list_.Reset(); }
+
   void ResetMetrics() {
     histogram_tester_ = std::make_unique<base::HistogramTester>();
   }
@@ -1090,6 +1101,16 @@
       std::make_unique<PretendUrlCheckerDelegate>();
   std::unique_ptr<safe_browsing::MockPingManager> ping_manager_ =
       std::make_unique<safe_browsing::MockPingManager>();
+  base::test::ScopedFeatureList feature_list_;
+};
+class SafeBrowsingLookupMechanismExperimenterUrlLevelValidationDisabledTest
+    : public SafeBrowsingLookupMechanismExperimenterTest {
+  void SetUp() override {
+    std::map<std::string, std::string> params = {
+        {"UrlLevelValidationForHprtExperimentEnabled", "false"}};
+    feature_list_.InitAndEnableFeatureWithParameters(
+        kSafeBrowsingLookupMechanismExperiment, params);
+  }
 };
 
 TEST_F(SafeBrowsingLookupMechanismExperimenterTest, TestLifetimes) {
@@ -1356,6 +1377,34 @@
   }
 }
 
+TEST_F(SafeBrowsingLookupMechanismExperimenterUrlLevelValidationDisabledTest,
+       TestUrlLevelValidation) {
+  std::vector<bool> no_time_outs = {false, false, false};
+  std::vector<absl::optional<UrlLevelValidationDetails>>
+      urt_hpd_hprt_url_level_validation_details = {
+          UrlLevelValidationDetails(
+              /*locally_cached_results_threat_type=*/SB_THREAT_TYPE_SAFE,
+              /*real_time_request_failed=*/false,
+              /*matched_high_confidence_allowlist=*/true),
+          UrlLevelValidationDetails(
+              /*locally_cached_results_threat_type=*/absl::nullopt,
+              /*real_time_request_failed=*/false,
+              /*matched_high_confidence_allowlist=*/absl::nullopt),
+          UrlLevelValidationDetails(
+              /*locally_cached_results_threat_type=*/SB_THREAT_TYPE_BILLING,
+              /*real_time_request_failed=*/false,
+              /*matched_high_confidence_allowlist=*/true)};
+  std::vector<SBThreatType> urt_hpd_hprt_threat_types = {
+      SB_THREAT_TYPE_SAFE, SB_THREAT_TYPE_URL_MALWARE,
+      SB_THREAT_TYPE_URL_PHISHING};
+
+  // The report is not sent if the feature is disabled.
+  RunUrlLevelValidationTest(urt_hpd_hprt_threat_types,
+                            urt_hpd_hprt_url_level_validation_details,
+                            /*urt_hpd_hprt_time_out=*/no_time_outs,
+                            /*expect_report_sent=*/false);
+}
+
 TEST_F(SafeBrowsingLookupMechanismExperimenterTest,
        TestGetExperimentDetailsThreatType) {
   struct TestCase {
diff --git a/components/safe_browsing/core/common/features.cc b/components/safe_browsing/core/common/features.cc
index cfc446f..8182741 100644
--- a/components/safe_browsing/core/common/features.cc
+++ b/components/safe_browsing/core/common/features.cc
@@ -193,6 +193,11 @@
              "SafeBrowsingLookupMechanismExperiment",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
+const base::FeatureParam<bool> kUrlLevelValidationForHprtExperimentEnabled{
+    &kSafeBrowsingLookupMechanismExperiment,
+    "UrlLevelValidationForHprtExperimentEnabled",
+    /*default_value=*/true};
+
 BASE_FEATURE(kSafeBrowsingOnUIThread,
              "SafeBrowsingOnUIThread",
              base::FEATURE_DISABLED_BY_DEFAULT);
diff --git a/components/safe_browsing/core/common/features.h b/components/safe_browsing/core/common/features.h
index ec30f31..7f62a7a 100644
--- a/components/safe_browsing/core/common/features.h
+++ b/components/safe_browsing/core/common/features.h
@@ -198,6 +198,12 @@
 // known as the hash-prefix real-time lookup experiment, since that mechanism is
 // the main comparison anchor.
 BASE_DECLARE_FEATURE(kSafeBrowsingLookupMechanismExperiment);
+// Controls whether the SafeBrowsingLookupMechanismExperiment (AKA HPRT
+// experiment) conditionally logs a Client Safe Browsing Report when the
+// experiment ends for URL-level validation purposes. This is only relevant
+// while the HPRT experiment is running, which is only enabled for ESB users.
+extern const base::FeatureParam<bool>
+    kUrlLevelValidationForHprtExperimentEnabled;
 
 // Run Safe Browsing code on UI thread.
 BASE_DECLARE_FEATURE(kSafeBrowsingOnUIThread);
diff --git a/components/search/ntp_features.cc b/components/search/ntp_features.cc
index 666e881..d9beb2d 100644
--- a/components/search/ntp_features.cc
+++ b/components/search/ntp_features.cc
@@ -248,6 +248,11 @@
              "NtpHistoryClustersModuleBeginTimeDuration",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
+// Dummy feature to set kNtpHistoryClustersModuleMinimumVisitsRequiredParam.
+BASE_FEATURE(kNtpHistoryClustersModuleMinimumVisitsRequired,
+             "NtpHistoryClustersModuleMinimumVisitsRequired",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
 // Dummy feature to set kNtpHistoryClustersModuleMinimumImagesRequiredParam.
 BASE_FEATURE(kNtpHistoryClustersModuleMinimumImagesRequired,
              "NtpHistoryClustersModuleMinimumImagesRequired",
@@ -332,6 +337,8 @@
     "NtpRecipeTasksModuleExperimentGroupParam";
 const char kNtpHistoryClustersModuleBeginTimeDurationHoursParam[] =
     "NtpHistoryClustersModuleBeginTimeDurationHoursParam";
+const char kNtpHistoryClustersModuleMinimumVisitsRequiredParam[] =
+    "NtpHistoryClustersModuleMinimumVisitsRequiredParam";
 const char kNtpHistoryClustersModuleMinimumImagesRequiredParam[] =
     "NtpHistoryClustersModuleMinimumImagesRequiredParam";
 const char kNtpHistoryClustersModuleCategoriesAllowlistParam[] =
diff --git a/components/search/ntp_features.h b/components/search/ntp_features.h
index 44c57a5..61a57c6 100644
--- a/components/search/ntp_features.h
+++ b/components/search/ntp_features.h
@@ -67,6 +67,7 @@
 BASE_DECLARE_FEATURE(kNtpHandleMostVisitedNavigationExplicitly);
 BASE_DECLARE_FEATURE(kNtpHistoryClustersModule);
 BASE_DECLARE_FEATURE(kNtpHistoryClustersModuleBeginTimeDuration);
+BASE_DECLARE_FEATURE(kNtpHistoryClustersModuleMinimumVisitsRequired);
 BASE_DECLARE_FEATURE(kNtpHistoryClustersModuleMinimumImagesRequired);
 BASE_DECLARE_FEATURE(kNtpHistoryClustersModuleCategories);
 BASE_DECLARE_FEATURE(kNtpHistoryClustersModuleLoad);
@@ -144,6 +145,9 @@
 // Parameter for determining the maximum number of hours to look back to show a
 // history cluster.
 extern const char kNtpHistoryClustersModuleBeginTimeDurationHoursParam[];
+// Parameter for determining the minimum number of visits that are required in
+// order to show a history cluster.
+extern const char kNtpHistoryClustersModuleMinimumVisitsRequiredParam[];
 // Parameter for determining the minimum number of visits with an image that are
 // required in order to show a history cluster.
 extern const char kNtpHistoryClustersModuleMinimumImagesRequiredParam[];
diff --git a/components/search_engines/template_url_service.cc b/components/search_engines/template_url_service.cc
index 2bd1ce0..77763f7 100644
--- a/components/search_engines/template_url_service.cc
+++ b/components/search_engines/template_url_service.cc
@@ -15,6 +15,7 @@
 #include "base/functional/callback_helpers.h"
 #include "base/i18n/case_conversion.h"
 #include "base/memory/raw_ptr.h"
+#include "base/metrics/field_trial_params.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/observer_list.h"
@@ -38,7 +39,6 @@
 #include "components/sync/protocol/entity_specifics.pb.h"
 #include "components/sync/protocol/search_engine_specifics.pb.h"
 #include "components/url_formatter/url_fixer.h"
-#include "components/variations/variations_associated_data.h"
 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
 #include "url/gurl.h"
 
@@ -171,7 +171,7 @@
   constexpr char kBundledExperimentFieldTrialName[] =
       "OmniboxBundledExperimentV1";
   constexpr char kKeywordRequiresRegistryRule[] = "KeywordRequiresRegistry";
-  const std::string value = variations::GetVariationParamValue(
+  const std::string value = base::GetFieldTrialParamValue(
       kBundledExperimentFieldTrialName, kKeywordRequiresRegistryRule);
   return value.empty() || (value == "true");
 }
diff --git a/components/security_interstitials/content/cert_report_helper.cc b/components/security_interstitials/content/cert_report_helper.cc
index 5054f69..8fa1029 100644
--- a/components/security_interstitials/content/cert_report_helper.cc
+++ b/components/security_interstitials/content/cert_report_helper.cc
@@ -8,6 +8,7 @@
 
 #include "base/logging.h"
 #include "base/metrics/field_trial.h"
+#include "base/metrics/field_trial_params.h"
 #include "base/rand_util.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/utf_string_conversions.h"
@@ -20,7 +21,6 @@
 #include "components/security_interstitials/core/metrics_helper.h"
 #include "components/strings/grit/components_strings.h"
 #include "components/user_prefs/user_prefs.h"
-#include "components/variations/variations_associated_data.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/web_contents.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -243,7 +243,7 @@
   // for all of these users. Check the Finch configuration for a sending
   // threshold and only send reports in case the threshold isn't exceeded.
   const std::string param =
-      variations::GetVariationParamValue(kFinchExperimentName, kFinchParamName);
+      base::GetFieldTrialParamValue(kFinchExperimentName, kFinchParamName);
   if (!param.empty()) {
     double sendingThreshold;
     if (base::StringToDouble(param, &sendingThreshold)) {
diff --git a/components/site_engagement/content/site_engagement_score.cc b/components/site_engagement/content/site_engagement_score.cc
index 00092e5..b5f8f39 100644
--- a/components/site_engagement/content/site_engagement_score.cc
+++ b/components/site_engagement/content/site_engagement_score.cc
@@ -8,6 +8,7 @@
 #include <cmath>
 #include <utility>
 
+#include "base/metrics/field_trial_params.h"
 #include "base/no_destructor.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/time/clock.h"
@@ -19,7 +20,6 @@
 #include "components/content_settings/core/common/content_settings_utils.h"
 #include "components/site_engagement/content/engagement_type.h"
 #include "components/site_engagement/content/site_engagement_metrics.h"
-#include "components/variations/variations_associated_data.h"
 #include "third_party/blink/public/mojom/site_engagement/site_engagement.mojom.h"
 
 namespace site_engagement {
@@ -191,8 +191,8 @@
   double param_vals[MAX_VARIATION];
 
   for (int i = 0; i < MAX_VARIATION; ++i) {
-    std::string param_string = variations::GetVariationParamValue(
-        param_name, GetParamValues()[i].first);
+    std::string param_string =
+        base::GetFieldTrialParamValue(param_name, GetParamValues()[i].first);
 
     // Bail out if we didn't get a param string for the key, or if we couldn't
     // convert the param string to a double, or if we get a negative value.
diff --git a/components/soda/BUILD.gn b/components/soda/BUILD.gn
index 30eaba1..91498497 100644
--- a/components/soda/BUILD.gn
+++ b/components/soda/BUILD.gn
@@ -58,6 +58,7 @@
     "//components/component_updater",
     "//components/crx_file",
     "//components/strings/",
+    "//ui/base",
   ]
 }
 
diff --git a/components/soda/constants.cc b/components/soda/constants.cc
index bd046b4..9d0004e7 100644
--- a/components/soda/constants.cc
+++ b/components/soda/constants.cc
@@ -15,6 +15,7 @@
 #include "components/component_updater/component_updater_paths.h"
 #include "components/crx_file/id_util.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
+#include "ui/base/l10n/l10n_util.h"
 
 namespace speech {
 
@@ -183,13 +184,15 @@
   return LanguageCode::kNone;
 }
 
-int GetLanguageDisplayName(const std::string& language_name) {
+const std::u16string GetLanguageDisplayName(const std::string& language_name,
+                                            const std::string& display_locale) {
   absl::optional<SodaLanguagePackComponentConfig> language_config =
       GetLanguageComponentConfig(language_name);
   if (language_config.has_value()) {
-    return language_config.value().display_name;
+    return l10n_util::GetDisplayNameForLocale(
+        language_config.value().language_name, display_locale, true);
   }
-  return 0;
+  return std::u16string();
 }
 
 const std::string GetInstallationSuccessTimeMetricForLanguagePack(
diff --git a/components/soda/constants.h b/components/soda/constants.h
index 367a8c8..82b2b98 100644
--- a/components/soda/constants.h
+++ b/components/soda/constants.h
@@ -43,10 +43,6 @@
   // The language name for the language component (e.g. "en-US").
   const char* language_name = nullptr;
 
-  // The message ID for the display name of the language component (e.g.
-  // "English").
-  const int display_name;
-
   // The name of the config file path pref for the language pack.
   const char* config_path_pref = nullptr;
 
@@ -55,47 +51,39 @@
   const uint8_t public_key_sha[32] = {};
 };
 
-// TODO(crbug.com/1161569): Replace the display name mapping with
-// GetDisplayNameForLocale.
 constexpr SodaLanguagePackComponentConfig kLanguageComponentConfigs[] = {
     {LanguageCode::kEnUs,
      "en-US",
-     IDS_SODA_LANGUAGE_DISPLAY_NAME_ENGLISH,
      prefs::kSodaEnUsConfigPath,
      {0xe4, 0x64, 0x1c, 0xc2, 0x8c, 0x2a, 0x97, 0xa7, 0x16, 0x61, 0xbd,
       0xa9, 0xbe, 0xe6, 0x93, 0x56, 0xf5, 0x05, 0x33, 0x9b, 0x8b, 0x0b,
       0x02, 0xe2, 0x6b, 0x7e, 0x6c, 0x40, 0xa1, 0xd2, 0x7e, 0x18}},
     {LanguageCode::kDeDe,
      "de-DE",
-     IDS_SODA_LANGUAGE_DISPLAY_NAME_GERMAN,
      prefs::kSodaDeDeConfigPath,
      {0x92, 0xb6, 0xd8, 0xa3, 0x0b, 0x09, 0xce, 0x21, 0xdb, 0x68, 0x48,
       0x15, 0xcb, 0x49, 0xd7, 0xc6, 0x21, 0x3f, 0xe5, 0x96, 0x10, 0x97,
       0x6e, 0x0f, 0x08, 0x31, 0xec, 0xe4, 0x7f, 0xed, 0xef, 0x3d}},
     {LanguageCode::kEsEs,
      "es-ES",
-     IDS_SODA_LANGUAGE_DISPLAY_NAME_SPANISH,
      prefs::kSodaEsEsConfigPath,
      {0x9a, 0x22, 0xac, 0x04, 0x97, 0xc1, 0x70, 0x61, 0x24, 0x1f, 0x49,
       0x18, 0x72, 0xd8, 0x67, 0x31, 0x72, 0x7a, 0xf9, 0x77, 0x04, 0xf0,
       0x17, 0xb5, 0xfe, 0x88, 0xac, 0x60, 0xdd, 0x8a, 0x67, 0xdd}},
     {LanguageCode::kFrFr,
      "fr-FR",
-     IDS_SODA_LANGUAGE_DISPLAY_NAME_FRENCH,
      prefs::kSodaFrFrConfigPath,
      {0x6e, 0x0e, 0x2b, 0xd3, 0xc6, 0xe5, 0x1b, 0x5e, 0xfa, 0xef, 0x42,
       0x3f, 0x57, 0xb9, 0x2b, 0x13, 0x56, 0x47, 0x58, 0xdb, 0x76, 0x89,
       0x71, 0xeb, 0x1f, 0xed, 0x48, 0x6c, 0xac, 0xd5, 0x31, 0xa0}},
     {LanguageCode::kItIt,
      "it-IT",
-     IDS_SODA_LANGUAGE_DISPLAY_NAME_ITALIAN,
      prefs::kSodaItItConfigPath,
      {0x97, 0x45, 0xd7, 0xbc, 0xf0, 0x61, 0x24, 0xb3, 0x0e, 0x13, 0xf2,
       0x97, 0xaa, 0xd5, 0x9e, 0x78, 0xa5, 0x81, 0x35, 0x75, 0xb5, 0x9d,
       0x3b, 0xbb, 0xde, 0xba, 0x0e, 0xf7, 0xf0, 0x48, 0x56, 0x01}},
     {LanguageCode::kJaJp,
      "ja-JP",
-     IDS_SODA_LANGUAGE_DISPLAY_NAME_JAPANESE,
      prefs::kSodaJaJpConfigPath,
      {0xed, 0x7f, 0x96, 0xa5, 0x60, 0x9c, 0xaa, 0x4d, 0x80, 0xe5, 0xb8,
       0x26, 0xea, 0xf0, 0x41, 0x50, 0x09, 0x52, 0xa4, 0xb3, 0x1e, 0x6a,
@@ -164,7 +152,8 @@
 
 LanguageCode GetLanguageCode(const std::string& language_name);
 
-int GetLanguageDisplayName(const std::string& language_name);
+const std::u16string GetLanguageDisplayName(const std::string& language_name,
+                                            const std::string& display_locale);
 
 // Returns the `SodaInstaller.Language.{language}.InstallationSuccessTime` uma
 // metric string for the language code.
diff --git a/components/soda_strings.grdp b/components/soda_strings.grdp
deleted file mode 100644
index 998a4ac..0000000
--- a/components/soda_strings.grdp
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<grit-part>
-  <message name="IDS_SODA_LANGUAGE_DISPLAY_NAME_ENGLISH" desc="The display name of the SODA English language component.">
-    English
-  </message>
-  <message name="IDS_SODA_LANGUAGE_DISPLAY_NAME_FRENCH" desc="The display name of the SODA French language component.">
-    French
-  </message>
-  <message name="IDS_SODA_LANGUAGE_DISPLAY_NAME_GERMAN" desc="The display name of the SODA German language component.">
-    German
-  </message>
-  <message name="IDS_SODA_LANGUAGE_DISPLAY_NAME_ITALIAN" desc="The display name of the SODA Italian language component.">
-    Italian
-  </message>
-  <message name="IDS_SODA_LANGUAGE_DISPLAY_NAME_JAPANESE" desc="The display name of the SODA Japanese language component.">
-    Japanese
-  </message>
-  <message name="IDS_SODA_LANGUAGE_DISPLAY_NAME_SPANISH" desc="The display name of the SODA Spanish language component.">
-    Spanish
-  </message>
-</grit-part>
diff --git a/components/soda_strings_grdp/DIR_METADATA b/components/soda_strings_grdp/DIR_METADATA
deleted file mode 100644
index 6667e7f..0000000
--- a/components/soda_strings_grdp/DIR_METADATA
+++ /dev/null
@@ -1,3 +0,0 @@
-monorail {
-  component: "Internals>Media"
-}
diff --git a/components/soda_strings_grdp/IDS_SODA_LANGUAGE_DISPLAY_NAME_ENGLISH.png.sha1 b/components/soda_strings_grdp/IDS_SODA_LANGUAGE_DISPLAY_NAME_ENGLISH.png.sha1
deleted file mode 100644
index 7e1549b..0000000
--- a/components/soda_strings_grdp/IDS_SODA_LANGUAGE_DISPLAY_NAME_ENGLISH.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-0f9def9b80dae916d6c25c529c8fc8bba7995395
\ No newline at end of file
diff --git a/components/soda_strings_grdp/IDS_SODA_LANGUAGE_DISPLAY_NAME_FRENCH.png.sha1 b/components/soda_strings_grdp/IDS_SODA_LANGUAGE_DISPLAY_NAME_FRENCH.png.sha1
deleted file mode 100644
index 7e1549b..0000000
--- a/components/soda_strings_grdp/IDS_SODA_LANGUAGE_DISPLAY_NAME_FRENCH.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-0f9def9b80dae916d6c25c529c8fc8bba7995395
\ No newline at end of file
diff --git a/components/soda_strings_grdp/IDS_SODA_LANGUAGE_DISPLAY_NAME_GERMAN.png.sha1 b/components/soda_strings_grdp/IDS_SODA_LANGUAGE_DISPLAY_NAME_GERMAN.png.sha1
deleted file mode 100644
index 7e1549b..0000000
--- a/components/soda_strings_grdp/IDS_SODA_LANGUAGE_DISPLAY_NAME_GERMAN.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-0f9def9b80dae916d6c25c529c8fc8bba7995395
\ No newline at end of file
diff --git a/components/soda_strings_grdp/IDS_SODA_LANGUAGE_DISPLAY_NAME_ITALIAN.png.sha1 b/components/soda_strings_grdp/IDS_SODA_LANGUAGE_DISPLAY_NAME_ITALIAN.png.sha1
deleted file mode 100644
index 7e1549b..0000000
--- a/components/soda_strings_grdp/IDS_SODA_LANGUAGE_DISPLAY_NAME_ITALIAN.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-0f9def9b80dae916d6c25c529c8fc8bba7995395
\ No newline at end of file
diff --git a/components/soda_strings_grdp/IDS_SODA_LANGUAGE_DISPLAY_NAME_JAPANESE.png.sha1 b/components/soda_strings_grdp/IDS_SODA_LANGUAGE_DISPLAY_NAME_JAPANESE.png.sha1
deleted file mode 100644
index 7e1549b..0000000
--- a/components/soda_strings_grdp/IDS_SODA_LANGUAGE_DISPLAY_NAME_JAPANESE.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-0f9def9b80dae916d6c25c529c8fc8bba7995395
\ No newline at end of file
diff --git a/components/soda_strings_grdp/IDS_SODA_LANGUAGE_DISPLAY_NAME_SPANISH.png.sha1 b/components/soda_strings_grdp/IDS_SODA_LANGUAGE_DISPLAY_NAME_SPANISH.png.sha1
deleted file mode 100644
index 7e1549b..0000000
--- a/components/soda_strings_grdp/IDS_SODA_LANGUAGE_DISPLAY_NAME_SPANISH.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-0f9def9b80dae916d6c25c529c8fc8bba7995395
\ No newline at end of file
diff --git a/components/soda_strings_grdp/OWNERS b/components/soda_strings_grdp/OWNERS
deleted file mode 100644
index 57d73f8..0000000
--- a/components/soda_strings_grdp/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-file://components/soda/OWNERS
diff --git a/components/soda_strings_grdp/README.md b/components/soda_strings_grdp/README.md
deleted file mode 100644
index 119d8b9..0000000
--- a/components/soda_strings_grdp/README.md
+++ /dev/null
@@ -1,5 +0,0 @@
-This directory of image SHA-1 hashes is used to improve translations of UI
-strings through context images for translators.
-
-See also: [Chrome Translation Screenshots - Instructions & FAQ
-](https://docs.google.com/document/d/1nwYWDny20icMSpLUuV_LgrlbWKrYpbXOERUIZNH636o/edit#heading=h.2t7lc4cxo2au)
diff --git a/components/supervised_user/core/browser/web_content_handler.h b/components/supervised_user/core/browser/web_content_handler.h
index 7dfb766..90a552c 100644
--- a/components/supervised_user/core/browser/web_content_handler.h
+++ b/components/supervised_user/core/browser/web_content_handler.h
@@ -10,12 +10,7 @@
 #include "base/functional/callback.h"
 #include "base/time/time.h"
 
-namespace gfx {
-class ImageSkia;
-}  // namespace gfx
-
 class GURL;
-
 namespace supervised_user {
 
 class SupervisedUserSettingsService;
@@ -49,7 +44,6 @@
   virtual void RequestLocalApproval(
       const GURL& url,
       const std::u16string& child_display_name,
-      const gfx::ImageSkia& favicon,
       ApprovalRequestInitiatedCallback callback) = 0;
 
   static const char* GetLocalApprovalDurationMillisecondsHistogram();
diff --git a/components/translate/core/browser/translate_language_list.cc b/components/translate/core/browser/translate_language_list.cc
index 6934580..b1f0a92 100644
--- a/components/translate/core/browser/translate_language_list.cc
+++ b/components/translate/core/browser/translate_language_list.cc
@@ -9,10 +9,10 @@
 #include <iterator>
 
 #include "base/check.h"
+#include "base/debug/dump_without_crashing.h"
 #include "base/functional/bind.h"
 #include "base/json/json_reader.h"
 #include "base/lazy_instance.h"
-#include "base/notreached.h"
 #include "base/ranges/algorithm.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
@@ -327,7 +327,7 @@
 
   if (!json_value || !json_value->is_dict()) {
     NotifyEvent(__LINE__, "Language list is invalid");
-    NOTREACHED();
+    base::debug::DumpWithoutCrashing();
     return false;
   }
   // The first level dictionary contains two sub-dicts, first for source
@@ -337,7 +337,7 @@
       json_value->FindDictPath(TranslateLanguageList::kTargetLanguagesKey);
   if (!target_languages) {
     NotifyEvent(__LINE__, "Target languages are not found in the response");
-    NOTREACHED();
+    base::debug::DumpWithoutCrashing();
     return false;
   }
 
diff --git a/components/variations/android/variations_associated_data_android.cc b/components/variations/android/variations_associated_data_android.cc
index 55d58be..7035756 100644
--- a/components/variations/android/variations_associated_data_android.cc
+++ b/components/variations/android/variations_associated_data_android.cc
@@ -5,6 +5,7 @@
 #include <string>
 
 #include "base/android/jni_string.h"
+#include "base/metrics/field_trial_params.h"
 #include "components/variations/jni/VariationsAssociatedData_jni.h"
 #include "components/variations/variations_associated_data.h"
 #include "components/variations/variations_ids_provider.h"
@@ -24,7 +25,7 @@
   std::string trial_name(ConvertJavaStringToUTF8(env, jtrial_name));
   std::string param_name(ConvertJavaStringToUTF8(env, jparam_name));
   std::string param_value =
-      variations::GetVariationParamValue(trial_name, param_name);
+      base::GetFieldTrialParamValue(trial_name, param_name);
   return ConvertUTF8ToJavaString(env, param_value);
 }
 
diff --git a/components/variations/field_trial_config/field_trial_util_unittest.cc b/components/variations/field_trial_config/field_trial_util_unittest.cc
index 9bcf0a16..356aa452 100644
--- a/components/variations/field_trial_config/field_trial_util_unittest.cc
+++ b/components/variations/field_trial_config/field_trial_util_unittest.cc
@@ -115,9 +115,9 @@
   ASSERT_TRUE(AssociateParamsFromString(kVariationsString));
 
   base::FieldTrialList::CreateFieldTrial(kTrialName, "B");
-  EXPECT_EQ("/", GetVariationParamValue(kTrialName, "a"));
-  EXPECT_EQ(std::string(), GetVariationParamValue(kTrialName, "b"));
-  EXPECT_EQ(std::string(), GetVariationParamValue(kTrialName, "x"));
+  EXPECT_EQ("/", base::GetFieldTrialParamValue(kTrialName, "a"));
+  EXPECT_EQ(std::string(), base::GetFieldTrialParamValue(kTrialName, "b"));
+  EXPECT_EQ(std::string(), base::GetFieldTrialParamValue(kTrialName, "x"));
 
   std::map<std::string, std::string> params;
   EXPECT_TRUE(base::GetFieldTrialParams(kTrialName, &params));
@@ -203,8 +203,8 @@
       kConfig, override_callback_.callback(), platform,
       variation_service_client_.GetCurrentFormFactor(), &feature_list);
 
-  EXPECT_EQ("1", GetVariationParamValue("TestTrial1", "x"));
-  EXPECT_EQ("2", GetVariationParamValue("TestTrial1", "y"));
+  EXPECT_EQ("1", base::GetFieldTrialParamValue("TestTrial1", "x"));
+  EXPECT_EQ("2", base::GetFieldTrialParamValue("TestTrial1", "y"));
 
   std::map<std::string, std::string> params;
   EXPECT_TRUE(base::GetFieldTrialParams("TestTrial1", &params));
@@ -407,8 +407,8 @@
         kConfig, override_callback_.callback(), platform,
         variation_service_client_.GetCurrentFormFactor(), &feature_list);
 
-    EXPECT_EQ("1", GetVariationParamValue("TestTrial", "x"));
-    EXPECT_EQ("2", GetVariationParamValue("TestTrial", "y"));
+    EXPECT_EQ("1", base::GetFieldTrialParamValue("TestTrial", "x"));
+    EXPECT_EQ("2", base::GetFieldTrialParamValue("TestTrial", "y"));
 
     std::map<std::string, std::string> params;
     EXPECT_TRUE(base::GetFieldTrialParams("TestTrial", &params));
@@ -454,8 +454,8 @@
       kConfig, override_callback_.callback(), Study::PLATFORM_ANDROID_WEBVIEW,
       variation_service_client_.GetCurrentFormFactor(), &feature_list);
 
-  EXPECT_EQ("", GetVariationParamValue("TestTrial", "x"));
-  EXPECT_EQ("", GetVariationParamValue("TestTrial", "y"));
+  EXPECT_EQ("", base::GetFieldTrialParamValue("TestTrial", "x"));
+  EXPECT_EQ("", base::GetFieldTrialParamValue("TestTrial", "y"));
 
   std::map<std::string, std::string> params;
   EXPECT_FALSE(base::GetFieldTrialParams("TestTrial", &params));
@@ -498,8 +498,8 @@
       kConfig, override_callback_.callback(), Study::PLATFORM_ANDROID_WEBVIEW,
       variation_service_client_.GetCurrentFormFactor(), &feature_list);
 
-  EXPECT_EQ("1", GetVariationParamValue("TestTrial", "x"));
-  EXPECT_EQ("2", GetVariationParamValue("TestTrial", "y"));
+  EXPECT_EQ("1", base::GetFieldTrialParamValue("TestTrial", "x"));
+  EXPECT_EQ("2", base::GetFieldTrialParamValue("TestTrial", "y"));
 
   std::map<std::string, std::string> params;
   EXPECT_TRUE(base::GetFieldTrialParams("TestTrial", &params));
@@ -537,8 +537,8 @@
       kConfig, override_callback_.callback(), platform,
       variation_service_client_.GetCurrentFormFactor(), &feature_list);
 
-  EXPECT_EQ("1", GetVariationParamValue("TestTrial", "x"));
-  EXPECT_EQ("2", GetVariationParamValue("TestTrial", "y"));
+  EXPECT_EQ("1", base::GetFieldTrialParamValue("TestTrial", "x"));
+  EXPECT_EQ("2", base::GetFieldTrialParamValue("TestTrial", "y"));
 
   std::map<std::string, std::string> params;
   EXPECT_TRUE(base::GetFieldTrialParams("TestTrial", &params));
@@ -574,8 +574,8 @@
       kConfig, override_callback_.callback(), platform,
       variation_service_client_.GetCurrentFormFactor(), &feature_list);
 
-  EXPECT_EQ("1", GetVariationParamValue("TestTrial", "x"));
-  EXPECT_EQ("2", GetVariationParamValue("TestTrial", "y"));
+  EXPECT_EQ("1", base::GetFieldTrialParamValue("TestTrial", "x"));
+  EXPECT_EQ("2", base::GetFieldTrialParamValue("TestTrial", "y"));
 
   std::map<std::string, std::string> params;
   EXPECT_TRUE(base::GetFieldTrialParams("TestTrial", &params));
@@ -618,8 +618,8 @@
         kConfig, override_callback_.callback(), Study::PLATFORM_ANDROID_WEBVIEW,
         variation_service_client_.GetCurrentFormFactor(), &feature_list);
 
-    EXPECT_EQ("", GetVariationParamValue("TestTrial", "x"));
-    EXPECT_EQ("", GetVariationParamValue("TestTrial", "y"));
+    EXPECT_EQ("", base::GetFieldTrialParamValue("TestTrial", "x"));
+    EXPECT_EQ("", base::GetFieldTrialParamValue("TestTrial", "y"));
 
     std::map<std::string, std::string> params;
     EXPECT_FALSE(base::GetFieldTrialParams("TestTrial", &params));
@@ -870,8 +870,8 @@
       kConfig, override_callback_.callback(), platform,
       variation_service_client_.GetCurrentFormFactor(), &feature_list);
 
-  EXPECT_EQ("1", GetVariationParamValue("TestTrial", "x"));
-  EXPECT_EQ("2", GetVariationParamValue("TestTrial", "y"));
+  EXPECT_EQ("1", base::GetFieldTrialParamValue("TestTrial", "x"));
+  EXPECT_EQ("2", base::GetFieldTrialParamValue("TestTrial", "y"));
 
   std::map<std::string, std::string> params;
   EXPECT_TRUE(base::GetFieldTrialParams("TestTrial", &params));
@@ -920,8 +920,8 @@
       kConfig, override_callback_.callback(), platform,
       variation_service_client_.GetCurrentFormFactor(), &feature_list);
 
-  EXPECT_EQ("1", GetVariationParamValue("TestTrial", "x"));
-  EXPECT_EQ("2", GetVariationParamValue("TestTrial", "y"));
+  EXPECT_EQ("1", base::GetFieldTrialParamValue("TestTrial", "x"));
+  EXPECT_EQ("2", base::GetFieldTrialParamValue("TestTrial", "y"));
 
   std::map<std::string, std::string> params;
   EXPECT_TRUE(base::GetFieldTrialParams("TestTrial", &params));
@@ -963,8 +963,8 @@
       kConfig, override_callback_.callback(), platform,
       variation_service_client_.GetCurrentFormFactor(), &feature_list);
 
-  EXPECT_EQ("", GetVariationParamValue("TestTrial", "x"));
-  EXPECT_EQ("", GetVariationParamValue("TestTrial", "y"));
+  EXPECT_EQ("", base::GetFieldTrialParamValue("TestTrial", "x"));
+  EXPECT_EQ("", base::GetFieldTrialParamValue("TestTrial", "y"));
 
   std::map<std::string, std::string> params;
   EXPECT_FALSE(base::GetFieldTrialParams("TestTrial", &params));
@@ -1005,8 +1005,8 @@
       kConfig, override_callback_.callback(), platform,
       variation_service_client_.GetCurrentFormFactor(), &feature_list);
 
-  EXPECT_EQ("1", GetVariationParamValue("TestTrial", "x"));
-  EXPECT_EQ("2", GetVariationParamValue("TestTrial", "y"));
+  EXPECT_EQ("1", base::GetFieldTrialParamValue("TestTrial", "x"));
+  EXPECT_EQ("2", base::GetFieldTrialParamValue("TestTrial", "y"));
 
   std::map<std::string, std::string> params;
   EXPECT_TRUE(base::GetFieldTrialParams("TestTrial", &params));
@@ -1052,8 +1052,8 @@
       kConfig, override_callback_.callback(), platform,
       variation_service_client_.GetCurrentFormFactor(), &feature_list);
 
-  EXPECT_EQ("", GetVariationParamValue("TestTrial", "x"));
-  EXPECT_EQ("", GetVariationParamValue("TestTrial", "y"));
+  EXPECT_EQ("", base::GetFieldTrialParamValue("TestTrial", "x"));
+  EXPECT_EQ("", base::GetFieldTrialParamValue("TestTrial", "y"));
 
   std::map<std::string, std::string> params;
   EXPECT_FALSE(base::GetFieldTrialParams("TestTrial", &params));
diff --git a/components/variations/variations_associated_data.cc b/components/variations/variations_associated_data.cc
index 26b3bc5..1dfda64 100644
--- a/components/variations/variations_associated_data.cc
+++ b/components/variations/variations_associated_data.cc
@@ -158,11 +158,6 @@
   return base::AssociateFieldTrialParams(trial_name, group_name, params);
 }
 
-std::string GetVariationParamValue(const std::string& trial_name,
-                                   const std::string& param_name) {
-  return base::GetFieldTrialParamValue(trial_name, param_name);
-}
-
 // Functions below are exposed for testing explicitly behind this namespace.
 // They simply wrap existing functions in this file.
 namespace testing {
diff --git a/components/variations/variations_associated_data.h b/components/variations/variations_associated_data.h
index 2674d0a..d5e04ae2 100644
--- a/components/variations/variations_associated_data.h
+++ b/components/variations/variations_associated_data.h
@@ -33,7 +33,7 @@
 //    // use |params|
 //  }
 //
-//  std::string value = GetVariationParamValue("trial", "param_x");
+//  std::string value = base::GetFieldTrialParamValue("trial", "param_x");
 //  // use |value|, which will be "" if it does not exist
 //
 // VariationID id = GetGoogleVariationID(
@@ -126,11 +126,6 @@
                               const std::string& group_name,
                               const std::map<std::string, std::string>& params);
 
-// Deprecated. Use base::GetFieldTrialParamValue() instead.
-COMPONENT_EXPORT(VARIATIONS)
-std::string GetVariationParamValue(const std::string& trial_name,
-                                   const std::string& param_name);
-
 // Expose some functions for testing.
 namespace testing {
 
diff --git a/components/variations/variations_request_scheduler.cc b/components/variations/variations_request_scheduler.cc
index 69805d3..19a16b40 100644
--- a/components/variations/variations_request_scheduler.cc
+++ b/components/variations/variations_request_scheduler.cc
@@ -6,6 +6,7 @@
 
 #include <stddef.h>
 
+#include "base/metrics/field_trial_params.h"
 #include "base/strings/string_number_conversions.h"
 #include "build/build_config.h"
 #include "components/variations/variations_associated_data.h"
@@ -93,8 +94,8 @@
     return overridden_period.value();
 
   // The fetch interval can be overridden by a variation param.
-  std::string period_min_str =
-      GetVariationParamValue("VariationsServiceControl", "fetch_period_min");
+  std::string period_min_str = base::GetFieldTrialParamValue(
+      "VariationsServiceControl", "fetch_period_min");
   size_t period_min;
   if (base::StringToSizeT(period_min_str, &period_min))
     return base::Minutes(period_min);
diff --git a/components/variations/variations_seed_processor_unittest.cc b/components/variations/variations_seed_processor_unittest.cc
index f11fbed..870fe9a 100644
--- a/components/variations/variations_seed_processor_unittest.cc
+++ b/components/variations/variations_seed_processor_unittest.cc
@@ -18,6 +18,7 @@
 #include "base/format_macros.h"
 #include "base/functional/bind.h"
 #include "base/functional/callback_helpers.h"
+#include "base/metrics/field_trial_params.h"
 #include "base/strings/string_split.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
@@ -407,13 +408,13 @@
   Study::Experiment* experiment2 = AddExperiment("B", 0, study);
 
   this->CreateTrialsFromSeed(seed);
-  EXPECT_EQ("y", GetVariationParamValue("Study1", "x"));
+  EXPECT_EQ("y", base::GetFieldTrialParamValue("Study1", "x"));
 
   study->set_name("Study2");
   experiment1->set_probability_weight(0);
   experiment2->set_probability_weight(1);
   this->CreateTrialsFromSeed(seed);
-  EXPECT_EQ(std::string(), GetVariationParamValue("Study2", "x"));
+  EXPECT_EQ(std::string(), base::GetFieldTrialParamValue("Study2", "x"));
 }
 
 TYPED_TEST(VariationsSeedProcessorTest, VariationParamsWithForcingFlag) {
@@ -427,7 +428,7 @@
   base::CommandLine::ForCurrentProcess()->AppendSwitch(kForcingFlag1);
   this->CreateTrialsFromSeed(seed);
   EXPECT_EQ(kFlagGroup1Name, base::FieldTrialList::FindFullName(study->name()));
-  EXPECT_EQ("y", GetVariationParamValue(study->name(), "x"));
+  EXPECT_EQ("y", base::GetFieldTrialParamValue(study->name(), "x"));
 }
 
 TYPED_TEST(VariationsSeedProcessorTest, StartsActive) {
@@ -503,7 +504,7 @@
             base::FieldTrialList::FindFullName(study->name()));
 
   // Check that params and experiment ids correspond.
-  EXPECT_EQ("y", GetVariationParamValue(study->name(), "x"));
+  EXPECT_EQ("y", base::GetFieldTrialParamValue(study->name(), "x"));
   VariationID id = GetGoogleVariationID(GOOGLE_WEB_PROPERTIES_ANY_CONTEXT,
                                         kFlagStudyName, kNonFlagGroupName);
   EXPECT_EQ(kExperimentId, id);
diff --git a/components/viz/service/display/resolved_frame_data.cc b/components/viz/service/display/resolved_frame_data.cc
index 641c102..e27f4cbe 100644
--- a/components/viz/service/display/resolved_frame_data.cc
+++ b/components/viz/service/display/resolved_frame_data.cc
@@ -269,16 +269,6 @@
 gfx::Rect ResolvedFrameData::GetSurfaceDamage() const {
   DCHECK(valid_);
 
-  // The |damage_rect| set in |SurfaceAnimationManager| is the |output_rect|.
-  // However, we dont use |damage_rect| because when we transition from
-  // interpolated frame we would end up using the |damage_rect| from the
-  // original non interpolated frame.
-  // TODO(vmpstr): This damage may be too large, but I think it's hard to figure
-  // out a small bounds on the damage given an animation that happens in
-  // SurfaceAnimationManager.
-  if (surface_->HasSurfaceAnimationDamage())
-    return GetOutputRect();
-
   if (IsSameFrameAsLastAggregation()) {
     return gfx::Rect();
   } else if (IsNextFrameSinceLastAggregation()) {
diff --git a/components/viz/service/display/surface_aggregator.cc b/components/viz/service/display/surface_aggregator.cc
index d521e118..4f82f6b 100644
--- a/components/viz/service/display/surface_aggregator.cc
+++ b/components/viz/service/display/surface_aggregator.cc
@@ -407,12 +407,6 @@
     const gfx::Transform& parent_target_transform,
     const Surface* surface,
     size_t* overlay_damage_index) {
-  // If we have damage from a surface animation, then we shouldn't have an
-  // overlay candidate from the root render pass, since that's an interpolated
-  // pass with "artificial" damage.
-  if (surface->HasSurfaceAnimationDamage())
-    return nullptr;
-
   // Only process the damage rect at the root render pass, once per surface.
   const CompositorFrame& frame = surface->GetActiveFrame();
   bool is_last_pass_on_src_surface =
@@ -552,8 +546,7 @@
     // If there is a new CompositorFrame for `surface` compute resolved frame
     // data for the new resolved CompositorFrame.
     if (resolved_frame.previous_frame_index() !=
-            surface->GetActiveFrameIndex() ||
-        surface->HasSurfaceAnimationDamage()) {
+        surface->GetActiveFrameIndex()) {
       base::ElapsedTimer timer;
       ProcessResolvedFrame(resolved_frame);
       stats_->declare_resources_time += timer.Elapsed();
@@ -912,7 +905,6 @@
   }
 
   referenced_surfaces_.erase(surface_id);
-  surface->DidAggregate();
 }
 
 void SurfaceAggregator::EmitDefaultBackgroundColorQuad(
diff --git a/components/viz/service/surfaces/surface.cc b/components/viz/service/surfaces/surface.cc
index 1e477e96..54ac755 100644
--- a/components/viz/service/surfaces/surface.cc
+++ b/components/viz/service/surfaces/surface.cc
@@ -732,23 +732,10 @@
   return active_frame_data_->frame.metadata;
 }
 
-void Surface::ResetInterpolatedFrame() {
-  interpolated_frame_.reset();
-  has_damage_from_interpolated_frame_ = true;
-}
-
 void Surface::SetInterpolatedFrame(CompositorFrame frame) {
   interpolated_frame_.emplace(std::move(frame));
 }
 
-bool Surface::HasSurfaceAnimationDamage() const {
-  return interpolated_frame_.has_value() || has_damage_from_interpolated_frame_;
-}
-
-void Surface::DidAggregate() {
-  has_damage_from_interpolated_frame_ = false;
-}
-
 const CompositorFrame& Surface::GetPendingFrame() {
   DCHECK(pending_frame_data_);
   return pending_frame_data_->frame;
diff --git a/components/viz/service/surfaces/surface.h b/components/viz/service/surfaces/surface.h
index 16ed8468..d7630fa8 100644
--- a/components/viz/service/surfaces/surface.h
+++ b/components/viz/service/surfaces/surface.h
@@ -202,7 +202,6 @@
   const CompositorFrame& GetActiveFrame() const;
   const CompositorFrameMetadata& GetActiveFrameMetadata() const;
 
-  void ResetInterpolatedFrame();
   void SetInterpolatedFrame(CompositorFrame frame);
   const CompositorFrame& GetActiveOrInterpolatedFrame() const;
   bool HasInterpolatedFrame() const;
@@ -311,8 +310,6 @@
       std::unique_ptr<CopyOutputRequest> copy_request,
       CompositorRenderPassId render_pass_id);
 
-  void DidAggregate();
-
   // Returns frame id of the oldest uncommitted frame if any,
   absl::optional<BeginFrameId> GetFirstUncommitedFrameId();
 
@@ -440,8 +437,6 @@
 
   const raw_ptr<SurfaceAllocationGroup> allocation_group_;
 
-  bool has_damage_from_interpolated_frame_ = false;
-
   const size_t max_uncommitted_frames_;
 
   base::WeakPtrFactory<Surface> weak_factory_{this};
diff --git a/components/webapps/browser/banners/app_banner_settings_helper.cc b/components/webapps/browser/banners/app_banner_settings_helper.cc
index f71cdf9a..9679ee41 100644
--- a/components/webapps/browser/banners/app_banner_settings_helper.cc
+++ b/components/webapps/browser/banners/app_banner_settings_helper.cc
@@ -15,12 +15,12 @@
 #include "base/json/values_util.h"
 #include "base/memory/raw_ptr.h"
 #include "base/memory/raw_ref.h"
+#include "base/metrics/field_trial_params.h"
 #include "base/strings/string_number_conversions.h"
 #include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "components/content_settings/core/common/content_settings_pattern.h"
 #include "components/content_settings/core/common/content_settings_utils.h"
 #include "components/permissions/permissions_client.h"
-#include "components/variations/variations_associated_data.h"
 #include "components/webapps/browser/banners/app_banner_manager.h"
 #include "components/webapps/browser/banners/app_banner_metrics.h"
 #include "components/webapps/common/switches.h"
@@ -134,9 +134,9 @@
 // Queries variations for the number of days which dismissing and ignoring the
 // banner should prevent a banner from showing.
 void UpdateDaysBetweenShowing() {
-  std::string dismiss_param = variations::GetVariationParamValue(
+  std::string dismiss_param = base::GetFieldTrialParamValue(
       kBannerParamsKey, kBannerParamsDaysAfterBannerDismissedKey);
-  std::string ignore_param = variations::GetVariationParamValue(
+  std::string ignore_param = base::GetFieldTrialParamValue(
       kBannerParamsKey, kBannerParamsDaysAfterBannerIgnoredKey);
 
   if (!dismiss_param.empty() && !ignore_param.empty()) {
@@ -154,7 +154,7 @@
 // Queries variations for the maximum site engagement score required to trigger
 // the banner showing.
 void UpdateSiteEngagementToTrigger() {
-  std::string total_param = variations::GetVariationParamValue(
+  std::string total_param = base::GetFieldTrialParamValue(
       kBannerParamsKey, kBannerParamsEngagementTotalKey);
 
   if (!total_param.empty()) {
diff --git a/content/browser/bad_message.h b/content/browser/bad_message.h
index 5378451..b6087d7 100644
--- a/content/browser/bad_message.h
+++ b/content/browser/bad_message.h
@@ -324,6 +324,8 @@
   BIBI_BIND_PRESSURE_MANAGER_BLOCKED_BY_PERMISSIONS_POLICY = 297,
   RFSCI_BROWSER_VALIDATION_BAD_ORIGIN_TRIAL_TOKEN = 298,
   RFH_RECEIVED_INVALID_BROWSING_TOPICS_ATTRIBUTE = 299,
+  RFHI_FULLSCREEN_NAV_INVALID_INITIAL_DOCUMENT = 300,
+  RFHI_FULLSCREEN_NAV_NOT_OUTERMOST_MAIN_FRAME = 301,
 
   // Please add new elements here. The naming convention is abbreviated class
   // name (e.g. RenderFrameHost becomes RFH) plus a unique description of the
diff --git a/content/browser/devtools/protocol/fedcm_handler.cc b/content/browser/devtools/protocol/fedcm_handler.cc
index e80e0e6..f07766eb 100644
--- a/content/browser/devtools/protocol/fedcm_handler.cc
+++ b/content/browser/devtools/protocol/fedcm_handler.cc
@@ -111,7 +111,15 @@
       accounts->push_back(std::move(entry));
     }
   }
-  frontend_->DialogShown(dialog_id_, std::move(accounts));
+  IdentityRequestDialogController* dialog = auth_request->GetDialogController();
+  CHECK(dialog);
+  Maybe<String> maybe_subtitle;
+  absl::optional<std::string> subtitle = dialog->GetSubtitle();
+  if (subtitle) {
+    maybe_subtitle = *subtitle;
+  }
+  frontend_->DialogShown(dialog_id_, std::move(accounts), dialog->GetTitle(),
+                         std::move(maybe_subtitle));
 }
 
 DispatchResponse FedCmHandler::SelectAccount(const String& in_dialogId,
diff --git a/content/browser/devtools/protocol/storage_handler.cc b/content/browser/devtools/protocol/storage_handler.cc
index a75318b..1b5a97b 100644
--- a/content/browser/devtools/protocol/storage_handler.cc
+++ b/content/browser/devtools/protocol/storage_handler.cc
@@ -10,7 +10,9 @@
 #include <vector>
 
 #include "base/functional/bind.h"
+#include "base/notreached.h"
 #include "base/scoped_observation.h"
+#include "base/strings/string_number_conversions.h"
 #include "base/strings/string_split.h"
 #include "base/strings/utf_string_conversions.h"
 #include "components/services/storage/privileged/mojom/indexed_db_control.mojom.h"
@@ -29,11 +31,14 @@
 #include "content/public/browser/storage_partition.h"
 #include "services/network/public/mojom/trust_tokens.mojom.h"
 #include "storage/browser/quota/quota_manager.h"
+#include "storage/browser/quota/quota_manager_impl.h"
+#include "storage/browser/quota/quota_manager_observer.mojom-forward.h"
 #include "storage/browser/quota/quota_manager_proxy.h"
 #include "storage/browser/quota/quota_override_handle.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/blink/public/common/interest_group/interest_group.h"
 #include "third_party/blink/public/common/storage_key/storage_key.h"
+#include "third_party/blink/public/mojom/buckets/bucket_manager_host.mojom-shared.h"
 #include "third_party/blink/public/mojom/quota/quota_types.mojom.h"
 #include "url/gurl.h"
 #include "url/origin.h"
@@ -118,6 +123,30 @@
       base::BindOnce(&GotUsageAndQuotaDataCallback, std::move(callback)));
 }
 
+std::unique_ptr<protocol::Storage::StorageBucketInfo> BuildBucketInfo(
+    const storage::BucketInfo& bucket) {
+  std::string durability_enum;
+  switch (bucket.durability) {
+    case blink::mojom::BucketDurability::kRelaxed:
+      durability_enum = Storage::StorageBucketsDurabilityEnum::Relaxed;
+      break;
+    case blink::mojom::BucketDurability::kStrict:
+      durability_enum = Storage::StorageBucketsDurabilityEnum::Strict;
+      break;
+  }
+
+  return protocol::Storage::StorageBucketInfo::Create()
+      .SetStorageKey(bucket.storage_key.Serialize())
+      .SetId(base::NumberToString(bucket.id.value()))
+      .SetName(bucket.name)
+      .SetIsDefault(bucket.is_default())
+      .SetExpiration(bucket.expiration.ToDoubleT())
+      .SetQuota(bucket.quota)
+      .SetPersistent(bucket.persistent)
+      .SetDurability(durability_enum)
+      .Build();
+}
+
 }  // namespace
 
 // Observer that listens on the UI thread for cache storage notifications and
@@ -140,8 +169,9 @@
 
   void TrackStorageKey(const blink::StorageKey& storage_key) {
     DCHECK_CURRENTLY_ON(BrowserThread::UI);
-    if (storage_keys_.find(storage_key) != storage_keys_.end())
+    if (storage_keys_.find(storage_key) != storage_keys_.end()) {
       return;
+    }
     storage_keys_.insert(storage_key);
   }
 
@@ -153,16 +183,18 @@
   void OnCacheListChanged(const blink::StorageKey& storage_key) override {
     DCHECK_CURRENTLY_ON(BrowserThread::UI);
     auto found = storage_keys_.find(storage_key);
-    if (found == storage_keys_.end())
+    if (found == storage_keys_.end()) {
       return;
+    }
     owner_->NotifyCacheStorageListChanged(storage_key);
   }
 
   void OnCacheContentChanged(const blink::StorageKey& storage_key,
                              const std::string& cache_name) override {
     DCHECK_CURRENTLY_ON(BrowserThread::UI);
-    if (storage_keys_.find(storage_key) == storage_keys_.end())
+    if (storage_keys_.find(storage_key) == storage_keys_.end()) {
       return;
+    }
     owner_->NotifyCacheStorageContentChanged(storage_key, cache_name);
   }
 
@@ -196,8 +228,9 @@
 
   void TrackStorageKey(const blink::StorageKey& storage_key) {
     DCHECK_CURRENTLY_ON(BrowserThread::UI);
-    if (storage_keys_.find(storage_key) != storage_keys_.end())
+    if (storage_keys_.find(storage_key) != storage_keys_.end()) {
       return;
+    }
     storage_keys_.insert(storage_key);
   }
 
@@ -209,12 +242,14 @@
   void OnIndexedDBListChanged(
       const storage::BucketLocator& bucket_locator) override {
     DCHECK_CURRENTLY_ON(BrowserThread::UI);
-    if (!owner_)
+    if (!owner_) {
       return;
+    }
     // TODO(crbug.com/1315371): Allow custom bucket names.
     auto found = storage_keys_.find(bucket_locator.storage_key);
-    if (found == storage_keys_.end())
+    if (found == storage_keys_.end()) {
       return;
+    }
 
     owner_->NotifyIndexedDBListChanged(
         bucket_locator.storage_key.origin().Serialize(),
@@ -226,12 +261,14 @@
       const std::u16string& database_name,
       const std::u16string& object_store_name) override {
     DCHECK_CURRENTLY_ON(BrowserThread::UI);
-    if (!owner_)
+    if (!owner_) {
       return;
+    }
     // TODO(crbug.com/1315371): Allow custom bucket names.
     auto found = storage_keys_.find(bucket_locator.storage_key);
-    if (found == storage_keys_.end())
+    if (found == storage_keys_.end()) {
       return;
+    }
 
     owner_->NotifyIndexedDBContentChanged(
         bucket_locator.storage_key.origin().Serialize(),
@@ -242,8 +279,9 @@
  private:
   void ReconnectObserver() {
     DCHECK(!receiver_.is_bound());
-    if (!owner_)
+    if (!owner_) {
       return;
+    }
 
     auto& control = owner_->storage_partition_->GetIndexedDBControl();
     mojo::PendingRemote<storage::mojom::IndexedDBObserver> remote;
@@ -307,6 +345,69 @@
       scoped_observation_{this};
 };
 
+class StorageHandler::QuotaManagerObserver
+    : storage::mojom::QuotaManagerObserver {
+ public:
+  QuotaManagerObserver(base::WeakPtr<StorageHandler> owner_storage_handler,
+                       storage::QuotaManagerProxy* quota_manager_proxy)
+      : owner_(owner_storage_handler) {
+    quota_manager_proxy->AddObserver(receiver_.BindNewPipeAndPassRemote());
+  }
+
+  QuotaManagerObserver(const QuotaManagerObserver&) = delete;
+  QuotaManagerObserver& operator=(const QuotaManagerObserver&) = delete;
+
+  ~QuotaManagerObserver() override = default;
+
+  void TrackStorageKey(const blink::StorageKey& storage_key,
+                       storage::QuotaManagerProxy* manager) {
+    if (!storage_keys_.insert(storage_key).second) {
+      return;
+    }
+    manager->GetBucketsForStorageKey(
+        storage_key, blink::mojom::StorageType::kTemporary, false,
+        base::SingleThreadTaskRunner::GetCurrentDefault(),
+        base::BindOnce(
+            [](base::WeakPtr<StorageHandler> owner_storage_handler,
+               storage::QuotaErrorOr<std::set<storage::BucketInfo>> buckets) {
+              if (!buckets.has_value()) {
+                return;
+              }
+
+              for (const storage::BucketInfo& bucket : buckets.value()) {
+                owner_storage_handler->NotifyCreateOrUpdateBucket(bucket);
+              }
+            },
+            owner_));
+  }
+
+  void UntrackStorageKey(const blink::StorageKey& storage_key) {
+    storage_keys_.erase(storage_key);
+  }
+
+ private:
+  void OnCreateOrUpdateBucket(const storage::BucketInfo& bucket_info) override {
+    auto found = storage_keys_.find(bucket_info.storage_key);
+    if (found == storage_keys_.end()) {
+      return;
+    }
+    owner_->NotifyCreateOrUpdateBucket(bucket_info);
+  }
+
+  void OnDeleteBucket(const storage::BucketLocator& bucket_locator) override {
+    auto found = storage_keys_.find(bucket_locator.storage_key);
+    if (found == storage_keys_.end()) {
+      return;
+    }
+    owner_->NotifyDeleteBucket(bucket_locator);
+  }
+
+  base::flat_set<blink::StorageKey> storage_keys_;
+
+  base::WeakPtr<StorageHandler> owner_;
+  mojo::Receiver<storage::mojom::QuotaManagerObserver> receiver_{this};
+};
+
 StorageHandler::StorageHandler(bool client_is_trusted)
     : DevToolsDomainHandler(Storage::Metainfo::domainName),
       client_is_trusted_(client_is_trusted) {}
@@ -334,13 +435,15 @@
   quota_override_handle_.reset();
   SetInterestGroupTracking(false);
   shared_storage_observer_.reset();
+  quota_manager_observer_.reset();
   return Response::Success();
 }
 
 void StorageHandler::GetCookies(Maybe<std::string> browser_context_id,
                                 std::unique_ptr<GetCookiesCallback> callback) {
-  if (!client_is_trusted_)
+  if (!client_is_trusted_) {
     callback->sendFailure(Response::ServerError("Permission denied"));
+  }
   StoragePartition* storage_partition = nullptr;
   Response response = StorageHandler::FindStoragePartition(browser_context_id,
                                                            &storage_partition);
@@ -405,17 +508,20 @@
 Response StorageHandler::GetStorageKeyForFrame(
     const std::string& frame_id,
     std::string* serialized_storage_key) {
-  if (!frame_host_)
+  if (!frame_host_) {
     return Response::InvalidParams("Frame host not found");
+  }
   FrameTreeNode* node = protocol::FrameTreeNodeFromDevToolsFrameToken(
       frame_host_->frame_tree_node(), frame_id);
-  if (!node)
+  if (!node) {
     return Response::InvalidParams("Frame tree node for given frame not found");
+  }
   RenderFrameHostImpl* rfh = node->current_frame_host();
-  if (rfh->storage_key().origin().opaque())
+  if (rfh->storage_key().origin().opaque()) {
     return Response::ServerError(
         "Frame corresponds to an opaque origin and its storage key cannot be "
         "serialized");
+  }
   *serialized_storage_key = rfh->storage_key().Serialize();
   return Response::Success();
 }
@@ -426,28 +532,39 @@
       storage_types, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
   std::unordered_set<std::string> set(types.begin(), types.end());
   uint32_t remove_mask = 0;
-  if (set.count(Storage::StorageTypeEnum::Cookies))
+  if (set.count(Storage::StorageTypeEnum::Cookies)) {
     remove_mask |= StoragePartition::REMOVE_DATA_MASK_COOKIES;
-  if (set.count(Storage::StorageTypeEnum::File_systems))
+  }
+  if (set.count(Storage::StorageTypeEnum::File_systems)) {
     remove_mask |= StoragePartition::REMOVE_DATA_MASK_FILE_SYSTEMS;
-  if (set.count(Storage::StorageTypeEnum::Indexeddb))
+  }
+  if (set.count(Storage::StorageTypeEnum::Indexeddb)) {
     remove_mask |= StoragePartition::REMOVE_DATA_MASK_INDEXEDDB;
-  if (set.count(Storage::StorageTypeEnum::Local_storage))
+  }
+  if (set.count(Storage::StorageTypeEnum::Local_storage)) {
     remove_mask |= StoragePartition::REMOVE_DATA_MASK_LOCAL_STORAGE;
-  if (set.count(Storage::StorageTypeEnum::Shader_cache))
+  }
+  if (set.count(Storage::StorageTypeEnum::Shader_cache)) {
     remove_mask |= StoragePartition::REMOVE_DATA_MASK_SHADER_CACHE;
-  if (set.count(Storage::StorageTypeEnum::Websql))
+  }
+  if (set.count(Storage::StorageTypeEnum::Websql)) {
     remove_mask |= StoragePartition::REMOVE_DATA_MASK_WEBSQL;
-  if (set.count(Storage::StorageTypeEnum::Service_workers))
+  }
+  if (set.count(Storage::StorageTypeEnum::Service_workers)) {
     remove_mask |= StoragePartition::REMOVE_DATA_MASK_SERVICE_WORKERS;
-  if (set.count(Storage::StorageTypeEnum::Cache_storage))
+  }
+  if (set.count(Storage::StorageTypeEnum::Cache_storage)) {
     remove_mask |= StoragePartition::REMOVE_DATA_MASK_CACHE_STORAGE;
-  if (set.count(Storage::StorageTypeEnum::Interest_groups))
+  }
+  if (set.count(Storage::StorageTypeEnum::Interest_groups)) {
     remove_mask |= StoragePartition::REMOVE_DATA_MASK_INTEREST_GROUPS;
-  if (set.count(Storage::StorageTypeEnum::Shared_storage))
+  }
+  if (set.count(Storage::StorageTypeEnum::Shared_storage)) {
     remove_mask |= StoragePartition::REMOVE_DATA_MASK_SHARED_STORAGE;
-  if (set.count(Storage::StorageTypeEnum::All))
+  }
+  if (set.count(Storage::StorageTypeEnum::All)) {
     remove_mask |= StoragePartition::REMOVE_DATA_MASK_ALL;
+  }
   return remove_mask;
 }
 }  // namespace
@@ -456,8 +573,9 @@
     const std::string& origin,
     const std::string& storage_types,
     std::unique_ptr<ClearDataForOriginCallback> callback) {
-  if (!storage_partition_)
+  if (!storage_partition_) {
     return callback->sendFailure(Response::InternalError());
+  }
 
   uint32_t remove_mask = GetRemoveDataMask(storage_types);
 
@@ -478,8 +596,9 @@
     const std::string& storage_key,
     const std::string& storage_types,
     std::unique_ptr<ClearDataForStorageKeyCallback> callback) {
-  if (!storage_partition_)
+  if (!storage_partition_) {
     return callback->sendFailure(Response::InternalError());
+  }
 
   uint32_t remove_mask = GetRemoveDataMask(storage_types);
 
@@ -504,8 +623,9 @@
 void StorageHandler::GetUsageAndQuota(
     const String& origin_string,
     std::unique_ptr<GetUsageAndQuotaCallback> callback) {
-  if (!storage_partition_)
+  if (!storage_partition_) {
     return callback->sendFailure(Response::InternalError());
+  }
 
   GURL origin_url(origin_string);
   url::Origin origin = url::Origin::Create(origin_url);
@@ -555,13 +675,15 @@
 
 Response StorageHandler::TrackCacheStorageForOrigin(
     const std::string& origin_string) {
-  if (!storage_partition_)
+  if (!storage_partition_) {
     return Response::InternalError();
+  }
 
   GURL origin_url(origin_string);
   url::Origin origin = url::Origin::Create(origin_url);
-  if (!origin_url.is_valid() || origin.opaque())
+  if (!origin_url.is_valid() || origin.opaque()) {
     return Response::InvalidParams(origin_string + " is not a valid URL");
+  }
 
   GetCacheStorageObserver()->TrackStorageKey(
       blink::StorageKey::CreateFirstParty(origin));
@@ -570,13 +692,15 @@
 
 Response StorageHandler::TrackCacheStorageForStorageKey(
     const std::string& storage_key) {
-  if (!storage_partition_)
+  if (!storage_partition_) {
     return Response::InternalError();
+  }
 
   absl::optional<blink::StorageKey> key =
       blink::StorageKey::Deserialize(storage_key);
-  if (!key)
+  if (!key) {
     return Response::InvalidParams("Unable to deserialize storage key");
+  }
 
   GetCacheStorageObserver()->TrackStorageKey(*key);
   return Response::Success();
@@ -584,13 +708,15 @@
 
 Response StorageHandler::UntrackCacheStorageForOrigin(
     const std::string& origin_string) {
-  if (!storage_partition_)
+  if (!storage_partition_) {
     return Response::InternalError();
+  }
 
   GURL origin_url(origin_string);
   url::Origin origin = url::Origin::Create(origin_url);
-  if (!origin_url.is_valid() || origin.opaque())
+  if (!origin_url.is_valid() || origin.opaque()) {
     return Response::InvalidParams(origin_string + " is not a valid URL");
+  }
 
   GetCacheStorageObserver()->UntrackStorageKey(
       blink::StorageKey::CreateFirstParty(origin));
@@ -599,13 +725,15 @@
 
 Response StorageHandler::UntrackCacheStorageForStorageKey(
     const std::string& storage_key) {
-  if (!storage_partition_)
+  if (!storage_partition_) {
     return Response::InternalError();
+  }
 
   absl::optional<blink::StorageKey> key =
       blink::StorageKey::Deserialize(storage_key);
-  if (!key)
+  if (!key) {
     return Response::InvalidParams("Unable to deserialize storage key");
+  }
 
   GetCacheStorageObserver()->UntrackStorageKey(*key);
   return Response::Success();
@@ -613,13 +741,15 @@
 
 Response StorageHandler::TrackIndexedDBForOrigin(
     const std::string& origin_string) {
-  if (!storage_partition_)
+  if (!storage_partition_) {
     return Response::InternalError();
+  }
 
   GURL origin_url(origin_string);
   url::Origin origin = url::Origin::Create(origin_url);
-  if (!origin_url.is_valid() || origin.opaque())
+  if (!origin_url.is_valid() || origin.opaque()) {
     return Response::InvalidParams(origin_string + " is not a valid URL");
+  }
 
   GetIndexedDBObserver()->TrackStorageKey(
       blink::StorageKey::CreateFirstParty(origin));
@@ -628,13 +758,15 @@
 
 Response StorageHandler::TrackIndexedDBForStorageKey(
     const std::string& storage_key) {
-  if (!storage_partition_)
+  if (!storage_partition_) {
     return Response::InternalError();
+  }
 
   absl::optional<blink::StorageKey> key =
       blink::StorageKey::Deserialize(storage_key);
-  if (!key)
+  if (!key) {
     return Response::InvalidParams("Unable to deserialize storage key");
+  }
 
   GetIndexedDBObserver()->TrackStorageKey(*key);
   return Response::Success();
@@ -642,13 +774,15 @@
 
 Response StorageHandler::UntrackIndexedDBForOrigin(
     const std::string& origin_string) {
-  if (!storage_partition_)
+  if (!storage_partition_) {
     return Response::InternalError();
+  }
 
   GURL origin_url(origin_string);
   url::Origin origin = url::Origin::Create(origin_url);
-  if (!origin_url.is_valid() || origin.opaque())
+  if (!origin_url.is_valid() || origin.opaque()) {
     return Response::InvalidParams(origin_string + " is not a valid URL");
+  }
 
   GetIndexedDBObserver()->UntrackStorageKey(
       blink::StorageKey::CreateFirstParty(origin));
@@ -657,13 +791,15 @@
 
 Response StorageHandler::UntrackIndexedDBForStorageKey(
     const std::string& storage_key) {
-  if (!storage_partition_)
+  if (!storage_partition_) {
     return Response::InternalError();
+  }
 
   absl::optional<blink::StorageKey> key =
       blink::StorageKey::Deserialize(storage_key);
-  if (!key)
+  if (!key) {
     return Response::InvalidParams("Unable to deserialize storage key");
+  }
 
   GetIndexedDBObserver()->UntrackStorageKey(*key);
   return Response::Success();
@@ -701,8 +837,9 @@
 
 absl::variant<protocol::Response, storage::SharedStorageManager*>
 StorageHandler::GetSharedStorageManager() {
-  if (!storage_partition_)
+  if (!storage_partition_) {
     return Response::InternalError();
+  }
 
   if (auto* manager = static_cast<StoragePartitionImpl*>(storage_partition_)
                           ->GetSharedStorageManager()) {
@@ -711,6 +848,13 @@
   return Response::ServerError("Shared storage is disabled");
 }
 
+storage::QuotaManagerProxy* StorageHandler::GetQuotaManagerProxy() {
+  DCHECK(storage_partition_);
+
+  return static_cast<StoragePartitionImpl*>(storage_partition_)
+      ->GetQuotaManagerProxy();
+}
+
 void StorageHandler::NotifyCacheStorageListChanged(
     const blink::StorageKey& storage_key) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
@@ -750,11 +894,13 @@
   BrowserContext* browser_context = nullptr;
   Response response =
       BrowserHandler::FindBrowserContext(browser_context_id, &browser_context);
-  if (!response.IsSuccess())
+  if (!response.IsSuccess()) {
     return response;
+  }
   *storage_partition = browser_context->GetDefaultStoragePartition();
-  if (!*storage_partition)
+  if (!*storage_partition) {
     return Response::InternalError();
+  }
   return Response::Success();
 }
 
@@ -874,8 +1020,9 @@
   auto trusted_bidding_signals_keys =
       std::make_unique<protocol::Array<std::string>>();
   if (group.trusted_bidding_signals_keys) {
-    for (const auto& key : group.trusted_bidding_signals_keys.value())
+    for (const auto& key : group.trusted_bidding_signals_keys.value()) {
       trusted_bidding_signals_keys->push_back(key);
+    }
   }
   auto ads =
       std::make_unique<protocol::Array<protocol::Storage::InterestGroupAd>>();
@@ -884,8 +1031,9 @@
       auto protocol_ad = protocol::Storage::InterestGroupAd::Create()
                              .SetRenderUrl(ad.render_url.spec())
                              .Build();
-      if (ad.metadata)
+      if (ad.metadata) {
         protocol_ad->SetMetadata(*ad.metadata);
+      }
       ads->push_back(std::move(protocol_ad));
     }
   }
@@ -896,8 +1044,9 @@
       auto protocol_ad = protocol::Storage::InterestGroupAd::Create()
                              .SetRenderUrl(ad.render_url.spec())
                              .Build();
-      if (ad.metadata)
+      if (ad.metadata) {
         protocol_ad->SetMetadata(*ad.metadata);
+      }
       ad_components->push_back(std::move(protocol_ad));
     }
   }
@@ -911,19 +1060,23 @@
           .SetAds(std::move(ads))
           .SetAdComponents(std::move(ad_components))
           .Build();
-  if (group.bidding_url)
+  if (group.bidding_url) {
     protocol_group->SetBiddingUrl(group.bidding_url->spec());
-  if (group.bidding_wasm_helper_url)
+  }
+  if (group.bidding_wasm_helper_url) {
     protocol_group->SetBiddingWasmHelperUrl(
         group.bidding_wasm_helper_url->spec());
+  }
   if (group.update_url) {
     protocol_group->SetUpdateUrl(group.update_url->spec());
   }
-  if (group.trusted_bidding_signals_url)
+  if (group.trusted_bidding_signals_url) {
     protocol_group->SetTrustedBiddingSignalsUrl(
         group.trusted_bidding_signals_url->spec());
-  if (group.user_bidding_signals)
+  }
+  if (group.user_bidding_signals) {
     protocol_group->SetUserBiddingSignals(*group.user_bidding_signals);
+  }
 
   callback->sendSuccess(std::move(protocol_group));
 }
@@ -961,20 +1114,23 @@
 }
 
 Response StorageHandler::SetInterestGroupTracking(bool enable) {
-  if (!storage_partition_)
+  if (!storage_partition_) {
     return Response::InternalError();
+  }
 
   InterestGroupManagerImpl* manager = static_cast<InterestGroupManagerImpl*>(
       storage_partition_->GetInterestGroupManager());
-  if (!manager)
+  if (!manager) {
     return Response::ServerError("Interest group storage is disabled.");
+  }
 
   if (enable) {
     // Only add if we are not already registered as an observer. We only
     // observe the interest group manager, so if we're observing anything then
     // we are already registered.
-    if (!IsInObserverList())
+    if (!IsInObserverList()) {
       manager->AddInterestGroupObserver(this);
+    }
   } else {
     // Removal doesn't care if we are not registered.
     manager->RemoveInterestGroupObserver(this);
@@ -1231,8 +1387,9 @@
 
 Response StorageHandler::SetSharedStorageTracking(bool enable) {
   if (enable) {
-    if (!GetSharedStorageWorkletHostManager())
+    if (!GetSharedStorageWorkletHostManager()) {
       return Response::ServerError("Shared storage is disabled.");
+    }
     shared_storage_observer_ = std::make_unique<SharedStorageObserver>(this);
   } else {
     shared_storage_observer_.reset();
@@ -1334,16 +1491,21 @@
   auto protocol_params =
       protocol::Storage::SharedStorageAccessParams::Create().Build();
 
-  if (params.script_source_url)
+  if (params.script_source_url) {
     protocol_params->SetScriptSourceUrl(*params.script_source_url);
-  if (params.operation_name)
+  }
+  if (params.operation_name) {
     protocol_params->SetOperationName(*params.operation_name);
-  if (params.serialized_data)
+  }
+  if (params.serialized_data) {
     protocol_params->SetSerializedData(*params.serialized_data);
-  if (params.key)
+  }
+  if (params.key) {
     protocol_params->SetKey(*params.key);
-  if (params.value)
+  }
+  if (params.value) {
     protocol_params->SetValue(*params.value);
+  }
 
   if (params.urls_with_metadata) {
     auto protocol_urls = std::make_unique<
@@ -1378,5 +1540,55 @@
                                    std::move(protocol_params));
 }
 
+DispatchResponse StorageHandler::SetStorageBucketTracking(
+    const std::string& serialized_storage_key,
+    bool enable) {
+  auto storage_key = blink::StorageKey::Deserialize(serialized_storage_key);
+  if (!storage_key.has_value()) {
+    return Response::InvalidParams("Invalid Storage Key given.");
+  }
+
+  if (enable) {
+    storage::QuotaManagerProxy* manager = GetQuotaManagerProxy();
+    if (!quota_manager_observer_) {
+      quota_manager_observer_ =
+          std::make_unique<StorageHandler::QuotaManagerObserver>(
+              weak_ptr_factory_.GetWeakPtr(), manager);
+    }
+    quota_manager_observer_->TrackStorageKey(storage_key.value(), manager);
+  } else if (quota_manager_observer_) {
+    quota_manager_observer_->UntrackStorageKey(storage_key.value());
+  }
+  return Response::Success();
+}
+
+DispatchResponse StorageHandler::DeleteStorageBucket(
+    const std::string& serialized_storage_key,
+    const std::string& bucket_name) {
+  storage::QuotaManagerProxy* manager = GetQuotaManagerProxy();
+  DCHECK(manager);
+
+  auto storage_key = blink::StorageKey::Deserialize(serialized_storage_key);
+  if (!storage_key.has_value()) {
+    return Response::InvalidParams("Invalid Storage Key given.");
+  }
+
+  manager->DeleteBucket(storage_key.value(), bucket_name,
+                        base::SingleThreadTaskRunner::GetCurrentDefault(),
+                        base::DoNothing());
+  return Response::Success();
+}
+
+void StorageHandler::NotifyCreateOrUpdateBucket(
+    const storage::BucketInfo& bucket_info) {
+  frontend_->StorageBucketCreatedOrUpdated(BuildBucketInfo(bucket_info));
+}
+
+void StorageHandler::NotifyDeleteBucket(
+    const storage::BucketLocator& bucket_locator) {
+  frontend_->StorageBucketDeleted(
+      base::NumberToString(bucket_locator.id.value()));
+}
+
 }  // namespace protocol
 }  // namespace content
diff --git a/content/browser/devtools/protocol/storage_handler.h b/content/browser/devtools/protocol/storage_handler.h
index 5fa6df9..99b2f02 100644
--- a/content/browser/devtools/protocol/storage_handler.h
+++ b/content/browser/devtools/protocol/storage_handler.h
@@ -16,6 +16,7 @@
 #include "content/browser/renderer_host/frame_tree_node.h"
 #include "content/browser/renderer_host/render_frame_host_impl.h"
 #include "content/browser/shared_storage/shared_storage_worklet_host_manager.h"
+#include "storage/browser/quota/quota_manager.h"
 
 namespace storage {
 class QuotaOverrideHandle;
@@ -129,12 +130,21 @@
       const std::string& owner_origin_string,
       std::unique_ptr<ResetSharedStorageBudgetCallback> callback) override;
 
+  DispatchResponse SetStorageBucketTracking(
+      const std::string& serialized_storage_key,
+      bool enable) override;
+
+  DispatchResponse DeleteStorageBucket(
+      const std::string& serialized_storage_key,
+      const std::string& bucket_name) override;
+
  private:
   // See definition for lifetime information.
   class CacheStorageObserver;
   class IndexedDBObserver;
   class InterestGroupObserver;
   class SharedStorageObserver;
+  class QuotaManagerObserver;
 
   // Not thread safe.
   CacheStorageObserver* GetCacheStorageObserver();
@@ -143,6 +153,7 @@
   SharedStorageWorkletHostManager* GetSharedStorageWorkletHostManager();
   absl::variant<protocol::Response, storage::SharedStorageManager*>
   GetSharedStorageManager();
+  storage::QuotaManagerProxy* GetQuotaManagerProxy();
 
   // content::InterestGroupManagerImpl::InterestGroupObserver
   void OnInterestGroupAccessed(
@@ -168,6 +179,8 @@
                                      const std::string& storage_key,
                                      const std::u16string& database_name,
                                      const std::u16string& object_store_name);
+  void NotifyCreateOrUpdateBucket(const storage::BucketInfo& bucket_info);
+  void NotifyDeleteBucket(const storage::BucketLocator& bucket_locator);
 
   Response FindStoragePartition(const Maybe<std::string>& browser_context_id,
                                 StoragePartition** storage_partition);
@@ -178,6 +191,7 @@
   std::unique_ptr<CacheStorageObserver> cache_storage_observer_;
   std::unique_ptr<IndexedDBObserver> indexed_db_observer_;
   std::unique_ptr<SharedStorageObserver> shared_storage_observer_;
+  std::unique_ptr<QuotaManagerObserver> quota_manager_observer_;
 
   // Exposes the API for managing storage quota overrides.
   std::unique_ptr<storage::QuotaOverrideHandle> quota_override_handle_;
diff --git a/content/browser/devtools/protocol_config.json b/content/browser/devtools/protocol_config.json
index ceb55e5..f5afd91 100644
--- a/content/browser/devtools/protocol_config.json
+++ b/content/browser/devtools/protocol_config.json
@@ -99,7 +99,7 @@
             },
             {
                 "domain": "Storage",
-              "async": ["getUsageAndQuota", "clearDataForOrigin", "clearDataForStorageKey", "getCookies", "setCookies", "clearCookies", "overrideQuotaForOrigin", "getTrustTokens", "clearTrustTokens", "getInterestGroupDetails", "getSharedStorageMetadata", "getSharedStorageEntries", "setSharedStorageEntry", "deleteSharedStorageEntry", "clearSharedStorageEntries", "resetSharedStorageBudget"]
+              "async": ["getUsageAndQuota", "clearDataForOrigin", "clearDataForStorageKey", "getCookies", "setCookies", "clearCookies", "overrideQuotaForOrigin", "getTrustTokens", "clearTrustTokens", "getInterestGroupDetails", "getSharedStorageMetadata", "getSharedStorageEntries", "setSharedStorageEntry", "deleteSharedStorageEntry", "clearSharedStorageEntries", "resetSharedStorageBudget", "getStorageBucketList"]
             },
             {
                 "domain": "SystemInfo",
diff --git a/content/browser/loader/navigation_url_loader.h b/content/browser/loader/navigation_url_loader.h
index 2ebfd46..38f82bb 100644
--- a/content/browser/loader/navigation_url_loader.h
+++ b/content/browser/loader/navigation_url_loader.h
@@ -11,8 +11,10 @@
 
 #include "content/browser/loader/navigation_loader_interceptor.h"
 #include "content/common/content_export.h"
+#include "services/network/public/mojom/cookie_access_observer.mojom.h"
 #include "services/network/public/mojom/devtools_observer.mojom-forward.h"
 #include "services/network/public/mojom/trust_token_access_observer.mojom-forward.h"
+#include "services/network/public/mojom/url_loader_network_service_observer.mojom.h"
 
 namespace net {
 class HttpRequestHeaders;
diff --git a/content/browser/loader/navigation_url_loader_impl_unittest.cc b/content/browser/loader/navigation_url_loader_impl_unittest.cc
index 556cd55..258c8be 100644
--- a/content/browser/loader/navigation_url_loader_impl_unittest.cc
+++ b/content/browser/loader/navigation_url_loader_impl_unittest.cc
@@ -238,7 +238,8 @@
             absl::nullopt /* web_bundle_token */,
             blink::mojom::NavigationInitiatorActivationAndAdStatus::
                 kDidNotStartWithTransientActivation,
-            false /* is_container_initiated */);
+            false /* is_container_initiated */,
+            false /* is_fullscreen_requested */);
 
     auto common_params = blink::CreateCommonNavigationParams();
     common_params->url = url;
diff --git a/content/browser/loader/navigation_url_loader_unittest.cc b/content/browser/loader/navigation_url_loader_unittest.cc
index 32a7402..6b55c1a1 100644
--- a/content/browser/loader/navigation_url_loader_unittest.cc
+++ b/content/browser/loader/navigation_url_loader_unittest.cc
@@ -99,7 +99,8 @@
             absl::nullopt /* web_bundle_token */,
             blink::mojom::NavigationInitiatorActivationAndAdStatus::
                 kDidNotStartWithTransientActivation,
-            false /* is_container_initiated */);
+            false /* is_container_initiated */,
+            false /* is_fullscreen_requested */);
     auto common_params = blink::CreateCommonNavigationParams();
     common_params->url = url;
     common_params->initiator_origin = url::Origin::Create(url);
diff --git a/content/browser/preloading/prefetch/prefetch_container.cc b/content/browser/preloading/prefetch/prefetch_container.cc
index 7388fb2..a4eabd709 100644
--- a/content/browser/preloading/prefetch/prefetch_container.cc
+++ b/content/browser/preloading/prefetch/prefetch_container.cc
@@ -6,6 +6,7 @@
 
 #include <memory>
 
+#include "base/metrics/histogram_functions.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/notreached.h"
 #include "base/time/time.h"
@@ -231,6 +232,40 @@
   }
 }
 
+std::string GetEagernessHistogramSuffix(
+    const blink::mojom::SpeculationEagerness& eagerness) {
+  switch (eagerness) {
+    case blink::mojom::SpeculationEagerness::kEager:
+      return "Eager";
+    case blink::mojom::SpeculationEagerness::kModerate:
+      return "Moderate";
+    case blink::mojom::SpeculationEagerness::kConservative:
+      return "Conservative";
+  }
+}
+
+void RecordWasBlockedUntilHeadWhenServingHistogram(
+    const blink::mojom::SpeculationEagerness& eagerness,
+    bool blocked_until_head) {
+  base::UmaHistogramBoolean(
+      base::StringPrintf(
+          "PrefetchProxy.AfterClick.WasBlockedUntilHeadWhenServing.%s",
+          GetEagernessHistogramSuffix(eagerness).c_str()),
+      blocked_until_head);
+}
+
+void RecordBlockUntilHeadDurationHistogram(
+    const blink::mojom::SpeculationEagerness& eagerness,
+    const base::TimeDelta& block_until_head_duration,
+    bool served) {
+  base::UmaHistogramTimes(
+      base::StringPrintf(
+          "PrefetchProxy.AfterClick.BlockUntilHeadDuration.%s.%s",
+          served ? "Served" : "NotServed",
+          GetEagernessHistogramSuffix(eagerness).c_str()),
+      block_until_head_duration);
+}
+
 }  // namespace
 
 PrefetchContainer::PrefetchContainer(
@@ -604,7 +639,8 @@
 bool PrefetchContainer::ShouldBlockUntilHeadReceived() const {
   // Can only block until head if the request has been started using a streaming
   // URL loader and head hasn't been received yet.
-  if (!streaming_loader_ || streaming_loader_->GetHead()) {
+  if (!streaming_loader_ || streaming_loader_->GetHead() ||
+      streaming_loader_->Failed()) {
     return false;
   }
   return PrefetchShouldBlockUntilHead(prefetch_type_.GetEagerness());
@@ -694,6 +730,28 @@
   return internal_url == no_vary_search_match_url.value();
 }
 
+void PrefetchContainer::OnGetPrefetchToServe(bool blocked_until_head) {
+  if (blocked_until_head) {
+    blocked_until_head_start_time_ = base::TimeTicks::Now();
+  }
+
+  RecordWasBlockedUntilHeadWhenServingHistogram(prefetch_type_.GetEagerness(),
+                                                blocked_until_head);
+}
+
+void PrefetchContainer::OnReturnPrefetchToServe(bool served) {
+  if (served) {
+    navigated_to_ = true;
+  }
+
+  if (blocked_until_head_start_time_.has_value()) {
+    RecordBlockUntilHeadDurationHistogram(
+        prefetch_type_.GetEagerness(),
+        base::TimeTicks::Now() - blocked_until_head_start_time_.value(),
+        served);
+  }
+}
+
 PrefetchContainer::SinglePrefetch::SinglePrefetch(const GURL& url)
     : url_(url) {}
 
diff --git a/content/browser/preloading/prefetch/prefetch_container.h b/content/browser/preloading/prefetch/prefetch_container.h
index c2522ad..fd6612d 100644
--- a/content/browser/preloading/prefetch/prefetch_container.h
+++ b/content/browser/preloading/prefetch/prefetch_container.h
@@ -172,9 +172,10 @@
   // The |PrefetchDocumentManager| that requested |this|.
   PrefetchDocumentManager* GetPrefetchDocumentManager() const;
 
-  // Called when a navigation is started that could pottentially use this
-  // prefetch.
-  void OnNavigationToPrefetch() { navigated_to_ = true; }
+  // Called when |PrefetchService::GetPrefetchToServe| and
+  // |PrefetchService::ReturnPrefetchToServe| with |this|.
+  void OnGetPrefetchToServe(bool blocked_until_head);
+  void OnReturnPrefetchToServe(bool served);
 
   // Returns whether or not this prefetch has been considered to serve for a
   // navigation in the past. If it has, then it shouldn't be used for any future
@@ -419,6 +420,10 @@
   const absl::optional<base::UnguessableToken>
       initiator_devtools_navigation_token_;
 
+  // The time at which |PrefetchService| started blocking until the head of
+  // |this| was received.
+  absl::optional<base::TimeTicks> blocked_until_head_start_time_;
+
   base::WeakPtrFactory<PrefetchContainer> weak_method_factory_{this};
 };
 
diff --git a/content/browser/preloading/prefetch/prefetch_container_unittest.cc b/content/browser/preloading/prefetch/prefetch_container_unittest.cc
index f19f3dfa..3be79ad 100644
--- a/content/browser/preloading/prefetch/prefetch_container_unittest.cc
+++ b/content/browser/preloading/prefetch/prefetch_container_unittest.cc
@@ -418,7 +418,7 @@
 
   // Simulates the URL of the prefetch being navigated to and the prefetch being
   // considered for serving.
-  prefetch_container->OnNavigationToPrefetch();
+  prefetch_container->OnReturnPrefetchToServe(/*served=*/true);
 
   // Simulate a successful DNS probe for this prefetch. Not this will also
   // update the status of the prefetch to
@@ -713,4 +713,73 @@
       GURL("https://test.com?a=2")));
 }
 
+TEST_F(PrefetchContainerTest, BlockUntilHeadHistograms) {
+  struct TestCase {
+    blink::mojom::SpeculationEagerness eagerness;
+    bool block_until_head;
+    base::TimeDelta block_until_head_duration;
+    bool served;
+  };
+
+  std::vector<TestCase> test_cases{
+      {blink::mojom::SpeculationEagerness::kEager, true, base::Milliseconds(10),
+       true},
+      {blink::mojom::SpeculationEagerness::kModerate, false,
+       base::Milliseconds(20), false},
+      {blink::mojom::SpeculationEagerness::kConservative, true,
+       base::Milliseconds(40), false}};
+
+  base::HistogramTester histogram_tester;
+  for (const auto& test_case : test_cases) {
+    PrefetchContainer prefetch_container(
+        GlobalRenderFrameHostId(1234, 5678), GURL("https://test.com"),
+        PrefetchType(/*use_isolated_network_context=*/true,
+                     /*use_prefetch_proxy=*/true, test_case.eagerness),
+        blink::mojom::Referrer(), nullptr);
+
+    prefetch_container.OnGetPrefetchToServe(test_case.block_until_head);
+    if (test_case.block_until_head) {
+      task_environment()->FastForwardBy(test_case.block_until_head_duration);
+    }
+    prefetch_container.OnReturnPrefetchToServe(test_case.served);
+  }
+
+  histogram_tester.ExpectBucketCount(
+      "PrefetchProxy.AfterClick.WasBlockedUntilHeadWhenServing.Eager", true, 1);
+  histogram_tester.ExpectBucketCount(
+      "PrefetchProxy.AfterClick.WasBlockedUntilHeadWhenServing.Eager", false,
+      0);
+
+  histogram_tester.ExpectBucketCount(
+      "PrefetchProxy.AfterClick.WasBlockedUntilHeadWhenServing.Moderate", true,
+      0);
+  histogram_tester.ExpectBucketCount(
+      "PrefetchProxy.AfterClick.WasBlockedUntilHeadWhenServing.Moderate", false,
+      1);
+
+  histogram_tester.ExpectBucketCount(
+      "PrefetchProxy.AfterClick.WasBlockedUntilHeadWhenServing.Conservative",
+      true, 1);
+  histogram_tester.ExpectBucketCount(
+      "PrefetchProxy.AfterClick.WasBlockedUntilHeadWhenServing.Conservative",
+      false, 0);
+
+  histogram_tester.ExpectUniqueTimeSample(
+      "PrefetchProxy.AfterClick.BlockUntilHeadDuration.Served.Eager",
+      base::Milliseconds(10), 1);
+  histogram_tester.ExpectTotalCount(
+      "PrefetchProxy.AfterClick.BlockUntilHeadDuration.NotServed.Eager", 0);
+
+  histogram_tester.ExpectTotalCount(
+      "PrefetchProxy.AfterClick.BlockUntilHeadDuration.Served.Moderate", 0);
+  histogram_tester.ExpectTotalCount(
+      "PrefetchProxy.AfterClick.BlockUntilHeadDuration.NotServed.Moderate", 0);
+
+  histogram_tester.ExpectTotalCount(
+      "PrefetchProxy.AfterClick.BlockUntilHeadDuration.Served.Conservative", 0);
+  histogram_tester.ExpectUniqueTimeSample(
+      "PrefetchProxy.AfterClick.BlockUntilHeadDuration.NotServed.Conservative",
+      base::Milliseconds(40), 1);
+}
+
 }  // namespace content
diff --git a/content/browser/preloading/prefetch/prefetch_document_manager_unittest.cc b/content/browser/preloading/prefetch/prefetch_document_manager_unittest.cc
index 036ffcd..b647074 100644
--- a/content/browser/preloading/prefetch/prefetch_document_manager_unittest.cc
+++ b/content/browser/preloading/prefetch/prefetch_document_manager_unittest.cc
@@ -283,7 +283,8 @@
   // Cover the case where we want to navigate again to the same prefetched
   // Url.
   // Simulate that we've already navigated to prefetched URL.
-  GetPrefetchesPreparedToServe()[0].second->OnNavigationToPrefetch();
+  GetPrefetchesPreparedToServe()[0].second->OnReturnPrefetchToServe(
+      /*served=*/true);
   // Try to navigate again to the same URL.
   NavigateMainframeRendererTo(GetCrossOriginUrl("/candidate2.html?a=2&b=3"));
   EXPECT_EQ(GetPrefetchesPreparedToServe().size(), 2u);
diff --git a/content/browser/preloading/prefetch/prefetch_params.cc b/content/browser/preloading/prefetch/prefetch_params.cc
index 6df1aa1..cd1d7f57 100644
--- a/content/browser/preloading/prefetch/prefetch_params.cc
+++ b/content/browser/preloading/prefetch/prefetch_params.cc
@@ -224,7 +224,7 @@
     case blink::mojom::SpeculationEagerness::kEager:
       return base::GetFieldTrialParamByFeatureAsBool(
           features::kPrefetchUseContentRefactor,
-          "block_until_head_eager_prefetch", false);
+          "block_until_head_eager_prefetch", true);
     case blink::mojom::SpeculationEagerness::kModerate:
       return base::GetFieldTrialParamByFeatureAsBool(
           features::kPrefetchUseContentRefactor,
diff --git a/content/browser/preloading/prefetch/prefetch_service.cc b/content/browser/preloading/prefetch/prefetch_service.cc
index c0d2a10..f3599c4 100644
--- a/content/browser/preloading/prefetch/prefetch_service.cc
+++ b/content/browser/preloading/prefetch/prefetch_service.cc
@@ -1230,12 +1230,14 @@
   }
 
   if (prefetch_container->IsPrefetchServable(PrefetchCacheableDuration())) {
+    prefetch_container->OnGetPrefetchToServe(/*blocked_until_head=*/false);
     ReturnPrefetchToServe(prefetch_container,
                           std::move(on_prefetch_to_serve_ready));
     return;
   }
 
   if (prefetch_container->ShouldBlockUntilHeadReceived()) {
+    prefetch_container->OnGetPrefetchToServe(/*blocked_until_head=*/false);
     prefetch_container->GetStreamingLoader()->SetOnReceivedHeadCallback(
         base::BindOnce(&PrefetchService::ReturnPrefetchToServe,
                        weak_method_factory_.GetWeakPtr(), prefetch_container,
@@ -1257,6 +1259,7 @@
       !prefetch_container->IsPrefetchServable(PrefetchCacheableDuration()) ||
       prefetch_container->HaveDefaultContextCookiesChanged(
           prefetch_container->GetURL())) {
+    prefetch_container->OnReturnPrefetchToServe(/*served=*/false);
     std::move(on_prefetch_to_serve_ready).Run(nullptr);
     return;
   }
@@ -1265,8 +1268,7 @@
     CopyIsolatedCookies(prefetch_container);
   }
 
-  prefetch_container->OnNavigationToPrefetch();
-
+  prefetch_container->OnReturnPrefetchToServe(/*served=*/true);
   std::move(on_prefetch_to_serve_ready).Run(prefetch_container);
   return;
 }
diff --git a/content/browser/preloading/prefetch/prefetch_streaming_url_loader.cc b/content/browser/preloading/prefetch/prefetch_streaming_url_loader.cc
index 4613f37..1d99c221 100644
--- a/content/browser/preloading/prefetch/prefetch_streaming_url_loader.cc
+++ b/content/browser/preloading/prefetch/prefetch_streaming_url_loader.cc
@@ -70,6 +70,31 @@
               response_complete_time_.value() + cacheable_duration);
 }
 
+bool PrefetchStreamingURLLoader::Failed() const {
+  switch (status_) {
+    case PrefetchStreamingURLLoaderStatus::kWaitingOnHead:
+    case PrefetchStreamingURLLoaderStatus::kHeadReceivedWaitingOnBody:
+    case PrefetchStreamingURLLoaderStatus::kSuccessfulNotServed:
+    case PrefetchStreamingURLLoaderStatus::kSuccessfulServedAfterCompletion:
+    case PrefetchStreamingURLLoaderStatus::kSuccessfulServedBeforeCompletion:
+    case PrefetchStreamingURLLoaderStatus::kPrefetchWasDecoy:
+    case PrefetchStreamingURLLoaderStatus::kFollowRedirect:
+    case PrefetchStreamingURLLoaderStatus::kPauseRedirectForEligibilityCheck:
+      return false;
+    case PrefetchStreamingURLLoaderStatus::kFailedInvalidHead:
+    case PrefetchStreamingURLLoaderStatus::kFailedInvalidHeaders:
+    case PrefetchStreamingURLLoaderStatus::kFailedNon2XX:
+    case PrefetchStreamingURLLoaderStatus::kFailedMIMENotSupported:
+    case PrefetchStreamingURLLoaderStatus::kFailedNetError:
+    case PrefetchStreamingURLLoaderStatus::kFailedNetErrorButServed:
+    case PrefetchStreamingURLLoaderStatus::kFailedInvalidRedirect:
+      return true;
+    case PrefetchStreamingURLLoaderStatus::kRedirected_DEPRECATED:
+      NOTREACHED();
+      return true;
+  }
+}
+
 void PrefetchStreamingURLLoader::DisconnectPrefetchURLLoaderMojo() {
   prefetch_url_loader_.reset();
   prefetch_url_loader_client_receiver_.reset();
@@ -208,9 +233,15 @@
     PrefetchStreamingURLLoaderStatus new_status) {
   DCHECK(redirect_head_);
 
+  // If the prefetch_url_loader_ is no longer connected, mark this as failed.
+  if (!prefetch_url_loader_) {
+    new_status = PrefetchStreamingURLLoaderStatus::kFailedInvalidRedirect;
+  }
+
   status_ = new_status;
   switch (status_) {
     case PrefetchStreamingURLLoaderStatus::kFollowRedirect:
+      DCHECK(prefetch_url_loader_);
       prefetch_url_loader_->FollowRedirect(
           /*removed_headers=*/std::vector<std::string>(),
           /*modified_headers=*/net::HttpRequestHeaders(),
diff --git a/content/browser/preloading/prefetch/prefetch_streaming_url_loader.h b/content/browser/preloading/prefetch/prefetch_streaming_url_