diff --git a/DEPS b/DEPS
index 20c1ee1..8e1f4bd 100644
--- a/DEPS
+++ b/DEPS
@@ -304,19 +304,19 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'src_internal_revision': '41f93f8ed68a00c511a47259273c4e829383fad3',
+  'src_internal_revision': 'be8ef875426e1cd969603fe4a9d6ac614ffa6e1f',
   # 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': 'e0881f06f94a1bc31e0a5ecd4618090002f61c28',
+  'skia_revision': 'fb644bfe964ddd51db6d6caf493ffc6a125b4db0',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'v8_revision': '54fbb5fc84ec83a32b25e6746db942e0aaca02af',
+  'v8_revision': 'ea0402bfac5428fbdb96f0ac19ac1dbbb4589b8f',
   # 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': 'bfc643a4c33fc83531d5226c2491a7f3da1760f1',
+  'angle_revision': 'b257da865ba588df53b6151299174bbc00a8082f',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -324,7 +324,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
-  'pdfium_revision': '49db42fb84e011f3367f30872266ab3a9c621489',
+  'pdfium_revision': '8007d6ed5cd165d13c5655f9a230b58cf10195f0',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling BoringSSL
   # and whatever else without interference from each other.
@@ -423,7 +423,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'dawn_revision': 'd25761da20b4fd38f3b1bf1cc53e3a40bc8932dd',
+  'dawn_revision': '6167f1b4634a9240b237942266d1f1617f1d16f1',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -1004,12 +1004,12 @@
 
   'src/clank': {
     'url': Var('chrome_git') + '/clank/internal/apps.git' + '@' +
-    '2cffdf40d0973f4fad8cbe455e916c46f1ec64f5',
+    'fe29512018ec9f92b49e92702baf0f087656fa32',
     'condition': 'checkout_android and checkout_src_internal',
   },
 
   'src/docs/website': {
-    'url': Var('chromium_git') + '/website.git' + '@' + 'dc8137be14f186da19c9cd33643cc075e15b568e',
+    'url': Var('chromium_git') + '/website.git' + '@' + 'c18170086240b4b598a76c73e89ff026af5d3c57',
   },
 
   'src/ios/third_party/earl_grey2/src': {
@@ -1425,7 +1425,7 @@
   # Tools used when building Chrome for Chrome OS. This affects both the Simple
   # Chrome workflow, as well as the chromeos-chrome ebuild.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '8199e2bbaef867e258148454b1ee7a472fe3a638',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'cca6a353a89b50997d6122904e1465c5eb3e57ea',
       'condition': 'checkout_chromeos',
   },
 
@@ -1460,7 +1460,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'b4fbce4794de1b135797bcdcfcc7be5900c3d109',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '22d7982fb2f1aec831063ac82d29a19311601760',
 
   'src/third_party/devtools-frontend/src':
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
@@ -1930,7 +1930,7 @@
     Var('chromium_git') + '/external/github.com/cisco/openh264' + '@' + '09a4f3ec842a8932341b195c5b01e141c8a16eb7',
 
   'src/third_party/openscreen/src':
-    Var('chromium_git') + '/openscreen' + '@' + 'd77735d9e5ef30477139b0fc1ca3db6706ec8651',
+    Var('chromium_git') + '/openscreen' + '@' + 'f90b21d85c04e51f0477042d0071db520e152593',
 
   'src/third_party/openxr/src': {
     'url': Var('chromium_git') + '/external/github.com/KhronosGroup/OpenXR-SDK' + '@' + '95fe35ffb383710a6e0567e958ead9a3b66e930c',
@@ -1956,7 +1956,7 @@
     Var('pdfium_git') + '/pdfium.git' + '@' +  Var('pdfium_revision'),
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + '9ac50d25a24563cd24268c1a5c6561c8595b4647',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + 'a81c77e87599fd87d238fb15ab4f4fb90a7a7adc',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '8ef97ff3b7332e38e61b347a2fbed425a4617151',
@@ -2170,10 +2170,10 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '1b6371436a0a60e6b9a4ae2a40a8eba198e3af02',
 
   'src/third_party/webgpu-cts/src':
-    Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '007e1015875a0a91794edbc14feb3ad5fe60f90b',
+    Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '125990efef045d9bbea45beb70291f1e0afda5fd',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + 'af0c18c594e71d067cda6e439df176135bea112f',
+    Var('webrtc_git') + '/src.git' + '@' + '316fe0b133419c15fb08846fc54f2d38651b98cc',
 
   # 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.
@@ -2296,7 +2296,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/help_app/app',
-        'version': '6FwohtL9JxTjoHpDqhB1LFIzwbcPwzcwKsPQrs3kRBEC',
+        'version': '3S_vch9BVEqwxlGe0IC6rd6LBxn7XN9zQENaXTo-30QC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
@@ -2307,7 +2307,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/media_app/app',
-        'version': '1SgwxREi0QXR9Sbc-MWrlCbZ98Byk7dZ09tHDL9j_N0C',
+        'version': 'P4FPxrqADFh0_jZwXT11-DL2tdJIIa1aWQS3kPhqbpoC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
@@ -4410,7 +4410,7 @@
 
   'src/ios_internal':  {
       'url': Var('chrome_git') + '/chrome/ios_internal.git' + '@' +
-        '9b0057e70bdd2d28c72ae0d461dc8539dee69b6e',
+        '4a88d53072d3477a8f490768dc5fa35e3f6434a7',
       'condition': 'checkout_ios and checkout_src_internal',
   },
 
diff --git a/android_webview/browser/gfx/task_queue_webview.cc b/android_webview/browser/gfx/task_queue_webview.cc
index d4dcb2d..b3d7fbc5 100644
--- a/android_webview/browser/gfx/task_queue_webview.cc
+++ b/android_webview/browser/gfx/task_queue_webview.cc
@@ -12,6 +12,7 @@
 #include "base/containers/queue.h"
 #include "base/functional/bind.h"
 #include "base/logging.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/synchronization/condition_variable.h"
 #include "base/synchronization/lock.h"
 #include "base/task/single_thread_task_runner.h"
@@ -131,6 +132,8 @@
 void TaskQueueViz::ScheduleOnVizAndBlock(VizTask viz_task) {
   TRACE_EVENT0("android_webview", "ScheduleOnVizAndBlock");
   DCHECK_CALLED_ON_VALID_THREAD(render_thread_checker_);
+  SCOPED_UMA_HISTOGRAM_TIMER_MICROS(
+      "Android.WebView.ScheduleOnVizAndBlockTime");
 
   // Expected behavior is |viz_task| on the viz thread. From |viz_task| until
   // the done closure is called (which may not be in the viz_task), viz thread
diff --git a/android_webview/browser/gfx/viz_compositor_thread_runner_webview.cc b/android_webview/browser/gfx/viz_compositor_thread_runner_webview.cc
index cdbcce1..415d9a69 100644
--- a/android_webview/browser/gfx/viz_compositor_thread_runner_webview.cc
+++ b/android_webview/browser/gfx/viz_compositor_thread_runner_webview.cc
@@ -9,6 +9,7 @@
 #include "android_webview/browser/gfx/task_queue_webview.h"
 #include "base/check_op.h"
 #include "base/location.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/no_destructor.h"
 #include "base/notreached.h"
 #include "base/synchronization/waitable_event.h"
@@ -112,6 +113,8 @@
 void VizCompositorThreadRunnerWebView::PostTaskAndBlock(
     const base::Location& from_here,
     base::OnceClosure task) {
+  SCOPED_UMA_HISTOGRAM_TIMER_MICROS(
+      "Android.WebView.VizCompositorRunnerPostTaskBlockTime");
   base::ScopedAllowBaseSyncPrimitivesOutsideBlockingScope allow_wait;
   base::WaitableEvent e;
   task_runner()->PostTask(from_here,
diff --git a/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java b/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java
index c4d00088..e504053 100644
--- a/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java
+++ b/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java
@@ -1002,6 +1002,7 @@
                 BlinkFeatures.CONCURRENT_VIEW_TRANSITIONS_SPA,
                 "Allows concurrent transitions in local frames rendered in the same process"),
         Flag.baseFeature("WebViewVizUseThreadPool"),
+        Flag.baseFeature("InProcessGpuUseIOThread"),
         // Add new commandline switches and features above. The final entry should have a
         // trailing comma for cleaner diffs.
     };
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index ac915c8..8ad66bb 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -1802,6 +1802,8 @@
     "system/input_device_settings/input_device_settings_logging.h",
     "system/input_device_settings/input_device_settings_metadata.cc",
     "system/input_device_settings/input_device_settings_metadata.h",
+    "system/input_device_settings/input_device_settings_metadata_manager.cc",
+    "system/input_device_settings/input_device_settings_metadata_manager.h",
     "system/input_device_settings/input_device_settings_metrics_manager.cc",
     "system/input_device_settings/input_device_settings_metrics_manager.h",
     "system/input_device_settings/input_device_settings_notification_controller.cc",
@@ -3946,6 +3948,7 @@
     "system/input_device_settings/input_device_settings_controller_unittest.cc",
     "system/input_device_settings/input_device_settings_dispatcher_unittest.cc",
     "system/input_device_settings/input_device_settings_logging_unittest.cc",
+    "system/input_device_settings/input_device_settings_metadata_manager_unittest.cc",
     "system/input_device_settings/input_device_settings_metadata_unittest.cc",
     "system/input_device_settings/input_device_settings_metrics_manager_unittest.cc",
     "system/input_device_settings/input_device_settings_notification_controller_unittest.cc",
diff --git a/ash/constants/ash_pref_names.h b/ash/constants/ash_pref_names.h
index 8c2acce..9d8a146 100644
--- a/ash/constants/ash_pref_names.h
+++ b/ash/constants/ash_pref_names.h
@@ -55,6 +55,15 @@
 // A boolean pref of whether mahi is enabled.
 inline constexpr char kMahiEnabled[] = "settings.mahi_enabled";
 
+// An integer pref which indicates the HMR (Quick answers and Mahi) consent
+// status from the user.
+inline constexpr char kHMRConsentStatus[] = "settings.hmr.consent_status";
+
+// An integer pref which indicates the number of times the HMR (Quick answers
+// and Mahi) consent window has been dismissed by the user.
+inline constexpr char kHMRConsentWindowDismissCount[] =
+    "settings.hmr.consent_window_dismiss_count";
+
 // A boolean pref used by an admin policy to enable/disable particular
 // features on the physical keyboard. See the policy at
 // PhysicalKeyboardAutocorrect.yml.
@@ -1267,8 +1276,15 @@
 inline constexpr char kRecentDailyGooglePhotosWallpapers[] =
     "recent_daily_google_photos_wallpapers";
 
-// A dictionary pref that maps usernames to wallpaper info.
+// A dictionary pref that maps usernames to versioned wallpaper info.
 // This is for wallpapers that are syncable across devices.
+inline constexpr char kSyncableVersionedWallpaperInfo[] =
+    "syncable_versioned_wallpaper_info";
+
+// A dictionary pref that maps usernames to wallpaper info.
+// This is for wallpapers that are syncable across devices. It is being replaced
+// by `kSyncableVersionedWallpaperInfo`. Data from this pref will be migrated to
+// the new pref.
 inline constexpr char kSyncableWallpaperInfo[] = "syncable_wallpaper_info";
 
 // A dictionary pref that maps wallpaper file paths to their prominent colors.
diff --git a/ash/game_dashboard/game_dashboard_button_reveal_controller.cc b/ash/game_dashboard/game_dashboard_button_reveal_controller.cc
index 1eaa3e0..960e43e 100644
--- a/ash/game_dashboard/game_dashboard_button_reveal_controller.cc
+++ b/ash/game_dashboard/game_dashboard_button_reveal_controller.cc
@@ -78,7 +78,9 @@
   // mouse cursor is within the top edge of the game window in fullscreen.
   const gfx::Point mouse_screen_location =
       event->target()->GetScreenLocation(*event);
-  if (IsMouseWithinButtonRevealBounds(mouse_screen_location)) {
+  if (IsEventWithinButtonRevealBounds(
+          mouse_screen_location,
+          chromeos::ImmersiveFullscreenController::kMouseRevealBoundsHeight)) {
     if (!top_edge_hover_timer_.IsRunning()) {
       top_edge_hover_timer_.Start(
           FROM_HERE, kMouseRevealDelay, this,
@@ -95,10 +97,46 @@
   }
 }
 
+void GameDashboardButtonRevealController::OnGestureEvent(
+    ui::GestureEvent* event) {
+  switch (event->type()) {
+    case ui::ET_GESTURE_SCROLL_BEGIN:
+      // Record the start location of a scroll gesture.
+      gesture_scroll_start_pos_ = event->location();
+      return;
+    case ui::ET_GESTURE_SCROLL_UPDATE: {
+      if (!gesture_scroll_start_pos_.has_value()) {
+        return;
+      }
+      // If scroll started at top and is going towards the bottom, show the game
+      // dashboard button; otherwise, hide the game dashboard button.
+      // Everything following is called at most once, as the optional variable
+      // is immediately reset.
+      const bool target_visibility =
+          event->details().scroll_y() > 0 &&
+          IsEventWithinButtonRevealBounds(
+              gesture_scroll_start_pos_.value(),
+              game_dashboard_utils::GetFrameHeaderHeight(
+                  context_->game_window()));
+      UpdateVisibility(target_visibility, /*animate=*/true);
+      gesture_scroll_start_pos_.reset();
+      return;
+    }
+    case ui::ET_GESTURE_SCROLL_END:
+    case ui::ET_SCROLL_FLING_START:
+      gesture_scroll_start_pos_.reset();
+      return;
+    default:
+      return;
+  }
+}
+
 bool GameDashboardButtonRevealController::CanShowGameDashboardButton(
     const gfx::Point& mouse_screen_location) {
   return !context_->game_dashboard_button_widget()->IsVisible() &&
-         IsMouseWithinButtonRevealBounds(mouse_screen_location);
+         IsEventWithinButtonRevealBounds(
+             mouse_screen_location,
+             chromeos::ImmersiveFullscreenController::kMouseRevealBoundsHeight);
 }
 
 bool GameDashboardButtonRevealController::CanHideGameDashboardButton(
@@ -108,12 +146,12 @@
          IsMouseOutsideHeaderBounds(mouse_screen_location);
 }
 
-bool GameDashboardButtonRevealController::IsMouseWithinButtonRevealBounds(
-    const gfx::Point& mouse_screen_location) {
+bool GameDashboardButtonRevealController::IsEventWithinButtonRevealBounds(
+    const gfx::Point& event_screen_location,
+    int reveal_height) {
   gfx::Rect button_reveal_bounds = context_->game_window()->GetBoundsInScreen();
-  button_reveal_bounds.set_height(
-      chromeos::ImmersiveFullscreenController::kMouseRevealBoundsHeight);
-  return button_reveal_bounds.Contains(mouse_screen_location);
+  button_reveal_bounds.set_height(reveal_height);
+  return button_reveal_bounds.Contains(event_screen_location);
 }
 
 bool GameDashboardButtonRevealController::IsMouseOutsideHeaderBounds(
diff --git a/ash/game_dashboard/game_dashboard_button_reveal_controller.h b/ash/game_dashboard/game_dashboard_button_reveal_controller.h
index 6721eb7..1784482 100644
--- a/ash/game_dashboard/game_dashboard_button_reveal_controller.h
+++ b/ash/game_dashboard/game_dashboard_button_reveal_controller.h
@@ -8,10 +8,7 @@
 #include "base/memory/raw_ptr.h"
 #include "base/timer/timer.h"
 #include "ui/events/event_handler.h"
-
-namespace gfx {
-class Point;
-}  // namespace gfx
+#include "ui/gfx/geometry/point.h"
 
 namespace ash {
 
@@ -39,6 +36,7 @@
 
   // ui::EventHandler:
   void OnMouseEvent(ui::MouseEvent* event) override;
+  void OnGestureEvent(ui::GestureEvent* event) override;
 
  private:
   friend class GameDashboardContextTestApi;
@@ -53,11 +51,11 @@
   // `mouse_screen_location` is within the Game Dashboard reveal bounds.
   bool CanHideGameDashboardButton(const gfx::Point& mouse_screen_location);
 
-  // Returns true if `mouse_screen_location` is within the Game Dashboard
-  // button's reveal bounds, which is the top section of the window with a
-  // height of
-  // `chromeos::ImmersiveFullscreenController::kMouseRevealBoundsHeight`.
-  bool IsMouseWithinButtonRevealBounds(const gfx::Point& mouse_screen_location);
+  // Returns true if `event_screen_location` is within the Game Dashboard
+  // button's reveal bounds, which is the top section of the window with the
+  // given `reveal height`.
+  bool IsEventWithinButtonRevealBounds(const gfx::Point& event_screen_location,
+                                       int reveal_height);
 
   // Returns true if `mouse_screen_location` is outside the window's frame
   // header.
@@ -74,6 +72,9 @@
   // Timer to track cursor being held at the top edge of the screen.
   base::OneShotTimer top_edge_hover_timer_;
 
+  // Gesture scroll begin position when drag starts.
+  std::optional<gfx::Point> gesture_scroll_start_pos_;
+
   base::WeakPtrFactory<GameDashboardButtonRevealController> weak_ptr_factory_{
       this};
 };
diff --git a/ash/game_dashboard/game_dashboard_context_unittest.cc b/ash/game_dashboard/game_dashboard_context_unittest.cc
index dbafac4..f37eb45 100644
--- a/ash/game_dashboard/game_dashboard_context_unittest.cc
+++ b/ash/game_dashboard/game_dashboard_context_unittest.cc
@@ -73,6 +73,10 @@
 const std::u16string& hidden_label = u"Hidden";
 const std::u16string& visible_label = u"Visible";
 
+// Touch drag constants.
+constexpr base::TimeDelta kTouchDragDuration = base::Milliseconds(200);
+const int kTouchDragSteps = 5;
+
 enum class Movement { kTouch, kMouse };
 
 template <typename T>
@@ -1550,7 +1554,8 @@
   ASSERT_FALSE(button_widget->IsVisible());
 }
 
-TEST_F(GameDashboardContextTest, GameDashboardButtonFullscreen_MouseOver) {
+TEST_F(GameDashboardContextTest,
+       GameDashboardButtonFullscreen_MouseOverAndTouchGesture) {
   // Create an ARC game window.
   SetAppBounds(gfx::Rect(50, 50, 800, 700));
   CreateGameWindow(/*is_arc_window=*/true,
@@ -1587,6 +1592,41 @@
   event_generator->MoveMouseTo(app_bounds.CenterPoint());
   ASSERT_FALSE(test_api_->GetGameDashboardButtonWidget()->IsVisible());
   ASSERT_FALSE(button_widget->IsVisible());
+
+  // Touch drag from top edge of window.
+  event_generator->GestureScrollSequence(app_bounds.top_center(),
+                                         app_bounds.CenterPoint(),
+                                         kTouchDragDuration, kTouchDragSteps);
+  ASSERT_TRUE(button_widget->IsVisible());
+  ASSERT_TRUE(test_api_->GetGameDashboardButtonWidget()->IsVisible());
+
+  // Touch drag to top edge of window.
+  event_generator->GestureScrollSequence(app_bounds.CenterPoint(),
+                                         app_bounds.top_center(),
+                                         kTouchDragDuration, kTouchDragSteps);
+  ASSERT_FALSE(test_api_->GetGameDashboardButtonWidget()->IsVisible());
+  ASSERT_FALSE(button_widget->IsVisible());
+
+  // Re-open the game dashboard button and touch drag to bottom edge of window.
+  event_generator->GestureScrollSequence(app_bounds.top_center(),
+                                         app_bounds.CenterPoint(),
+                                         kTouchDragDuration, kTouchDragSteps);
+  ASSERT_TRUE(button_widget->IsVisible());
+  ASSERT_TRUE(test_api_->GetGameDashboardButtonWidget()->IsVisible());
+
+  event_generator->GestureScrollSequence(app_bounds.CenterPoint(),
+                                         app_bounds.bottom_center(),
+                                         kTouchDragDuration, kTouchDragSteps);
+  ASSERT_FALSE(test_api_->GetGameDashboardButtonWidget()->IsVisible());
+  ASSERT_FALSE(button_widget->IsVisible());
+
+  // Touch drag to bottom edge of window while the game dashboard button is
+  // hidden.
+  event_generator->GestureScrollSequence(app_bounds.CenterPoint(),
+                                         app_bounds.bottom_center(),
+                                         kTouchDragDuration, kTouchDragSteps);
+  ASSERT_FALSE(test_api_->GetGameDashboardButtonWidget()->IsVisible());
+  ASSERT_FALSE(button_widget->IsVisible());
 }
 
 // Verifies that at startup and with the welcome dialog is visible, opening
diff --git a/ash/public/cpp/BUILD.gn b/ash/public/cpp/BUILD.gn
index ae34f06..b4ec4d6 100644
--- a/ash/public/cpp/BUILD.gn
+++ b/ash/public/cpp/BUILD.gn
@@ -527,6 +527,7 @@
     "tab_cluster/correlation_clusterer_unittest.cc",
     "tab_cluster/undirected_graph_unittest.cc",
     "wallpaper/wallpaper_info_unittest.cc",
+    "wallpaper/wallpaper_types_unittest.cc",
   ]
 
   deps = [
@@ -589,6 +590,8 @@
     "test/mock_projector_client.h",
     "test/mock_projector_controller.cc",
     "test/mock_projector_controller.h",
+    "test/mock_session_controller.cc",
+    "test/mock_session_controller.h",
     "test/test_cast_config_controller.cc",
     "test/test_cast_config_controller.h",
     "test/test_desk_profiles_delegate.cc",
diff --git a/ash/public/cpp/external_arc/message_center/arc_notification_view_unittest.cc b/ash/public/cpp/external_arc/message_center/arc_notification_view_unittest.cc
index d4461e8..51b78d0 100644
--- a/ash/public/cpp/external_arc/message_center/arc_notification_view_unittest.cc
+++ b/ash/public/cpp/external_arc/message_center/arc_notification_view_unittest.cc
@@ -2,12 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "ash/public/cpp/external_arc/message_center/arc_notification_view.h"
+
 #include <memory>
 
 #include "ash/public/cpp/external_arc/message_center/arc_notification_content_view.h"
 #include "ash/public/cpp/external_arc/message_center/arc_notification_item.h"
 #include "ash/public/cpp/external_arc/message_center/arc_notification_surface.h"
-#include "ash/public/cpp/external_arc/message_center/arc_notification_view.h"
 #include "ash/public/cpp/external_arc/message_center/mock_arc_notification_item.h"
 #include "ash/public/cpp/external_arc/message_center/mock_arc_notification_surface.h"
 #include "ash/public/cpp/message_center/arc_notification_constants.h"
@@ -25,7 +26,6 @@
 #include "ui/base/ime/dummy_text_input_client.h"
 #include "ui/base/ime/input_method.h"
 #include "ui/base/ime/text_input_client.h"
-#include "ui/base/ui_base_features.h"
 #include "ui/compositor/layer.h"
 #include "ui/compositor/scoped_animation_duration_scale_mode.h"
 #include "ui/compositor/test/layer_animation_stopped_waiter.h"
@@ -228,7 +228,6 @@
       nullptr;  // owned by its widget.
 
   std::unique_ptr<MockArcNotificationItem> item_;
-  base::test::ScopedFeatureList scoped_feature_list_;
 };
 
 TEST_F(ArcNotificationViewTest, Events) {
@@ -398,32 +397,13 @@
   EXPECT_EQ("1000x1000", size.ToString());
 }
 
-class NotificationGestureUpdateTest : public ArcNotificationViewTest {
- public:
-  NotificationGestureUpdateTest() = default;
-  NotificationGestureUpdateTest(const NotificationGestureUpdateTest&) = delete;
-  NotificationGestureUpdateTest& operator=(
-      const NotificationGestureUpdateTest&) = delete;
-
-  // Overridden from ViewsTestBase:
-  void SetUp() override {
-    scoped_feature_list_.InitWithFeatures(
-        {::features::kNotificationGesturesUpdate}, {});
-
-    ArcNotificationViewTest::SetUp();
-  }
-
- private:
-  base::test::ScopedFeatureList scoped_feature_list_;
-};
-
-TEST_F(NotificationGestureUpdateTest, TrackPadGestureSlideOut) {
+TEST_F(ArcNotificationViewTest, TrackPadGestureSlideOut) {
   ui::ScopedAnimationDurationScaleMode zero_duration_scope(
       ui::ScopedAnimationDurationScaleMode::ZERO_DURATION);
 
   ui::test::EventGenerator generator(
       (notification_view()->GetWidget()->GetNativeWindow()->GetRootWindow()));
-  generator.ScrollSequence(gfx::Point(), base::TimeDelta(), /*x_offset=*/20,
+  generator.ScrollSequence(gfx::Point(), base::TimeDelta(), /*x_offset=*/200,
                            /*y_offset=*/0, /*steps=*/1, /*num_fingers=*/2);
   EXPECT_TRUE(IsPopupRemovedAfterIdle(kDefaultNotificationId));
 }
diff --git a/ash/public/cpp/test/mock_session_controller.cc b/ash/public/cpp/test/mock_session_controller.cc
new file mode 100644
index 0000000..5d0ca250
--- /dev/null
+++ b/ash/public/cpp/test/mock_session_controller.cc
@@ -0,0 +1,13 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/public/cpp/test/mock_session_controller.h"
+
+namespace ash {
+
+MockSessionController::MockSessionController() = default;
+
+MockSessionController::~MockSessionController() = default;
+
+}  // namespace ash
diff --git a/ash/public/cpp/test/mock_session_controller.h b/ash/public/cpp/test/mock_session_controller.h
new file mode 100644
index 0000000..51f4b6f
--- /dev/null
+++ b/ash/public/cpp/test/mock_session_controller.h
@@ -0,0 +1,87 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_PUBLIC_CPP_TEST_MOCK_SESSION_CONTROLLER_H_
+#define ASH_PUBLIC_CPP_TEST_MOCK_SESSION_CONTROLLER_H_
+
+#include <cstdint>
+#include <optional>
+#include <string>
+#include <vector>
+
+#include "ash/public/cpp/session/session_controller.h"
+#include "base/functional/callback.h"
+#include "base/time/time.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace ash {
+
+class MockSessionController : public ash::SessionController {
+ public:
+  MockSessionController();
+  MockSessionController(const MockSessionController&) = delete;
+  MockSessionController& operator=(const MockSessionController&) = delete;
+  ~MockSessionController() override;
+
+  // SessionController:
+  MOCK_METHOD(void, SetClient, (SessionControllerClient * client), (override));
+  MOCK_METHOD(void, SetSessionInfo, (const SessionInfo& info), (override));
+  MOCK_METHOD(void,
+              UpdateUserSession,
+              (const UserSession& user_session),
+              (override));
+  MOCK_METHOD(void,
+              SetUserSessionOrder,
+              (const std::vector<uint32_t>& user_session_ids),
+              (override));
+  MOCK_METHOD(void,
+              PrepareForLock,
+              (PrepareForLockCallback callback),
+              (override));
+  MOCK_METHOD(void, StartLock, (StartLockCallback callback), (override));
+  MOCK_METHOD(void, NotifyChromeLockAnimationsComplete, (), (override));
+  MOCK_METHOD(void,
+              RunUnlockAnimation,
+              (RunUnlockAnimationCallback callback),
+              (override));
+  MOCK_METHOD(void, NotifyChromeTerminating, (), (override));
+  MOCK_METHOD(void,
+              SetSessionLengthLimit,
+              (base::TimeDelta length_limit, base::Time start_time),
+              (override));
+  MOCK_METHOD(void,
+              CanSwitchActiveUser,
+              (CanSwitchActiveUserCallback callback),
+              (override));
+  MOCK_METHOD(void,
+              ShowMultiprofilesIntroDialog,
+              (ShowMultiprofilesIntroDialogCallback callback),
+              (override));
+  MOCK_METHOD(void,
+              ShowTeleportWarningDialog,
+              (ShowTeleportWarningDialogCallback callback),
+              (override));
+  MOCK_METHOD(void,
+              ShowMultiprofilesSessionAbortedDialog,
+              (const std::string& user_email),
+              (override));
+  MOCK_METHOD(void,
+              AddSessionActivationObserverForAccountId,
+              (const AccountId& account_id,
+               SessionActivationObserver* observer),
+              (override));
+  MOCK_METHOD(void,
+              RemoveSessionActivationObserverForAccountId,
+              (const AccountId& account_id,
+               SessionActivationObserver* observer),
+              (override));
+  MOCK_METHOD(void, AddObserver, (SessionObserver * observer), (override));
+  MOCK_METHOD(void, RemoveObserver, (SessionObserver * observer), (override));
+  MOCK_METHOD(bool, IsScreenLocked, (), (const, override));
+  MOCK_METHOD(std::optional<int>, GetExistingUsersCount, (), (const, override));
+};
+
+}  // namespace ash
+
+#endif  // ASH_PUBLIC_CPP_TEST_MOCK_SESSION_CONTROLLER_H_
diff --git a/ash/public/cpp/wallpaper/wallpaper_info.cc b/ash/public/cpp/wallpaper/wallpaper_info.cc
index 204564e..f863ace 100644
--- a/ash/public/cpp/wallpaper/wallpaper_info.cc
+++ b/ash/public/cpp/wallpaper/wallpaper_info.cc
@@ -6,6 +6,7 @@
 
 #include <iostream>
 
+#include "ash/constants/ash_features.h"
 #include "ash/public/cpp/wallpaper/online_wallpaper_params.h"
 #include "ash/public/cpp/wallpaper/wallpaper_types.h"
 #include "base/logging.h"
@@ -88,10 +89,15 @@
                ? WallpaperType::kDaily
                : WallpaperType::kOnline),
       date(base::Time::Now()),
-      asset_id(target_variant.asset_id),
       collection_id(online_wallpaper_params.collection_id),
       unit_id(online_wallpaper_params.unit_id),
-      variants(online_wallpaper_params.variants) {}
+      variants(online_wallpaper_params.variants) {
+  if (features::IsVersionWallpaperInfoEnabled()) {
+    version = GetSupportedVersion(type);
+  } else {
+    asset_id = target_variant.asset_id;
+  }
+}
 
 WallpaperInfo::WallpaperInfo(
     const GooglePhotosWallpaperParams& google_photos_wallpaper_params)
@@ -104,6 +110,9 @@
     location = google_photos_wallpaper_params.id;
     dedup_key = google_photos_wallpaper_params.dedup_key;
   }
+  if (features::IsVersionWallpaperInfoEnabled()) {
+    version = GetSupportedVersion(type);
+  }
 }
 
 WallpaperInfo::WallpaperInfo(const std::string& in_location,
@@ -115,7 +124,11 @@
       user_file_path(in_user_file_path),
       layout(in_layout),
       type(in_type),
-      date(in_date) {}
+      date(in_date) {
+  if (features::IsVersionWallpaperInfoEnabled()) {
+    version = GetSupportedVersion(type);
+  }
+}
 
 WallpaperInfo::WallpaperInfo(const WallpaperInfo& other) = default;
 WallpaperInfo& WallpaperInfo::operator=(const WallpaperInfo& other) = default;
@@ -124,6 +137,16 @@
 WallpaperInfo& WallpaperInfo::operator=(WallpaperInfo&& other) = default;
 
 bool WallpaperInfo::MatchesSelection(const WallpaperInfo& other) const {
+  if (features::IsVersionWallpaperInfoEnabled()) {
+    // Checks for exact match of the version here to avoid unexpected data
+    // mismatch between two WallpaperInfos. Any difference in version should
+    // surface to the callers of this function so they can decide how to handle
+    // it.
+    if (!version.IsValid() || !other.version.IsValid() ||
+        version != other.version) {
+      return false;
+    }
+  }
   // |location| are skipped on purpose in favor of |unit_id| as
   // online wallpapers can vary across devices due to their color mode. Other
   // wallpaper types still require location to be equal.
@@ -301,6 +324,7 @@
   os << "  collection_id: " << info.collection_id << std::endl;
   os << "  unit_id: " << info.unit_id.value_or(-1) << std::endl;
   os << "  variants_size: " << info.variants.size() << std::endl;
+  os << "  version: " << info.version.GetString() << std::endl;
   return os;
 }
 
diff --git a/ash/public/cpp/wallpaper/wallpaper_info_unittest.cc b/ash/public/cpp/wallpaper/wallpaper_info_unittest.cc
index 2e7850e..ab1e04f9 100644
--- a/ash/public/cpp/wallpaper/wallpaper_info_unittest.cc
+++ b/ash/public/cpp/wallpaper/wallpaper_info_unittest.cc
@@ -4,12 +4,15 @@
 
 #include "ash/public/cpp/wallpaper/wallpaper_info.h"
 
+#include "ash/constants/ash_features.h"
 #include "ash/public/cpp/wallpaper/google_photos_wallpaper_params.h"
 #include "ash/public/cpp/wallpaper/online_wallpaper_params.h"
 #include "ash/public/cpp/wallpaper/wallpaper_types.h"
 #include "ash/webui/personalization_app/proto/backdrop_wallpaper.pb.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/time/time.h"
 #include "base/values.h"
+#include "base/version.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace ash {
@@ -99,5 +102,29 @@
   }
 }
 
+TEST_F(WallpaperInfoTest, OnlineWallpapeV1DoesNotContainAssetId) {
+  base::test::ScopedFeatureList scoped_feature_list(
+      features::kVersionedWallpaperInfo);
+  OnlineWallpaperParams params = OnlineWallpaperParams(
+      kAccountId1,
+      /*collection_id=*/std::string(), WALLPAPER_LAYOUT_CENTER_CROPPED,
+      /*preview_mode=*/false, /*from_user=*/false,
+      /*daily_refresh_enabled=*/false, kUnitId,
+      /*variants=*/
+      {{kAssetId, GURL("https://example.com/image.png"),
+        backdrop::Image::IMAGE_TYPE_UNKNOWN}});
+  WallpaperInfo actual_info = WallpaperInfo(params, params.variants[0]);
+  base::Value::Dict dict = actual_info.ToDict();
+
+  std::optional<WallpaperInfo> expected_info = WallpaperInfo::FromDict(dict);
+
+  EXPECT_TRUE(actual_info.MatchesAsset(expected_info.value()));
+  EXPECT_FALSE(actual_info.asset_id.has_value());
+  EXPECT_TRUE(actual_info.MatchesAsset(expected_info.value()));
+  EXPECT_FALSE(expected_info->asset_id.has_value());
+  EXPECT_TRUE(expected_info->version.IsValid());
+  EXPECT_EQ(expected_info->version, GetSupportedVersion(expected_info->type));
+}
+
 }  // namespace
 }  // namespace ash
diff --git a/ash/public/cpp/wallpaper/wallpaper_types.cc b/ash/public/cpp/wallpaper/wallpaper_types.cc
index 12545aa..f25e4d1 100644
--- a/ash/public/cpp/wallpaper/wallpaper_types.cc
+++ b/ash/public/cpp/wallpaper/wallpaper_types.cc
@@ -60,4 +60,29 @@
   return type == WallpaperType::kDaily || type == WallpaperType::kOnline;
 }
 
+base::Version GetSupportedVersion(WallpaperType type) {
+  switch (type) {
+    case WallpaperType::kOnline:
+    case WallpaperType::kDaily:
+    case WallpaperType::kOnceGooglePhotos:
+    case WallpaperType::kDailyGooglePhotos:
+    case WallpaperType::kCustomized:
+    case WallpaperType::kDefault:
+    case WallpaperType::kPolicy:
+    case WallpaperType::kThirdParty:
+    case WallpaperType::kDevice:
+    case WallpaperType::kOneShot:
+    case WallpaperType::kOobe:
+    case WallpaperType::kSeaPen:
+      return base::Version("1.0");
+    case WallpaperType::kCount:
+      LOG(WARNING) << __func__
+                   << " Unknown wallpaper type: " << base::to_underlying(type);
+      return base::Version();
+  }
+  LOG(WARNING) << __func__
+               << " Unknown wallpaper type: " << base::to_underlying(type);
+  return base::Version();
+}
+
 }  // namespace ash
diff --git a/ash/public/cpp/wallpaper/wallpaper_types.h b/ash/public/cpp/wallpaper/wallpaper_types.h
index 6416f7e..4e8d0c4 100644
--- a/ash/public/cpp/wallpaper/wallpaper_types.h
+++ b/ash/public/cpp/wallpaper/wallpaper_types.h
@@ -6,6 +6,7 @@
 #define ASH_PUBLIC_CPP_WALLPAPER_WALLPAPER_TYPES_H_
 
 #include "ash/public/cpp/ash_public_export.h"
+#include "base/version.h"
 #include "third_party/skia/include/core/SkColor.h"
 
 namespace ash {
@@ -93,6 +94,9 @@
 
 ASH_PUBLIC_EXPORT bool IsOnlineWallpaper(WallpaperType type);
 
+// Returns the supported version of the wallpaper type.
+ASH_PUBLIC_EXPORT base::Version GetSupportedVersion(WallpaperType type);
+
 }  // namespace ash
 
 #endif  // ASH_PUBLIC_CPP_WALLPAPER_WALLPAPER_TYPES_H_
diff --git a/ash/public/cpp/wallpaper/wallpaper_types_unittest.cc b/ash/public/cpp/wallpaper/wallpaper_types_unittest.cc
new file mode 100644
index 0000000..9816051
--- /dev/null
+++ b/ash/public/cpp/wallpaper/wallpaper_types_unittest.cc
@@ -0,0 +1,93 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/public/cpp/wallpaper/wallpaper_types.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace ash {
+namespace {
+
+using WallpaperTypeTest = ::testing::Test;
+
+TEST_F(WallpaperTypeTest, GetSupportedVersion) {
+  {
+    // WallpaperType::kOnline:
+    EXPECT_EQ(GetSupportedVersion(WallpaperType::kOnline),
+              base::Version("1.0"));
+    EXPECT_TRUE(GetSupportedVersion(WallpaperType::kOnline).IsValid());
+  }
+  {
+    // WallpaperType::kDaily:
+    EXPECT_EQ(GetSupportedVersion(WallpaperType::kDaily), base::Version("1.0"));
+    EXPECT_TRUE(GetSupportedVersion(WallpaperType::kDaily).IsValid());
+  }
+  {
+    // WallpaperType::kOnceGooglePhotos:
+    EXPECT_EQ(GetSupportedVersion(WallpaperType::kOnceGooglePhotos),
+              base::Version("1.0"));
+    EXPECT_TRUE(
+        GetSupportedVersion(WallpaperType::kOnceGooglePhotos).IsValid());
+  }
+  {  // WallpaperType::kDailyGooglePhotos:
+    EXPECT_EQ(GetSupportedVersion(WallpaperType::kDailyGooglePhotos),
+              base::Version("1.0"));
+    EXPECT_TRUE(
+        GetSupportedVersion(WallpaperType::kDailyGooglePhotos).IsValid());
+  }
+  {
+    // WallpaperType::kCustomized:
+    EXPECT_EQ(GetSupportedVersion(WallpaperType::kCustomized),
+              base::Version("1.0"));
+    EXPECT_TRUE(GetSupportedVersion(WallpaperType::kCustomized).IsValid());
+  }
+  {
+    // WallpaperType::kDefault:
+    EXPECT_EQ(GetSupportedVersion(WallpaperType::kDefault),
+              base::Version("1.0"));
+    EXPECT_TRUE(GetSupportedVersion(WallpaperType::kDefault).IsValid());
+  }
+  {
+    // WallpaperType::kPolicy:
+    EXPECT_EQ(GetSupportedVersion(WallpaperType::kPolicy),
+              base::Version("1.0"));
+    EXPECT_TRUE(GetSupportedVersion(WallpaperType::kPolicy).IsValid());
+  }
+  {
+    // WallpaperType::kThirdParty:
+    EXPECT_EQ(GetSupportedVersion(WallpaperType::kThirdParty),
+              base::Version("1.0"));
+    EXPECT_TRUE(GetSupportedVersion(WallpaperType::kThirdParty).IsValid());
+  }
+  {
+    // WallpaperType::kDevice:
+    EXPECT_EQ(GetSupportedVersion(WallpaperType::kDevice),
+              base::Version("1.0"));
+    EXPECT_TRUE(GetSupportedVersion(WallpaperType::kDevice).IsValid());
+  }
+  {
+    // WallpaperType::kOneShot:
+    EXPECT_EQ(GetSupportedVersion(WallpaperType::kOneShot),
+              base::Version("1.0"));
+    EXPECT_TRUE(GetSupportedVersion(WallpaperType::kOneShot).IsValid());
+  }
+  {
+    // WallpaperType::kOobe:
+    EXPECT_EQ(GetSupportedVersion(WallpaperType::kOobe), base::Version("1.0"));
+    EXPECT_TRUE(GetSupportedVersion(WallpaperType::kOobe).IsValid());
+  }
+  {
+    // WallpaperType::kSeaPen:
+    EXPECT_EQ(GetSupportedVersion(WallpaperType::kSeaPen),
+              base::Version("1.0"));
+    EXPECT_TRUE(GetSupportedVersion(WallpaperType::kSeaPen).IsValid());
+  }
+  {
+    // WallpaperType::kCount:
+    EXPECT_FALSE(GetSupportedVersion(WallpaperType::kCount).IsValid());
+  }
+}
+
+}  // namespace
+}  // namespace ash
diff --git a/ash/system/focus_mode/focus_mode_controller.cc b/ash/system/focus_mode/focus_mode_controller.cc
index d2873b3..b92430d3 100644
--- a/ash/system/focus_mode/focus_mode_controller.cc
+++ b/ash/system/focus_mode/focus_mode_controller.cc
@@ -357,8 +357,7 @@
   selected_task_ = task;
   // TODO(b/305089077): Update user prefs.
   if (focus_mode_metrics_recorder_ && !selected_task_.empty()) {
-    focus_mode_metrics_recorder_->set_tasks_selected_count(
-        focus_mode_metrics_recorder_->tasks_selected_count() + 1);
+    focus_mode_metrics_recorder_->IncrementTasksSelectedCount();
   }
 }
 
@@ -371,6 +370,10 @@
                              selected_task_.task_id, selected_task_.title,
                              /*completed=*/true, base::DoNothing());
   SetSelectedTask({});
+
+  if (focus_mode_metrics_recorder_) {
+    focus_mode_metrics_recorder_->IncrementTasksCompletedCount();
+  }
 }
 
 void FocusModeController::MaybeShowEndingMomentNudge() {
@@ -402,9 +405,10 @@
   focus_mode_metrics_recorder_ =
       std::make_unique<FocusModeMetricsRecorder>(session_duration_);
   focus_mode_metrics_recorder_->RecordHistogramsOnStart(source);
+  if (HasSelectedTask()) {
+    focus_mode_metrics_recorder_->IncrementTasksSelectedCount();
+  }
 
-  focus_mode_metrics_recorder_->set_tasks_selected_count(HasSelectedTask() ? 1
-                                                                           : 0);
   current_session_ = FocusModeSession(session_duration_,
                                       session_duration_ + base::Time::Now());
 
diff --git a/ash/system/focus_mode/focus_mode_controller_unittest.cc b/ash/system/focus_mode/focus_mode_controller_unittest.cc
index fca3335..c1e2c7b0 100644
--- a/ash/system/focus_mode/focus_mode_controller_unittest.cc
+++ b/ash/system/focus_mode/focus_mode_controller_unittest.cc
@@ -11,6 +11,7 @@
 #include "ash/public/cpp/ash_prefs.h"
 #include "ash/shell.h"
 #include "ash/system/focus_mode/focus_mode_controller.h"
+#include "ash/system/focus_mode/focus_mode_histogram_names.h"
 #include "ash/system/focus_mode/focus_mode_session.h"
 #include "ash/system/focus_mode/focus_mode_task_test_utils.h"
 #include "ash/system/focus_mode/focus_mode_tray.h"
@@ -31,6 +32,10 @@
 constexpr char kUser1Email[] = "user1@focusmode";
 constexpr char kUser2Email[] = "user2@focusmode";
 
+constexpr base::TimeDelta kShortDuration = base::Minutes(10);
+constexpr base::TimeDelta kMediumDuration = base::Minutes(11);
+constexpr base::TimeDelta kLongDuration = base::Minutes(30);
+
 bool IsEndingMomentNudgeShown() {
   return Shell::Get()->anchored_nudge_manager()->IsNudgeShown(
       focus_mode_util::kFocusModeEndingMomentNudgeId);
@@ -531,9 +536,6 @@
 // minutes the user extended during the session.
 TEST_F(FocusModeControllerMultiUserTest, CheckTimeAddedOnSessionEndHistogram) {
   base::HistogramTester histogram_tester;
-  constexpr base::TimeDelta kShortDuration = base::Minutes(10);
-  constexpr base::TimeDelta kMediumDuration = base::Minutes(11);
-  constexpr base::TimeDelta kLongDuration = base::Minutes(30);
 
   // 1. Start a session with 10 minutes and extend the session duration while in
   // an active session.
@@ -546,9 +548,8 @@
   controller->ExtendSessionDuration();
   controller->ToggleFocusMode();
   EXPECT_FALSE(controller->in_focus_session());
-  histogram_tester.ExpectBucketCount(
-      focus_mode_histogram_names::kShortTimeAddedOnSessionEndHistogramName,
-      /*sample=*/10, /*expected_count=*/1);
+  histogram_tester.ExpectBucketCount(/*name=*/"Ash.FocusMode.TimeAdded.Short",
+                                     /*sample=*/10, /*expected_count=*/1);
 
   // 2. Start a session with 11 minutes. Even not extending the session will
   // still trigger the metric to be recorded.
@@ -559,9 +560,8 @@
 
   controller->ToggleFocusMode();
   EXPECT_FALSE(controller->in_focus_session());
-  histogram_tester.ExpectBucketCount(
-      focus_mode_histogram_names::kMediumTimeAddedOnSessionEndHistogramName,
-      /*sample=*/0, /*expected_count=*/1);
+  histogram_tester.ExpectBucketCount(/*name=*/"Ash.FocusMode.TimeAdded.Medium",
+                                     /*sample=*/0, /*expected_count=*/1);
 
   // 3. Start a session with 30 minutes and extend the session.
   controller->SetInactiveSessionDuration(kLongDuration);
@@ -572,9 +572,138 @@
   controller->ExtendSessionDuration();
   controller->ToggleFocusMode();
   EXPECT_FALSE(controller->in_focus_session());
+  histogram_tester.ExpectBucketCount(/*name=*/"Ash.FocusMode.TimeAdded.Long",
+                                     /*sample=*/10, /*expected_count=*/1);
+}
+
+// Verify that when a session is over, the histogram will record the session
+// progress percentage.
+TEST_F(FocusModeControllerMultiUserTest, CheckPercentCompletedHistogram) {
+  base::HistogramTester histogram_tester;
+
+  // 1. Start a session with 10 minutes and stop the session when the progress
+  // is 50%.
+  auto* controller = FocusModeController::Get();
+  controller->SetInactiveSessionDuration(kShortDuration);
+  controller->ToggleFocusMode();
+  EXPECT_TRUE(controller->in_focus_session());
+  EXPECT_EQ(kShortDuration, controller->session_duration());
+
+  AdvanceClock(base::Minutes(5));
+  controller->ToggleFocusMode();
+  EXPECT_FALSE(controller->in_focus_session());
   histogram_tester.ExpectBucketCount(
-      focus_mode_histogram_names::kLongTimeAddedOnSessionEndHistogramName,
-      /*sample=*/10, /*expected_count=*/1);
+      /*name=*/"Ash.FocusMode.PercentOfSessionCompleted.Short",
+      /*sample=*/50, /*expected_count=*/1);
+
+  // 2. Start a session with 11 minutes and stop the session immediately, which
+  // should record 0%.
+  controller->SetInactiveSessionDuration(kMediumDuration);
+  controller->ToggleFocusMode();
+  EXPECT_TRUE(controller->in_focus_session());
+  EXPECT_EQ(kMediumDuration, controller->session_duration());
+
+  controller->ToggleFocusMode();
+  EXPECT_FALSE(controller->in_focus_session());
+  histogram_tester.ExpectBucketCount(
+      /*name=*/"Ash.FocusMode.PercentOfSessionCompleted.Medium",
+      /*sample=*/0, /*expected_count=*/1);
+
+  // 3. Start a session with 30 minutes and extend the session two times. After
+  // the ending moment, the histogram should record 100%.
+  controller->SetInactiveSessionDuration(kLongDuration);
+  controller->ToggleFocusMode();
+  EXPECT_TRUE(controller->in_focus_session());
+  EXPECT_EQ(kLongDuration, controller->session_duration());
+
+  controller->ExtendSessionDuration();
+  controller->ExtendSessionDuration();
+
+  const auto& current_session = controller->current_session();
+  EXPECT_TRUE(current_session.has_value());
+
+  // Once the session expires, the ending moment should start.
+  AdvanceClock(current_session->session_duration());
+  EXPECT_TRUE(controller->in_ending_moment());
+
+  // After the ending moment expires, the histogram will record 100%.
+  AdvanceClock(focus_mode_util::kEndingMomentDuration);
+  histogram_tester.ExpectBucketCount(
+      /*name=*/"Ash.FocusMode.PercentOfSessionCompleted.Long",
+      /*sample=*/100, /*expected_count=*/1);
+}
+
+// Verify that the histogram will record the number of tasks completed during an
+// active session and the ending moment.
+TEST_F(FocusModeControllerMultiUserTest, CheckTasksCompletedHistogram) {
+  base::HistogramTester histogram_tester;
+
+  SimulateUserLogin(GetUser1AccountId());
+
+  auto* controller = FocusModeController::Get();
+  EXPECT_FALSE(controller->in_focus_session());
+
+  // 1. Select a new task before a session starts, which will not be recorded
+  // into the histogram.
+  auto& tasks_client = CreateFakeTasksClient(GetUser1AccountId());
+
+  FocusModeTask task;
+  task.task_list_id = "list0";
+  task.task_id = "task0";
+  task.title = "Focus Task";
+  task.updated = base::Time::Now();
+
+  AddFakeTaskList(tasks_client, task.task_list_id);
+  AddFakeTask(tasks_client, task.task_list_id, task.task_id, task.title);
+
+  controller->SetSelectedTask(task);
+  controller->CompleteTask();
+  histogram_tester.ExpectTotalCount(
+      focus_mode_histogram_names::kTasksCompletedHistogramName, 0);
+
+  // 2. Start a focus session and select a task during the active session.
+  controller->ToggleFocusMode();
+  EXPECT_TRUE(controller->in_focus_session());
+
+  task.task_list_id = "list1";
+  task.task_id = "task1";
+  task.title = "A New Focus Task";
+  task.updated = base::Time::Now();
+
+  AddFakeTaskList(tasks_client, task.task_list_id);
+  AddFakeTask(tasks_client, task.task_list_id, task.task_id, task.title);
+
+  controller->SetSelectedTask(task);
+  EXPECT_TRUE(controller->HasSelectedTask());
+
+  // Mark the first task as completed.
+  controller->CompleteTask();
+
+  // Select a new task during this session.
+  task.task_list_id = "list2";
+  task.task_id = "task2";
+  task.title = "A New Focus Task";
+  task.updated = base::Time::Now();
+
+  AddFakeTaskList(tasks_client, task.task_list_id);
+  AddFakeTask(tasks_client, task.task_list_id, task.task_id, task.title);
+
+  controller->SetSelectedTask(task);
+
+  // 3. Mark the second task as completed during the ending moment.
+  const auto& current_session = controller->current_session();
+  EXPECT_TRUE(current_session.has_value());
+  AdvanceClock(current_session->session_duration());
+  EXPECT_TRUE(controller->in_ending_moment());
+  controller->CompleteTask();
+
+  // Verify the histogram after the ending moment expires.
+  AdvanceClock(focus_mode_util::kEndingMomentDuration);
+  histogram_tester.ExpectBucketCount(
+      focus_mode_histogram_names::kTasksCompletedHistogramName,
+      /*sample=*/2, /*expected_count=*/1);
+  histogram_tester.ExpectTotalCount(
+      focus_mode_histogram_names::kTasksCompletedHistogramName, 1);
 }
 
 }  // namespace ash
diff --git a/ash/system/focus_mode/focus_mode_histogram_names.h b/ash/system/focus_mode/focus_mode_histogram_names.h
index c5d5d10f..03c7f6b 100644
--- a/ash/system/focus_mode/focus_mode_histogram_names.h
+++ b/ash/system/focus_mode/focus_mode_histogram_names.h
@@ -7,6 +7,10 @@
 
 namespace ash::focus_mode_histogram_names {
 
+constexpr char kShortSuffix[] = "Short";
+constexpr char kMediumSuffix[] = "Medium";
+constexpr char kLongSuffix[] = "Long";
+
 // Histograms recorded when starting a session.
 constexpr char kHasSelectedTaskOnSessionStartHistogramName[] =
     "Ash.FocusMode.StartSession.HasSelectedTask";
@@ -23,12 +27,10 @@
 constexpr char kTasksSelectedHistogramName[] = "Ash.FocusMode.TasksSelected";
 constexpr char kDNDStateOnFocusEndHistogramName[] =
     "Ash.FocusMode.DNDStateOnFocusEnd";
-constexpr char kShortTimeAddedOnSessionEndHistogramName[] =
-    "Ash.FocusMode.TimeAdded.Short";
-constexpr char kMediumTimeAddedOnSessionEndHistogramName[] =
-    "Ash.FocusMode.TimeAdded.Medium";
-constexpr char kLongTimeAddedOnSessionEndHistogramName[] =
-    "Ash.FocusMode.TimeAdded.Long";
+constexpr char kTimeAddedOnSessionEndPrefix[] = "Ash.FocusMode.TimeAdded.";
+constexpr char kPercentCompletedPrefix[] =
+    "Ash.FocusMode.PercentOfSessionCompleted.";
+constexpr char kTasksCompletedHistogramName[] = "Ash.FocusMode.TasksCompleted";
 
 // This enum is used for metrics, so enum values should not be changed. New enum
 // values can be added, but existing enums must never be renumbered or deleted
diff --git a/ash/system/focus_mode/focus_mode_metrics_recorder.cc b/ash/system/focus_mode/focus_mode_metrics_recorder.cc
index 0880a851..95179e5 100644
--- a/ash/system/focus_mode/focus_mode_metrics_recorder.cc
+++ b/ash/system/focus_mode/focus_mode_metrics_recorder.cc
@@ -14,6 +14,15 @@
 namespace ash {
 namespace {
 
+const char* GetNameSuffixBySessionDuration(int session_duration) {
+  if (session_duration <= 10) {
+    return focus_mode_histogram_names::kShortSuffix;
+  } else if (session_duration >= 30) {
+    return focus_mode_histogram_names::kLongSuffix;
+  }
+  return focus_mode_histogram_names::kMediumSuffix;
+}
+
 void RecordInitialDurationHistogram(base::TimeDelta session_duration) {
   base::UmaHistogramCustomCounts(
       /*name=*/focus_mode_histogram_names::
@@ -56,6 +65,12 @@
       tasks_selected_count);
 }
 
+void RecordTasksCompletedHistogram(const int tasks_completed_count) {
+  base::UmaHistogramCounts100(
+      focus_mode_histogram_names::kTasksCompletedHistogramName,
+      tasks_completed_count);
+}
+
 void RecordDNDStateOnFocusEndHistogram(
     bool has_user_interactions_on_dnd_in_focus_session) {
   auto* message_center = message_center::MessageCenter::Get();
@@ -82,17 +97,10 @@
 
 void RecordTimeAddedOnSessionEndHistogram(int initial_session_duration,
                                           int current_session_duration) {
-  std::string histogram_name;
-  if (initial_session_duration <= 10) {
-    histogram_name =
-        focus_mode_histogram_names::kShortTimeAddedOnSessionEndHistogramName;
-  } else if (initial_session_duration >= 30) {
-    histogram_name =
-        focus_mode_histogram_names::kLongTimeAddedOnSessionEndHistogramName;
-  } else {
-    histogram_name =
-        focus_mode_histogram_names::kMediumTimeAddedOnSessionEndHistogramName;
-  }
+  std::string histogram_name(
+      focus_mode_histogram_names::kTimeAddedOnSessionEndPrefix);
+  histogram_name.append(
+      GetNameSuffixBySessionDuration(initial_session_duration));
 
   base::UmaHistogramCustomCounts(
       /*name=*/histogram_name,
@@ -102,6 +110,17 @@
       /*buckets=*/50);
 }
 
+void RecordPercentCompletedHistogram(double progress,
+                                     int final_session_duration) {
+  std::string histogram_name(
+      focus_mode_histogram_names::kPercentCompletedPrefix);
+  histogram_name.append(GetNameSuffixBySessionDuration(final_session_duration));
+
+  base::UmaHistogramPercentage(
+      /*name=*/histogram_name,
+      /*percent=*/(progress * 100));
+}
+
 }  // namespace
 
 FocusModeMetricsRecorder::FocusModeMetricsRecorder(
@@ -114,6 +133,14 @@
   message_center::MessageCenter::Get()->RemoveObserver(this);
 }
 
+void FocusModeMetricsRecorder::IncrementTasksSelectedCount() {
+  tasks_selected_count_++;
+}
+
+void FocusModeMetricsRecorder::IncrementTasksCompletedCount() {
+  tasks_completed_count_++;
+}
+
 void FocusModeMetricsRecorder::OnQuietModeChanged(bool in_quiet_mode) {
   // Only set the value if it's in a focus session and not triggered by
   // Focus Mode.
@@ -135,15 +162,17 @@
 
 void FocusModeMetricsRecorder::RecordHistogramsOnEnd() {
   RecordTasksSelectedHistogram(tasks_selected_count_);
+  RecordTasksCompletedHistogram(tasks_completed_count_);
   RecordDNDStateOnFocusEndHistogram(
       has_user_interactions_on_dnd_in_focus_session_);
 
-  if (auto current_session = FocusModeController::Get()->current_session();
-      current_session.has_value()) {
-    RecordTimeAddedOnSessionEndHistogram(
-        initial_session_duration_.InMinutes(),
-        current_session->session_duration().InMinutes());
-  }
+  auto session_snapshot =
+      FocusModeController::Get()->GetSnapshot(base::Time::Now());
+  RecordTimeAddedOnSessionEndHistogram(
+      initial_session_duration_.InMinutes(),
+      session_snapshot.session_duration.InMinutes());
+  RecordPercentCompletedHistogram(
+      session_snapshot.progress, session_snapshot.session_duration.InMinutes());
 }
 
 }  // namespace ash
diff --git a/ash/system/focus_mode/focus_mode_metrics_recorder.h b/ash/system/focus_mode/focus_mode_metrics_recorder.h
index 6f605d2..b4126ad 100644
--- a/ash/system/focus_mode/focus_mode_metrics_recorder.h
+++ b/ash/system/focus_mode/focus_mode_metrics_recorder.h
@@ -21,10 +21,8 @@
   FocusModeMetricsRecorder& operator=(const FocusModeMetricsRecorder&) = delete;
   ~FocusModeMetricsRecorder() override;
 
-  int tasks_selected_count() const { return tasks_selected_count_; }
-  void set_tasks_selected_count(int tasks_selected_count) {
-    tasks_selected_count_ = tasks_selected_count;
-  }
+  void IncrementTasksSelectedCount();
+  void IncrementTasksCompletedCount();
 
   // message_center::MessageCenterObserver:
   void OnQuietModeChanged(bool in_quiet_mode) override;
@@ -39,6 +37,9 @@
   // Counts the number of tasks selected during a session.
   int tasks_selected_count_ = 0;
 
+  // Counts the number of tasks marked as completed during a session.
+  int tasks_completed_count_ = 0;
+
   // True if the user turns DND on or off in an active session.
   bool has_user_interactions_on_dnd_in_focus_session_ = false;
   const base::TimeDelta initial_session_duration_;
diff --git a/ash/system/input_device_settings/input_device_settings_metadata_manager.cc b/ash/system/input_device_settings/input_device_settings_metadata_manager.cc
new file mode 100644
index 0000000..1b4783c
--- /dev/null
+++ b/ash/system/input_device_settings/input_device_settings_metadata_manager.cc
@@ -0,0 +1,14 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/system/input_device_settings/input_device_settings_metadata_manager.h"
+
+namespace ash {
+
+InputDeviceSettingsMetadataManager::InputDeviceSettingsMetadataManager() =
+    default;
+InputDeviceSettingsMetadataManager::~InputDeviceSettingsMetadataManager() =
+    default;
+
+}  // namespace ash
diff --git a/ash/system/input_device_settings/input_device_settings_metadata_manager.h b/ash/system/input_device_settings/input_device_settings_metadata_manager.h
new file mode 100644
index 0000000..d1d9780
--- /dev/null
+++ b/ash/system/input_device_settings/input_device_settings_metadata_manager.h
@@ -0,0 +1,26 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_SYSTEM_INPUT_DEVICE_SETTINGS_INPUT_DEVICE_SETTINGS_METADATA_MANAGER_H_
+#define ASH_SYSTEM_INPUT_DEVICE_SETTINGS_INPUT_DEVICE_SETTINGS_METADATA_MANAGER_H_
+
+#include "ash/ash_export.h"
+
+namespace ash {
+
+// Handles input device metadata (images, app info) and updates components on
+// changes.
+class ASH_EXPORT InputDeviceSettingsMetadataManager {
+ public:
+  InputDeviceSettingsMetadataManager();
+  InputDeviceSettingsMetadataManager(
+      const InputDeviceSettingsMetadataManager&) = delete;
+  InputDeviceSettingsMetadataManager& operator=(
+      const InputDeviceSettingsMetadataManager&) = delete;
+  ~InputDeviceSettingsMetadataManager();
+};
+
+}  // namespace ash
+
+#endif  // ASH_SYSTEM_INPUT_DEVICE_SETTINGS_INPUT_DEVICE_SETTINGS_METADATA_MANAGER_H_
diff --git a/ash/system/input_device_settings/input_device_settings_metadata_manager_unittest.cc b/ash/system/input_device_settings/input_device_settings_metadata_manager_unittest.cc
new file mode 100644
index 0000000..909791e
--- /dev/null
+++ b/ash/system/input_device_settings/input_device_settings_metadata_manager_unittest.cc
@@ -0,0 +1,42 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/system/input_device_settings/input_device_settings_metadata_manager.h"
+
+#include <memory>
+
+#include "ash/test/ash_test_base.h"
+
+namespace ash {
+
+class InputDeviceSettingsMetadataManagerTest : public AshTestBase {
+ public:
+  InputDeviceSettingsMetadataManagerTest() = default;
+  InputDeviceSettingsMetadataManagerTest(
+      const InputDeviceSettingsMetadataManagerTest&) = delete;
+  InputDeviceSettingsMetadataManagerTest& operator=(
+      const InputDeviceSettingsMetadataManagerTest&) = delete;
+  ~InputDeviceSettingsMetadataManagerTest() override = default;
+
+  // testing::Test:
+  void SetUp() override {
+    AshTestBase::SetUp();
+    manager_ = std::make_unique<InputDeviceSettingsMetadataManager>();
+  }
+
+  void TearDown() override {
+    manager_.reset();
+    AshTestBase::TearDown();
+  }
+
+ protected:
+  std::unique_ptr<InputDeviceSettingsMetadataManager> manager_;
+};
+
+// TODO(b/329686601): Remove this stub test.
+TEST_F(InputDeviceSettingsMetadataManagerTest, Initialize) {
+  EXPECT_NE(manager_.get(), nullptr);
+}
+
+}  // namespace ash
diff --git a/ash/system/notification_center/notification_grouping_controller_unittest.cc b/ash/system/notification_center/notification_grouping_controller_unittest.cc
index 868ab98..9d20679c 100644
--- a/ash/system/notification_center/notification_grouping_controller_unittest.cc
+++ b/ash/system/notification_center/notification_grouping_controller_unittest.cc
@@ -9,9 +9,9 @@
 #include "ash/constants/ash_constants.h"
 #include "ash/constants/ash_features.h"
 #include "ash/system/notification_center/ash_message_popup_collection.h"
-#include "ash/system/notification_center/views/ash_notification_view.h"
 #include "ash/system/notification_center/message_center_utils.h"
 #include "ash/system/notification_center/notification_center_tray.h"
+#include "ash/system/notification_center/views/ash_notification_view.h"
 #include "ash/system/notification_center/views/notification_center_view.h"
 #include "ash/system/notification_center/views/notification_list_view.h"
 #include "ash/system/tray/tray_background_view.h"
@@ -20,7 +20,6 @@
 #include "base/run_loop.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/time/time.h"
-#include "ui/base/ui_base_features.h"
 #include "ui/color/color_id.h"
 #include "ui/compositor/scoped_animation_duration_scale_mode.h"
 #include "ui/compositor/test/layer_animation_stopped_waiter.h"
@@ -565,10 +564,6 @@
 // the center of the screen. Also, tests that the correct notifications are
 // dismissed by swiping in the expanded state.
 TEST_F(NotificationGroupingControllerTest, NotificationSwipeGestureBehavior) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndEnableFeature(
-      ::features::kNotificationGesturesUpdate);
-
   auto* message_center = MessageCenter::Get();
   std::string parent_id, id0, id1, id2, id3;
   const GURL url(u"http://test-url.com/");
diff --git a/ash/wallpaper/wallpaper_controller_impl.cc b/ash/wallpaper/wallpaper_controller_impl.cc
index 847116c..ee63f90 100644
--- a/ash/wallpaper/wallpaper_controller_impl.cc
+++ b/ash/wallpaper/wallpaper_controller_impl.cc
@@ -1694,7 +1694,7 @@
     pref_change_registrar_ = std::make_unique<PrefChangeRegistrar>();
     pref_change_registrar_->Init(pref_service);
     pref_change_registrar_->Add(
-        prefs::kSyncableWallpaperInfo,
+        WallpaperPrefManager::GetSyncPrefName(),
         base::BindRepeating(&WallpaperControllerImpl::SyncLocalAndRemotePrefs,
                             weak_factory_.GetWeakPtr(), account_id));
 
diff --git a/ash/wallpaper/wallpaper_controller_unittest.cc b/ash/wallpaper/wallpaper_controller_unittest.cc
index 058d102..1f1306c 100644
--- a/ash/wallpaper/wallpaper_controller_unittest.cc
+++ b/ash/wallpaper/wallpaper_controller_unittest.cc
@@ -5093,8 +5093,9 @@
   EXPECT_EQ(actual.date, original_timestamp);
 }
 
+// TODO(b:341840200): Re-enable this test.
 TEST_P(WallpaperControllerAutoScheduleTest,
-       UpdateTimeOfDayWallpaperWithAutoColorModeOff) {
+       DISABLED_UpdateTimeOfDayWallpaperWithAutoColorModeOff) {
   static constexpr gfx::Size kTestImageSize = gfx::Size(100, 100);
   static constexpr SkColor kSunriseImageColor = SK_ColorRED;
   static constexpr SkColor kMorningImageColor = SK_ColorGREEN;
diff --git a/ash/wallpaper/wallpaper_pref_manager.cc b/ash/wallpaper/wallpaper_pref_manager.cc
index a5b5c75..29a3489 100644
--- a/ash/wallpaper/wallpaper_pref_manager.cc
+++ b/ash/wallpaper/wallpaper_pref_manager.cc
@@ -10,6 +10,7 @@
 #include <string_view>
 #include <vector>
 
+#include "ash/constants/ash_features.h"
 #include "ash/constants/ash_pref_names.h"
 #include "ash/public/cpp/session/session_types.h"
 #include "ash/public/cpp/wallpaper/wallpaper_controller_client.h"
@@ -66,8 +67,13 @@
                       const WallpaperInfo& info,
                       PrefService* const pref_service,
                       const std::string& pref_name) {
-  if (!pref_service)
+  if (features::IsVersionWallpaperInfoEnabled()) {
+    CHECK(info.version.IsValid());
+  }
+
+  if (!pref_service) {
     return false;
+  }
 
   DCHECK(IsAllowedInPrefs(info.type))
       << "Cannot save WallpaperType=" << base::to_underlying(info.type)
@@ -203,7 +209,7 @@
     RemoveWallpaperInfo(account_id, local_state_, prefs::kUserWallpaperInfo);
     RemoveWallpaperInfo(account_id,
                         profile_helper_->GetUserPrefServiceSyncable(account_id),
-                        prefs::kSyncableWallpaperInfo);
+                        GetSyncPrefName());
   }
 
   std::optional<WallpaperCalculatedColors> GetCachedWallpaperColors(
@@ -320,8 +326,7 @@
     if (!pref_service)
       return false;
 
-    return GetWallpaperInfo(account_id, pref_service,
-                            prefs::kSyncableWallpaperInfo, info);
+    return GetWallpaperInfo(account_id, pref_service, GetSyncPrefName(), info);
   }
 
   // Store |info| into the syncable pref service for |account_id|.
@@ -334,8 +339,7 @@
 
     DCHECK(IsWallpaperTypeSyncable(info.type));
 
-    return SetWallpaperInfo(account_id, info, pref_service,
-                            prefs::kSyncableWallpaperInfo);
+    return SetWallpaperInfo(account_id, info, pref_service, GetSyncPrefName());
   }
 
   base::TimeDelta GetTimeToNextDailyRefreshUpdate(
@@ -404,6 +408,13 @@
 }  // namespace
 
 // static
+const char* WallpaperPrefManager::GetSyncPrefName() {
+  return features::IsVersionWallpaperInfoEnabled()
+             ? prefs::kSyncableVersionedWallpaperInfo
+             : prefs::kSyncableWallpaperInfo;
+}
+
+// static
 bool WallpaperPrefManager::ShouldSyncOut(const WallpaperInfo& local_info) {
   if (IsTimeOfDayWallpaper(local_info.collection_id)) {
     // Time Of Day wallpapers are not syncable.
@@ -421,6 +432,23 @@
                << " from remote prefs is not syncable.";
     return false;
   }
+
+  if (features::IsVersionWallpaperInfoEnabled()) {
+    base::Version sync_version = synced_info.version;
+    base::Version local_version = GetSupportedVersion(synced_info.type);
+    if (!sync_version.IsValid()) {
+      LOG(WARNING) << __func__ << " invalid sync version";
+      return false;
+    }
+    if (sync_version.IsValid() && local_version.IsValid()) {
+      const int remote_major_version = sync_version.components()[0];
+      const int local_major_version = local_version.components()[0];
+      if (remote_major_version > local_major_version) {
+        return false;
+      }
+    }
+  }
+
   if (synced_info.MatchesSelection(local_info)) {
     return false;
   }
@@ -477,6 +505,8 @@
 
   registry->RegisterDictionaryPref(prefs::kSyncableWallpaperInfo,
                                    PrefRegistrySyncable::SYNCABLE_OS_PREF);
+  registry->RegisterDictionaryPref(prefs::kSyncableVersionedWallpaperInfo,
+                                   PrefRegistrySyncable::SYNCABLE_OS_PREF);
 }
 
 }  // namespace ash
diff --git a/ash/wallpaper/wallpaper_pref_manager.h b/ash/wallpaper/wallpaper_pref_manager.h
index 00dd32d..aa6aae4 100644
--- a/ash/wallpaper/wallpaper_pref_manager.h
+++ b/ash/wallpaper/wallpaper_pref_manager.h
@@ -59,6 +59,8 @@
 // Manages wallpaper preferences and tracks the currently configured wallpaper.
 class ASH_EXPORT WallpaperPrefManager : public SessionObserver {
  public:
+  // Returns the name of the syncable pref of the user's wallpaper info.
+  static const char* GetSyncPrefName();
   // Determines whether the wallpaper info is syncable and should be stored in
   // synced prefs.
   static bool ShouldSyncOut(const WallpaperInfo& local_info);
diff --git a/ash/wallpaper/wallpaper_pref_manager_unittest.cc b/ash/wallpaper/wallpaper_pref_manager_unittest.cc
index 7c78e9c..086abaa 100644
--- a/ash/wallpaper/wallpaper_pref_manager_unittest.cc
+++ b/ash/wallpaper/wallpaper_pref_manager_unittest.cc
@@ -8,6 +8,7 @@
 #include <string_view>
 #include <utility>
 
+#include "ash/constants/ash_features.h"
 #include "ash/constants/ash_pref_names.h"
 #include "ash/public/cpp/wallpaper/wallpaper_info.h"
 #include "ash/session/session_controller_impl.h"
@@ -18,6 +19,7 @@
 #include "base/functional/callback_forward.h"
 #include "base/memory/raw_ptr.h"
 #include "base/run_loop.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
 #include "components/prefs/scoped_user_pref_update.h"
 #include "components/prefs/testing_pref_service.h"
@@ -473,6 +475,80 @@
                                                   /*is_oobe=*/false));
 }
 
+TEST_F(WallpaperPrefManagerTest,
+       SetUserWallpaperInfoChecksVersionWhenVersionedWallpaperInfoIsEnabled) {
+  base::test::ScopedFeatureList features(features::kVersionedWallpaperInfo);
+  WallpaperInfo local_info = InfoWithType(WallpaperType::kOnline);
+
+  EXPECT_TRUE(local_info.version.IsValid());
+  EXPECT_TRUE(pref_manager_->SetUserWallpaperInfo(account_id_1, local_info));
+}
+
+TEST_F(WallpaperPrefManagerTest, ShouldNotSyncInIfSyncPrefHasInvalidVersion) {
+  base::test::ScopedFeatureList features(features::kVersionedWallpaperInfo);
+  WallpaperInfo local_info = InfoWithType(WallpaperType::kOnline);
+  WallpaperInfo synced_info = InfoWithType(WallpaperType::kOnline);
+  synced_info.version = base::Version();
+
+  EXPECT_FALSE(WallpaperPrefManager::ShouldSyncIn(synced_info, local_info,
+                                                  /*is_oobe=*/false));
+}
+
+TEST_F(WallpaperPrefManagerTest,
+       ShouldNotSyncInIfSyncPrefHasUnsupportedVersion) {
+  base::test::ScopedFeatureList features(features::kVersionedWallpaperInfo);
+  WallpaperInfo local_info = InfoWithType(WallpaperType::kOnline);
+  WallpaperInfo synced_info = InfoWithType(WallpaperType::kOnline);
+  synced_info.version = base::Version("99.99");
+
+  EXPECT_FALSE(WallpaperPrefManager::ShouldSyncIn(synced_info, local_info,
+                                                  /*is_oobe=*/false));
+}
+
+TEST_F(WallpaperPrefManagerTest,
+       ShouldSyncInIfSyncPrefHasSameVersionAsSupportedVersion) {
+  base::test::ScopedFeatureList features(features::kVersionedWallpaperInfo);
+  WallpaperInfo local_info = InfoWithType(WallpaperType::kOnline);
+  WallpaperInfo synced_info = InfoWithType(WallpaperType::kDaily);
+  synced_info.version = base::Version("1.0");
+
+  EXPECT_TRUE(WallpaperPrefManager::ShouldSyncIn(synced_info, local_info,
+                                                 /*is_oobe=*/false));
+}
+
+TEST_F(WallpaperPrefManagerTest,
+       ShouldSyncInIfSyncPrefHasSameMajorButDifferentMinorVersion) {
+  base::test::ScopedFeatureList features(features::kVersionedWallpaperInfo);
+  WallpaperInfo local_info = InfoWithType(WallpaperType::kOnline);
+  WallpaperInfo synced_info = InfoWithType(WallpaperType::kOnline);
+  synced_info.version = base::Version("1.1");
+
+  EXPECT_TRUE(WallpaperPrefManager::ShouldSyncIn(synced_info, local_info,
+                                                 /*is_oobe=*/false));
+}
+
+TEST_F(WallpaperPrefManagerTest,
+       ShouldSyncInIfSyncPrefHasOlderVersionThanSupportedVersion) {
+  base::test::ScopedFeatureList features(features::kVersionedWallpaperInfo);
+  WallpaperInfo local_info = InfoWithType(WallpaperType::kOnline);
+  WallpaperInfo synced_info = InfoWithType(WallpaperType::kOnline);
+  synced_info.version = base::Version("0.1");
+
+  EXPECT_TRUE(WallpaperPrefManager::ShouldSyncIn(synced_info, local_info,
+                                                 /*is_oobe=*/false));
+}
+
+TEST_F(WallpaperPrefManagerTest,
+       VersionIsIgnoredWhenVersionWallpaperInfoIsDisabled) {
+  WallpaperInfo local_info = InfoWithType(WallpaperType::kOnline);
+  local_info.version = base::Version("1.0");
+  WallpaperInfo synced_info = InfoWithType(WallpaperType::kDaily);
+  synced_info.version = base::Version("2.0");
+
+  EXPECT_TRUE(WallpaperPrefManager::ShouldSyncIn(synced_info, local_info,
+                                                 /*is_oobe=*/false));
+}
+
 // Verifies that creating a wallpaper info from prefs with an invalid layout
 // enum fails.
 TEST_F(WallpaperPrefManagerTest, GetSyncedWallpaperInfo_InvalidLayoutEnum) {
diff --git a/ash/webui/common/resources/accessibility/macro_names.ts b/ash/webui/common/resources/accessibility/macro_names.ts
index cd1091b..e919dba 100644
--- a/ash/webui/common/resources/accessibility/macro_names.ts
+++ b/ash/webui/common/resources/accessibility/macro_names.ts
@@ -152,6 +152,12 @@
   // Generates a synthetic down arrow key event.
   KEY_PRESS_DOWN = 42,
 
+  // Shows/hides the overview of the user's active desktops.
+  KEY_PRESS_TOGGLE_OVERVIEW = 43,
+
+  // Pauses/plays active media.
+  KEY_PRESS_MEDIA_PLAY_PAUSE = 44,
+
   // Any new actions should match with Voice Access's semantic tags where
   // possible.
 }
diff --git a/ash/wm/overview/overview_drop_target.cc b/ash/wm/overview/overview_drop_target.cc
index 3884a0c..b6f5e17 100644
--- a/ash/wm/overview/overview_drop_target.cc
+++ b/ash/wm/overview/overview_drop_target.cc
@@ -199,11 +199,11 @@
 
 void OverviewDropTarget::StopWidgetAnimation() {}
 
-OverviewGridWindowFillMode OverviewDropTarget::GetWindowDimensionsType() const {
-  return OverviewGridWindowFillMode::kNormal;
+OverviewItemFillMode OverviewDropTarget::GetOverviewItemFillMode() const {
+  return OverviewItemFillMode::kNormal;
 }
 
-void OverviewDropTarget::UpdateWindowDimensionsType() {}
+void OverviewDropTarget::UpdateOverviewItemFillMode() {}
 
 gfx::Point OverviewDropTarget::GetMagnifierFocusPointInScreen() const {
   return gfx::Point();
diff --git a/ash/wm/overview/overview_drop_target.h b/ash/wm/overview/overview_drop_target.h
index b1b372b..eb144620 100644
--- a/ash/wm/overview/overview_drop_target.h
+++ b/ash/wm/overview/overview_drop_target.h
@@ -73,8 +73,8 @@
   void Shutdown() override;
   void AnimateAndCloseItem(bool up) override;
   void StopWidgetAnimation() override;
-  OverviewGridWindowFillMode GetWindowDimensionsType() const override;
-  void UpdateWindowDimensionsType() override;
+  OverviewItemFillMode GetOverviewItemFillMode() const override;
+  void UpdateOverviewItemFillMode() override;
   gfx::Point GetMagnifierFocusPointInScreen() const override;
   const gfx::RoundedCornersF GetRoundedCorners() const override;
 
diff --git a/ash/wm/overview/overview_grid.cc b/ash/wm/overview/overview_grid.cc
index cd6bcb1..5881496 100644
--- a/ash/wm/overview/overview_grid.cc
+++ b/ash/wm/overview/overview_grid.cc
@@ -1948,7 +1948,7 @@
     int height) {
   gfx::SizeF target_size = item->GetWindowsUnionScreenBounds().size();
   float scale = item->GetItemScale(height);
-  OverviewGridWindowFillMode grid_fill_mode = item->GetWindowDimensionsType();
+  OverviewItemFillMode item_fill_mode = item->GetOverviewItemFillMode();
 
   // The drop target, unlike the other windows has its bounds set directly, so
   // `GetWindowsUnionScreenBounds()` won't return the value we want. Instead,
@@ -1969,8 +1969,7 @@
         // this grid, the drop target size should reflect the maximized window
         // size on this grid's display (i.e. this display's work area size)
         // which can be different than the source display's work area size.
-        grid_fill_mode = ScopedOverviewTransformWindow::GetWindowDimensionsType(
-            work_area_size);
+        item_fill_mode = GetOverviewItemFillMode(work_area_size);
         target_size = gfx::SizeF(work_area_size);
       } else {
         // If the drag started from a different root window, `dragged_windows`
@@ -1981,8 +1980,7 @@
         gfx::Size dragged_item_size =
             GetTotalDraggedWindowsSize(dragged_windows);
         dragged_item_size.SetToMin(work_area_size);
-        grid_fill_mode = ScopedOverviewTransformWindow::GetWindowDimensionsType(
-            dragged_item_size);
+        item_fill_mode = GetOverviewItemFillMode(dragged_item_size);
         target_size = GetTotalUnionSizeIncludingTransients(dragged_windows);
         target_size.SetToMin(gfx::SizeF(work_area_size));
       }
@@ -1994,11 +1992,11 @@
   }
 
   int width = std::max(1, base::ClampFloor(target_size.width() * scale));
-  switch (grid_fill_mode) {
-    case OverviewGridWindowFillMode::kLetterBoxed:
+  switch (item_fill_mode) {
+    case OverviewItemFillMode::kLetterBoxed:
       width = kExtremeWindowRatioThreshold * height;
       break;
-    case OverviewGridWindowFillMode::kPillarBoxed:
+    case OverviewItemFillMode::kPillarBoxed:
       width = height / kExtremeWindowRatioThreshold;
       break;
     default:
diff --git a/ash/wm/overview/overview_group_item.cc b/ash/wm/overview/overview_group_item.cc
index e90f9b40..16e76d6 100644
--- a/ash/wm/overview/overview_group_item.cc
+++ b/ash/wm/overview/overview_group_item.cc
@@ -10,12 +10,16 @@
 #include "ash/style/rounded_label_widget.h"
 #include "ash/wm/desks/desks_util.h"
 #include "ash/wm/overview/overview_constants.h"
+#include "ash/wm/overview/overview_controller.h"
+#include "ash/wm/overview/overview_focus_cycler_old.h"
 #include "ash/wm/overview/overview_focusable_view.h"
 #include "ash/wm/overview/overview_grid.h"
 #include "ash/wm/overview/overview_group_container_view.h"
 #include "ash/wm/overview/overview_item.h"
 #include "ash/wm/overview/overview_item_base.h"
 #include "ash/wm/overview/overview_item_view.h"
+#include "ash/wm/overview/overview_session.h"
+#include "ash/wm/overview/overview_utils.h"
 #include "ash/wm/snap_group/snap_group.h"
 #include "ash/wm/snap_group/snap_group_controller.h"
 #include "ash/wm/splitview/layout_divider_controller.h"
@@ -29,6 +33,7 @@
 #include "base/notreached.h"
 #include "base/trace_event/trace_event.h"
 #include "third_party/abseil-cpp/absl/cleanup/cleanup.h"
+#include "ui/gfx/geometry/point.h"
 #include "ui/gfx/geometry/rect_f.h"
 #include "ui/gfx/geometry/rounded_corners_f.h"
 #include "ui/views/widget/widget.h"
@@ -198,7 +203,7 @@
     return item0->SetBounds(target_bounds, animation_type);
   }
 
-  CHECK_EQ(size, 2);
+  CHECK_EQ(2, size);
   auto& item1 = overview_items_[1];
 
   aura::Window* item0_window = item0->GetWindow();
@@ -364,6 +369,10 @@
 }
 
 void OverviewGroupItem::PrepareForOverview() {
+  for (const auto& overview_item : overview_items_) {
+    overview_item->PrepareForOverview();
+  }
+
   prepared_for_overview_ = true;
 }
 
@@ -468,25 +477,50 @@
   }
 }
 
-void OverviewGroupItem::Shutdown() {}
+void OverviewGroupItem::Shutdown() {
+  for (const auto& overview_item : overview_items_) {
+    overview_item->Shutdown();
+  }
+}
 
 void OverviewGroupItem::AnimateAndCloseItem(bool up) {}
 
-void OverviewGroupItem::StopWidgetAnimation() {}
+void OverviewGroupItem::StopWidgetAnimation() {
+  for (const auto& overview_item : overview_items_) {
+    overview_item->StopWidgetAnimation();
+  }
 
-OverviewGridWindowFillMode OverviewGroupItem::GetWindowDimensionsType() const {
-  // This return value assumes that the snap group represented by `this` will
-  // occupy the entire work space. So it's mostly likely that the window
-  // dimension type will be normal.
-  // TODO(michelefan): Consider the corner cases when the work space has
-  // abnormal dimension ratios.
-  return OverviewGridWindowFillMode::kNormal;
+  item_widget_->GetNativeWindow()->layer()->GetAnimator()->StopAnimating();
 }
 
-void OverviewGroupItem::UpdateWindowDimensionsType() {}
+OverviewItemFillMode OverviewGroupItem::GetOverviewItemFillMode() const {
+  return ash::GetOverviewItemFillMode(
+      gfx::ToRoundedSize(target_bounds_.size()));
+}
+
+void OverviewGroupItem::UpdateOverviewItemFillMode() {
+  for (const auto& overview_item : overview_items_) {
+    overview_item->UpdateOverviewItemFillMode();
+  }
+}
 
 gfx::Point OverviewGroupItem::GetMagnifierFocusPointInScreen() const {
-  return overview_group_container_view_->GetBoundsInScreen().CenterPoint();
+  CHECK(!overview_items_.empty());
+
+  OverviewSession* overview_session =
+      OverviewController::Get()->overview_session();
+  CHECK(overview_session);
+  OverviewFocusCyclerOld* focus_cycler_old =
+      overview_session->focus_cycler_old();
+  for (const auto& overview_item : overview_items_) {
+    if (overview_item->overview_item_view() ==
+        focus_cycler_old->focused_view()) {
+      return overview_item->GetMagnifierFocusPointInScreen();
+    }
+  }
+
+  NOTREACHED_IN_MIGRATION();
+  return gfx::Point();
 }
 
 const gfx::RoundedCornersF OverviewGroupItem::GetRoundedCorners() const {
diff --git a/ash/wm/overview/overview_group_item.h b/ash/wm/overview/overview_group_item.h
index a98794f..c60dd65b 100644
--- a/ash/wm/overview/overview_group_item.h
+++ b/ash/wm/overview/overview_group_item.h
@@ -83,8 +83,8 @@
   void Shutdown() override;
   void AnimateAndCloseItem(bool up) override;
   void StopWidgetAnimation() override;
-  OverviewGridWindowFillMode GetWindowDimensionsType() const override;
-  void UpdateWindowDimensionsType() override;
+  OverviewItemFillMode GetOverviewItemFillMode() const override;
+  void UpdateOverviewItemFillMode() override;
   gfx::Point GetMagnifierFocusPointInScreen() const override;
   const gfx::RoundedCornersF GetRoundedCorners() const override;
 
diff --git a/ash/wm/overview/overview_item.cc b/ash/wm/overview/overview_item.cc
index 17379bb..d805dcc 100644
--- a/ash/wm/overview/overview_item.cc
+++ b/ash/wm/overview/overview_item.cc
@@ -528,8 +528,8 @@
           screen_rect, transformed_bounds, top_view_inset,
           kWindowMiniViewHeaderHeight);
 
-  if (transform_window_.type() == OverviewGridWindowFillMode::kNormal ||
-      transform_window_.type() == OverviewGridWindowFillMode::kLetterBoxed) {
+  if (transform_window_.fill_mode() == OverviewItemFillMode::kNormal ||
+      transform_window_.fill_mode() == OverviewItemFillMode::kLetterBoxed) {
     overview_item_bounds.set_x(transformed_bounds.x());
     overview_item_bounds.set_width(transformed_bounds.width());
   }
@@ -538,8 +538,8 @@
   // normal or pillar dimensions type to make sure there's no gap between the
   // header and the window and no empty space at the end of the overview item
   // container.
-  if (transform_window_.type() == OverviewGridWindowFillMode::kNormal ||
-      transform_window_.type() == OverviewGridWindowFillMode::kPillarBoxed) {
+  if (transform_window_.fill_mode() == OverviewItemFillMode::kNormal ||
+      transform_window_.fill_mode() == OverviewItemFillMode::kPillarBoxed) {
     if (!overview_item_view_->header_view()->GetBoundsInScreen().IsEmpty()) {
       // The window top bar's target height with the transform.
       const float window_top_inset_target_height =
@@ -717,7 +717,7 @@
   }
 
   const bool show_backdrop =
-      GetWindowDimensionsType() != OverviewGridWindowFillMode::kNormal;
+      GetOverviewItemFillMode() != OverviewItemFillMode::kNormal;
   overview_item_view_->SetBackdropVisibility(show_backdrop);
   UpdateCannotSnapWarningVisibility(/*animate=*/true);
 }
@@ -931,14 +931,14 @@
   item_widget_->GetNativeWindow()->layer()->GetAnimator()->StopAnimating();
 }
 
-OverviewGridWindowFillMode OverviewItem::GetWindowDimensionsType() const {
-  return transform_window_.type();
+OverviewItemFillMode OverviewItem::GetOverviewItemFillMode() const {
+  return transform_window_.fill_mode();
 }
 
-void OverviewItem::UpdateWindowDimensionsType() {
-  transform_window_.UpdateWindowDimensionsType();
+void OverviewItem::UpdateOverviewItemFillMode() {
+  transform_window_.UpdateOverviewItemFillMode();
   const bool show_backdrop =
-      GetWindowDimensionsType() != OverviewGridWindowFillMode::kNormal;
+      GetOverviewItemFillMode() != OverviewItemFillMode::kNormal;
   overview_item_view_->SetBackdropVisibility(show_backdrop);
 }
 
@@ -1037,7 +1037,7 @@
   // Immediately finish any active bounds animation.
   window->layer()->GetAnimator()->StopAnimatingProperty(
       ui::LayerAnimationElement::BOUNDS);
-  UpdateWindowDimensionsType();
+  UpdateOverviewItemFillMode();
   overview_grid_->PositionWindows(/*animate=*/false);
 }
 
diff --git a/ash/wm/overview/overview_item.h b/ash/wm/overview/overview_item.h
index c8d6885..c7c4e6a 100644
--- a/ash/wm/overview/overview_item.h
+++ b/ash/wm/overview/overview_item.h
@@ -136,8 +136,8 @@
   void Shutdown() override;
   void AnimateAndCloseItem(bool up) override;
   void StopWidgetAnimation() override;
-  OverviewGridWindowFillMode GetWindowDimensionsType() const override;
-  void UpdateWindowDimensionsType() override;
+  OverviewItemFillMode GetOverviewItemFillMode() const override;
+  void UpdateOverviewItemFillMode() override;
   gfx::Point GetMagnifierFocusPointInScreen() const override;
   const gfx::RoundedCornersF GetRoundedCorners() const override;
 
diff --git a/ash/wm/overview/overview_item_base.h b/ash/wm/overview/overview_item_base.h
index a9b50d8..777e800 100644
--- a/ash/wm/overview/overview_item_base.h
+++ b/ash/wm/overview/overview_item_base.h
@@ -294,11 +294,10 @@
   // Stops the current animation of `item_widget_`.
   virtual void StopWidgetAnimation() = 0;
 
-  virtual OverviewGridWindowFillMode GetWindowDimensionsType() const = 0;
+  virtual OverviewItemFillMode GetOverviewItemFillMode() const = 0;
 
-  // Recalculates the window dimensions type of the transform window. Called on
-  // window bounds change.
-  virtual void UpdateWindowDimensionsType() = 0;
+  // Updates the `OverviewItemFillMode` for this item.
+  virtual void UpdateOverviewItemFillMode() = 0;
 
   // Returns the point the accessibility magnifiers should focus on when `this`
   // is focused.
diff --git a/ash/wm/overview/overview_item_view.cc b/ash/wm/overview/overview_item_view.cc
index 673676be..2c4fa92 100644
--- a/ash/wm/overview/overview_item_view.cc
+++ b/ash/wm/overview/overview_item_view.cc
@@ -10,6 +10,7 @@
 #include "ash/style/close_button.h"
 #include "ash/wm/overview/overview_grid.h"
 #include "ash/wm/overview/overview_item.h"
+#include "ash/wm/overview/overview_types.h"
 #include "ash/wm/snap_group/snap_group.h"
 #include "ash/wm/snap_group/snap_group_controller.h"
 #include "ash/wm/window_mini_view_header_view.h"
@@ -159,16 +160,16 @@
   const float aspect_ratio =
       preview_pref_size.width() / preview_pref_size.height();
   gfx::SizeF target_size(GetContentAreaBounds().size());
-  OverviewGridWindowFillMode fill_mode =
-      overview_item_ ? overview_item_->GetWindowDimensionsType()
-                     : OverviewGridWindowFillMode::kNormal;
+  OverviewItemFillMode fill_mode =
+      overview_item_ ? overview_item_->GetOverviewItemFillMode()
+                     : OverviewItemFillMode::kNormal;
   switch (fill_mode) {
-    case OverviewGridWindowFillMode::kNormal:
+    case OverviewItemFillMode::kNormal:
       break;
-    case OverviewGridWindowFillMode::kLetterBoxed:
+    case OverviewItemFillMode::kLetterBoxed:
       target_size.set_height(target_size.width() / aspect_ratio);
       break;
-    case OverviewGridWindowFillMode::kPillarBoxed:
+    case OverviewItemFillMode::kPillarBoxed:
       target_size.set_width(target_size.height() * aspect_ratio);
       break;
   }
diff --git a/ash/wm/overview/overview_session_unittest.cc b/ash/wm/overview/overview_session_unittest.cc
index 3d40e40..464bec47 100644
--- a/ash/wm/overview/overview_session_unittest.cc
+++ b/ash/wm/overview/overview_session_unittest.cc
@@ -3068,8 +3068,8 @@
 
   ToggleOverview();
   auto* item = GetOverviewItemForWindow(window.get());
-  ASSERT_EQ(OverviewGridWindowFillMode::kLetterBoxed,
-            item->GetWindowDimensionsType());
+  ASSERT_EQ(OverviewItemFillMode::kLetterBoxed,
+            item->GetOverviewItemFillMode());
 
   // Tap the target.
   GetEventGenerator()->set_current_screen_location(
@@ -10571,8 +10571,8 @@
   // Verify that |item| is letter boxed. The bounds of |item|, minus the margin
   // should have an aspect ratio of 2 : 1.
   gfx::RectF item_bounds = item->target_bounds();
-  EXPECT_EQ(OverviewGridWindowFillMode::kLetterBoxed,
-            item->GetWindowDimensionsType());
+  EXPECT_EQ(OverviewItemFillMode::kLetterBoxed,
+            item->GetOverviewItemFillMode());
   EXPECT_EQ(2.f, item_bounds.width() / item_bounds.height());
   auto* event_generator = GetEventGenerator();
   event_generator->MoveMouseTo(gfx::ToRoundedPoint(item_bounds.CenterPoint()));
@@ -10584,10 +10584,11 @@
 
   auto* drop_target = GetDropTarget(1);
   ASSERT_TRUE(drop_target);
-  // Verify that |drop_target| is effectively pillar boxed. Avoid calling
-  // |OverviewItem::GetWindowDimensionsType|, because it does not work for drop
-  // targets (and that is okay). The bounds of |drop_target|, minus the margin
-  // should have an aspect ratio of 1 : 2.
+
+  // Verify that `drop_target` is effectively pillar boxed. Avoid calling
+  // `OverviewItem::GetOverviewItemFillMode()`, because it does not work for
+  // drop targets (and that is okay). The bounds of `drop_target`, minus the
+  // margin should have an aspect ratio of 1 : 2.
   const gfx::Size drop_target_size =
       drop_target->item_widget()->GetWindowBoundsInScreen().size();
   EXPECT_EQ(0.5f, static_cast<float>(drop_target_size.width()) /
@@ -10637,15 +10638,13 @@
   auto* item4 = GetOverviewItemForWindow(window4.get());
 
   // For good test coverage in each case, the dragged window and the drop target
-  // have different |OverviewGridWindowFillMode| values.
-  EXPECT_EQ(OverviewGridWindowFillMode::kNormal,
-            item1->GetWindowDimensionsType());
-  EXPECT_EQ(OverviewGridWindowFillMode::kLetterBoxed,
-            item2->GetWindowDimensionsType());
-  EXPECT_EQ(OverviewGridWindowFillMode::kNormal,
-            item3->GetWindowDimensionsType());
-  EXPECT_EQ(OverviewGridWindowFillMode::kPillarBoxed,
-            item4->GetWindowDimensionsType());
+  // have different `OverviewItemFillMode` values.
+  EXPECT_EQ(OverviewItemFillMode::kNormal, item1->GetOverviewItemFillMode());
+  EXPECT_EQ(OverviewItemFillMode::kLetterBoxed,
+            item2->GetOverviewItemFillMode());
+  EXPECT_EQ(OverviewItemFillMode::kNormal, item3->GetOverviewItemFillMode());
+  EXPECT_EQ(OverviewItemFillMode::kPillarBoxed,
+            item4->GetOverviewItemFillMode());
 
   EXPECT_EQ(root1_drop_target_bounds(item1), root1_drop_target_bounds(item2));
   EXPECT_EQ(root1_drop_target_bounds(item3), root1_drop_target_bounds(item4));
diff --git a/ash/wm/overview/overview_types.h b/ash/wm/overview/overview_types.h
index ab0593d..ae77ce2c 100644
--- a/ash/wm/overview/overview_types.h
+++ b/ash/wm/overview/overview_types.h
@@ -101,7 +101,7 @@
 // Overview items have certain properties if their aspect ratio exceeds a
 // threshold. This enum keeps track of which category the window falls into,
 // based on its aspect ratio.
-enum class OverviewGridWindowFillMode {
+enum class OverviewItemFillMode {
   // Aspect ratio is between 1:2 and 2:1.
   kNormal,
   // Width to height ratio exceeds 2:1. The overview item will have a 2:1
diff --git a/ash/wm/overview/overview_utils.cc b/ash/wm/overview/overview_utils.cc
index 45f5166..a112906 100644
--- a/ash/wm/overview/overview_utils.cc
+++ b/ash/wm/overview/overview_utils.cc
@@ -18,6 +18,7 @@
 #include "ash/wm/mru_window_tracker.h"
 #include "ash/wm/overview/cleanup_animation_observer.h"
 #include "ash/wm/overview/delayed_animation_observer_impl.h"
+#include "ash/wm/overview/overview_constants.h"
 #include "ash/wm/overview/overview_controller.h"
 #include "ash/wm/overview/overview_focus_cycler_old.h"
 #include "ash/wm/overview/overview_grid.h"
@@ -153,6 +154,18 @@
   return bounds;
 }
 
+OverviewItemFillMode GetOverviewItemFillMode(const gfx::Size& size) {
+  if (size.width() > size.height() * kExtremeWindowRatioThreshold) {
+    return OverviewItemFillMode::kLetterBoxed;
+  }
+
+  if (size.height() > size.width() * kExtremeWindowRatioThreshold) {
+    return OverviewItemFillMode::kPillarBoxed;
+  }
+
+  return OverviewItemFillMode::kNormal;
+}
+
 void MaximizeIfSnapped(aura::Window* window) {
   auto* window_state = WindowState::Get(window);
   if (window_state && window_state->IsSnapped()) {
diff --git a/ash/wm/overview/overview_utils.h b/ash/wm/overview/overview_utils.h
index 14833ab..19bffc37 100644
--- a/ash/wm/overview/overview_utils.h
+++ b/ash/wm/overview/overview_utils.h
@@ -66,6 +66,8 @@
 // window's transient hierarchy.
 ASH_EXPORT gfx::RectF GetUnionScreenBoundsForWindow(aura::Window* window);
 
+OverviewItemFillMode GetOverviewItemFillMode(const gfx::Size& size);
+
 // Maximize the window if it is snapped without animation.
 void MaximizeIfSnapped(aura::Window* window);
 
diff --git a/ash/wm/overview/scoped_overview_transform_window.cc b/ash/wm/overview/scoped_overview_transform_window.cc
index 78002c9..136e1213c 100644
--- a/ash/wm/overview/scoped_overview_transform_window.cc
+++ b/ash/wm/overview/scoped_overview_transform_window.cc
@@ -152,7 +152,7 @@
       (new RasterScaleLayerObserver(window_, window_->layer(), window_))
           ->Lock());
 
-  type_ = GetWindowDimensionsType(window->bounds().size());
+  fill_mode_ = GetOverviewItemFillMode(window->bounds().size());
 
   std::vector<raw_ptr<aura::Window, VectorExperimental>>
       transient_children_to_hide;
@@ -243,18 +243,6 @@
                             (source_height - top_view_inset));
 }
 
-// static
-OverviewGridWindowFillMode
-ScopedOverviewTransformWindow::GetWindowDimensionsType(const gfx::Size& size) {
-  if (size.width() > size.height() * kExtremeWindowRatioThreshold)
-    return OverviewGridWindowFillMode::kLetterBoxed;
-
-  if (size.height() > size.width() * kExtremeWindowRatioThreshold)
-    return OverviewGridWindowFillMode::kPillarBoxed;
-
-  return OverviewGridWindowFillMode::kNormal;
-}
-
 void ScopedOverviewTransformWindow::RestoreWindow(bool reset_transform,
                                                   bool animate) {
   base::AutoReset<bool> restoring(&is_restoring_, true);
@@ -419,13 +407,13 @@
   gfx::RectF new_bounds(bounds.x() + horizontal_offset,
                         bounds.y() + vertical_offset, width, height);
 
-  switch (type()) {
-    case OverviewGridWindowFillMode::kLetterBoxed:
-    case OverviewGridWindowFillMode::kPillarBoxed: {
+  switch (fill_mode_) {
+    case OverviewItemFillMode::kLetterBoxed:
+    case OverviewItemFillMode::kPillarBoxed: {
       // Attempt to scale |rect| to fit |bounds|. Maintain the aspect ratio of
       // |rect|. Letter boxed windows' width will match |bounds|'s width and
       // pillar boxed windows' height will match |bounds|'s height.
-      const bool is_pillar = type() == OverviewGridWindowFillMode::kPillarBoxed;
+      const bool is_pillar = fill_mode_ == OverviewItemFillMode::kPillarBoxed;
       const gfx::Rect window_bounds =
           ::wm::GetTransientRoot(window_)->GetBoundsInScreen();
       const float window_ratio =
@@ -520,8 +508,8 @@
   original_opacity_ = 1.f;
 }
 
-void ScopedOverviewTransformWindow::UpdateWindowDimensionsType() {
-  type_ = GetWindowDimensionsType(window_->bounds().size());
+void ScopedOverviewTransformWindow::UpdateOverviewItemFillMode() {
+  fill_mode_ = ash::GetOverviewItemFillMode(window_->bounds().size());
 }
 
 void ScopedOverviewTransformWindow::UpdateRoundedCorners(bool show) {
diff --git a/ash/wm/overview/scoped_overview_transform_window.h b/ash/wm/overview/scoped_overview_transform_window.h
index ec45bb6b..a323eb6 100644
--- a/ash/wm/overview/scoped_overview_transform_window.h
+++ b/ash/wm/overview/scoped_overview_transform_window.h
@@ -51,9 +51,6 @@
                             int top_view_inset,
                             int title_height);
 
-  static OverviewGridWindowFillMode GetWindowDimensionsType(
-      const gfx::Size& size);
-
   ScopedOverviewTransformWindow(OverviewItem* overview_item,
                                 aura::Window* window);
   ScopedOverviewTransformWindow(const ScopedOverviewTransformWindow&) = delete;
@@ -65,7 +62,7 @@
 
   bool is_restoring() const { return is_restoring_; }
 
-  OverviewGridWindowFillMode type() const { return type_; }
+  OverviewItemFillMode fill_mode() const { return fill_mode_; }
 
   // Starts an animation sequence which will use animation settings specified by
   // |animation_type|. The |animation_settings| container is populated with
@@ -134,7 +131,7 @@
 
   // Called via OverviewItem from OverviewGrid when |window_|'s bounds
   // change. Must be called before PositionWindows in OverviewGrid.
-  void UpdateWindowDimensionsType();
+  void UpdateOverviewItemFillMode();
 
   // Updates the rounded corners on `window_` and its transient hierarchy (if
   // needed).
@@ -192,7 +189,7 @@
   float original_opacity_;
 
   // Specifies how the window is laid out in the grid.
-  OverviewGridWindowFillMode type_ = OverviewGridWindowFillMode::kNormal;
+  OverviewItemFillMode fill_mode_ = OverviewItemFillMode::kNormal;
 
   // The observers associated with the layers we requested caching render
   // surface and trilinear filtering. The requests will be removed in dtor if
diff --git a/ash/wm/overview/scoped_overview_transform_window_unittest.cc b/ash/wm/overview/scoped_overview_transform_window_unittest.cc
index ee8ac25..9ac7b57 100644
--- a/ash/wm/overview/scoped_overview_transform_window_unittest.cc
+++ b/ash/wm/overview/scoped_overview_transform_window_unittest.cc
@@ -6,6 +6,7 @@
 
 #include "ash/public/cpp/window_properties.h"
 #include "ash/test/ash_test_base.h"
+#include "ash/wm/overview/overview_types.h"
 #include "ash/wm/overview/overview_utils.h"
 #include "ash/wm/window_state.h"
 #include "ash/wm/window_util.h"
@@ -138,7 +139,7 @@
   const int scale = 3;
   std::unique_ptr<aura::Window> window = CreateTestWindow(original_bounds);
   ScopedOverviewTransformWindow transform_window(nullptr, window.get());
-  EXPECT_EQ(OverviewGridWindowFillMode::kLetterBoxed, transform_window.type());
+  EXPECT_EQ(OverviewItemFillMode::kLetterBoxed, transform_window.fill_mode());
 
   // Without any headers, the width should match the target, and the height
   // should be such that the aspect ratio of |original_bounds| is maintained.
@@ -171,7 +172,7 @@
   const int scale = 3;
   std::unique_ptr<aura::Window> window = CreateTestWindow(original_bounds);
   ScopedOverviewTransformWindow transform_window(nullptr, window.get());
-  EXPECT_EQ(OverviewGridWindowFillMode::kPillarBoxed, transform_window.type());
+  EXPECT_EQ(OverviewItemFillMode::kPillarBoxed, transform_window.fill_mode());
 
   // Without any headers, the height should match the target, and the width
   // should be such that the aspect ratio of |original_bounds| is maintained.
@@ -215,25 +216,24 @@
 
   // Verify the window dimension type is as expected after entering overview
   // mode.
-  using OverviewGridWindowFillMode = OverviewGridWindowFillMode;
-  EXPECT_EQ(OverviewGridWindowFillMode::kLetterBoxed, scoped_wide.type());
-  EXPECT_EQ(OverviewGridWindowFillMode::kPillarBoxed, scoped_tall.type());
-  EXPECT_EQ(OverviewGridWindowFillMode::kNormal, scoped_normal.type());
+  EXPECT_EQ(OverviewItemFillMode::kLetterBoxed, scoped_wide.fill_mode());
+  EXPECT_EQ(OverviewItemFillMode::kPillarBoxed, scoped_tall.fill_mode());
+  EXPECT_EQ(OverviewItemFillMode::kNormal, scoped_normal.fill_mode());
 
   display::Screen* screen = display::Screen::GetScreen();
   const display::Display& display = screen->GetPrimaryDisplay();
   display_manager()->SetDisplayRotation(
       display.id(), display::Display::ROTATE_90,
       display::Display::RotationSource::ACTIVE);
-  scoped_wide.UpdateWindowDimensionsType();
-  scoped_tall.UpdateWindowDimensionsType();
-  scoped_normal.UpdateWindowDimensionsType();
+  scoped_wide.UpdateOverviewItemFillMode();
+  scoped_tall.UpdateOverviewItemFillMode();
+  scoped_normal.UpdateOverviewItemFillMode();
 
   // Verify that |wide| has its window dimension type updated after the display
   // change.
-  EXPECT_EQ(OverviewGridWindowFillMode::kNormal, scoped_wide.type());
-  EXPECT_EQ(OverviewGridWindowFillMode::kPillarBoxed, scoped_tall.type());
-  EXPECT_EQ(OverviewGridWindowFillMode::kNormal, scoped_normal.type());
+  EXPECT_EQ(OverviewItemFillMode::kNormal, scoped_wide.fill_mode());
+  EXPECT_EQ(OverviewItemFillMode::kPillarBoxed, scoped_tall.fill_mode());
+  EXPECT_EQ(OverviewItemFillMode::kNormal, scoped_normal.fill_mode());
 }
 
 // Tests that transients which should be invisible in overview do not have their
diff --git a/base/BUILD.gn b/base/BUILD.gn
index 0fd8801..f16210d 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -4842,6 +4842,7 @@
       "test/android/javatests/src/org/chromium/base/test/util/AnnotationRule.java",
       "test/android/javatests/src/org/chromium/base/test/util/ApplicationContextWrapper.java",
       "test/android/javatests/src/org/chromium/base/test/util/ApplicationTestUtils.java",
+      "test/android/javatests/src/org/chromium/base/test/util/BaseRestrictions.java",
       "test/android/javatests/src/org/chromium/base/test/util/Batch.java",
       "test/android/javatests/src/org/chromium/base/test/util/CallbackHelper.java",
       "test/android/javatests/src/org/chromium/base/test/util/CommandLineFlags.java",
@@ -4983,6 +4984,7 @@
       "android/junit/src/org/chromium/base/PostNativeFlagUnitTest.java",
       "android/junit/src/org/chromium/base/PromiseTest.java",
       "android/junit/src/org/chromium/base/ResettersForTestingTest.java",
+      "android/junit/src/org/chromium/base/ThreadUtilsTest.java",
       "android/junit/src/org/chromium/base/TimeUtilsTest.java",
       "android/junit/src/org/chromium/base/TokenUnitTest.java",
       "android/junit/src/org/chromium/base/TraceEventTest.java",
diff --git a/base/android/java/src/org/chromium/base/ThreadUtils.java b/base/android/java/src/org/chromium/base/ThreadUtils.java
index 8dfe3ea5d..f82254bd 100644
--- a/base/android/java/src/org/chromium/base/ThreadUtils.java
+++ b/base/android/java/src/org/chromium/base/ThreadUtils.java
@@ -47,14 +47,16 @@
      */
     // TODO(b/274802355): Add @CheckDiscard once R8 can remove this.
     public static class ThreadChecker {
-        private long mThreadId;
+        private Thread mThread;
 
         public ThreadChecker() {
             resetThreadId();
         }
 
         public void resetThreadId() {
-            mThreadId = BuildConfig.ENABLE_ASSERTS ? Process.myTid() : 0;
+            if (BuildConfig.ENABLE_ASSERTS) {
+                mThread = Thread.currentThread();
+            }
         }
 
         /**
@@ -62,8 +64,25 @@
          * on.
          */
         public void assertOnValidThread() {
-            assert sThreadAssertsDisabledForTesting || mThreadId == Process.myTid()
-                    : "Must only be used on a single thread.";
+            if (BuildConfig.ENABLE_ASSERTS && !sThreadAssertsDisabledForTesting) {
+                Thread curThread = Thread.currentThread();
+                if (curThread != mThread) {
+                    Thread uiThread = getUiThreadLooper().getThread();
+                    if (curThread == uiThread) {
+                        assert false
+                                : "Background-only class called from UI thread (expected: "
+                                        + mThread
+                                        + ")";
+                    } else if (mThread == uiThread) {
+                        assert false : "UI-only class called from background thread: " + curThread;
+                    }
+                    assert false
+                            : "Method called from wrong background thread. Expected: "
+                                    + mThread
+                                    + " Actual: "
+                                    + curThread;
+                }
+            }
         }
     }
 
diff --git a/base/android/java/src/org/chromium/base/task/PostTask.java b/base/android/java/src/org/chromium/base/task/PostTask.java
index 16a5ca0..3005af21 100644
--- a/base/android/java/src/org/chromium/base/task/PostTask.java
+++ b/base/android/java/src/org/chromium/base/task/PostTask.java
@@ -130,19 +130,13 @@
      * same as the one corresponding to the SingleThreadTaskRunner, otherwise it
      * posts it and blocks until the task finishes.
      *
-     * It should be executed only for tasks with traits corresponding to
-     * executors backed by a SingleThreadTaskRunner, like TaskTraits.UI_*.
-     *
-     * Use this only for trivial tasks as it ignores task priorities.
-     *
-     * Note that non-test usage of this function is heavily discouraged. For non-tests, use
-     * callbacks rather than blocking threads.
+     * Usage outside of testing contexts is discouraged. Prefer callbacks in order
+     * to avoid blocking.
      *
      * @param taskTraits The TaskTraits that describe the desired TaskRunner.
      * @param task The task to be run with the specified traits.
      * @return The result of the callable
      */
-    @Deprecated
     public static <T> T runSynchronously(@TaskTraits int taskTraits, Callable<T> c) {
         return runSynchronouslyInternal(taskTraits, new FutureTask<T>(c));
     }
@@ -152,18 +146,12 @@
      * same as the one corresponding to the SingleThreadTaskRunner, otherwise it
      * posts it and blocks until the task finishes.
      *
-     * It should be executed only for tasks with traits corresponding to
-     * executors backed by a SingleThreadTaskRunner, like TaskTraits.UI_*.
-     *
-     * Use this only for trivial tasks as it ignores task priorities.
-     *
-     * Note that non-test usage of this function is heavily discouraged. For non-tests, use
-     * callbacks rather than blocking threads.
+     * Usage outside of testing contexts is discouraged. Prefer callbacks in order
+     * to avoid blocking.
      *
      * @param taskTraits The TaskTraits that describe the desired TaskRunner.
      * @param task The task to be run with the specified traits.
      */
-    @Deprecated
     public static void runSynchronously(@TaskTraits int taskTraits, Runnable r) {
         runSynchronouslyInternal(taskTraits, new FutureTask<Void>(r, null));
     }
diff --git a/base/android/junit/src/org/chromium/base/ThreadUtilsTest.java b/base/android/junit/src/org/chromium/base/ThreadUtilsTest.java
new file mode 100644
index 0000000..781418a
--- /dev/null
+++ b/base/android/junit/src/org/chromium/base/ThreadUtilsTest.java
@@ -0,0 +1,65 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base;
+
+import static org.hamcrest.core.StringStartsWith.startsWith;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Assert;
+import org.junit.Assume;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.Config;
+
+import org.chromium.base.ThreadUtils.ThreadChecker;
+import org.chromium.base.task.PostTask;
+import org.chromium.base.task.TaskTraits;
+import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.build.BuildConfig;
+
+/** Unit tests for ThreadUtils. */
+@RunWith(BaseRobolectricTestRunner.class)
+@Config(manifest = Config.NONE)
+public class ThreadUtilsTest {
+    @Test
+    @SmallTest
+    public void testThreadChecker_uiThread() {
+        Assume.assumeTrue(BuildConfig.ENABLE_ASSERTS);
+        ThreadChecker checker = new ThreadChecker();
+        checker.assertOnValidThread();
+
+        try {
+            PostTask.runSynchronously(TaskTraits.USER_BLOCKING, checker::assertOnValidThread);
+            Assert.fail("Expected AssertionError from ThreadChecker.");
+        } catch (RuntimeException r) {
+            AssertionError e;
+            try {
+                e = (AssertionError) r.getCause().getCause();
+            } catch (Throwable unused) {
+                throw new RuntimeException("Wrong Exception Type.", r);
+            }
+            Assert.assertThat(
+                    e.getMessage(), startsWith("UI-only class called from background thread"));
+        }
+    }
+
+    @Test
+    @SmallTest
+    public void testThreadChecker_backgroundThread() {
+        Assume.assumeTrue(BuildConfig.ENABLE_ASSERTS);
+        ThreadChecker[] checkerHolder = new ThreadChecker[1];
+        PostTask.runSynchronously(
+                TaskTraits.USER_BLOCKING,
+                () -> {
+                    checkerHolder[0] = new ThreadChecker();
+                });
+
+        AssertionError e =
+                Assert.assertThrows(AssertionError.class, checkerHolder[0]::assertOnValidThread);
+        Assert.assertThat(
+                e.getMessage(), startsWith("Background-only class called from UI thread"));
+    }
+}
diff --git a/base/files/scoped_file.cc b/base/files/scoped_file.cc
index 7ff0c17..7094e212 100644
--- a/base/files/scoped_file.cc
+++ b/base/files/scoped_file.cc
@@ -14,18 +14,19 @@
 #include "base/posix/eintr_wrapper.h"
 #endif
 
-namespace base {
-namespace internal {
+namespace base::internal {
 
 #if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
 
 // static
 void ScopedFDCloseTraits::Free(int fd) {
-  // It's important to crash here.
+  // It's important to crash here if something goes wrong.
+  //
   // There are security implications to not closing a file descriptor
   // properly. As file descriptors are "capabilities", keeping them open
   // would make the current process keep access to a resource. Much of
   // Chrome relies on being able to "drop" such access.
+  //
   // It's especially problematic on Linux with the setuid sandbox, where
   // a single open directory would bypass the entire security model.
   int ret = IGNORE_EINTR(close(fd));
@@ -36,8 +37,9 @@
   // filesystems such as NFS and Linux input devices. On Linux, macOS, and
   // Fuchsia's POSIX layer, errors from close other than EBADF do not indicate
   // failure to actually close the fd.
-  if (ret != 0 && errno != EBADF)
+  if (ret != 0 && errno != EBADF) {
     ret = 0;
+  }
 #endif
 
   PCHECK(0 == ret);
@@ -45,5 +47,4 @@
 
 #endif  // BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
 
-}  // namespace internal
-}  // namespace base
+}  // namespace base::internal
diff --git a/base/files/scoped_file.h b/base/files/scoped_file.h
index 23180de..181d821 100644
--- a/base/files/scoped_file.h
+++ b/base/files/scoped_file.h
@@ -17,38 +17,37 @@
 
 namespace internal {
 
-#if BUILDFLAG(IS_ANDROID)
-// Use fdsan on android.
+#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_LINUX)
+// Platforms for which it is possible to track ownership of file descriptors.
+//
+// On Android, fdsan is used.
+//
+// On ChromeOS and Linux, file descriptor lifetime is guarded with a global
+// table and a hook into libc close().
 struct BASE_EXPORT ScopedFDCloseTraits : public ScopedGenericOwnershipTracking {
   static int InvalidValue() { return -1; }
-  static void Free(int);
-  static void Acquire(const ScopedGeneric<int, ScopedFDCloseTraits>&, int);
-  static void Release(const ScopedGeneric<int, ScopedFDCloseTraits>&, int);
-};
-#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
-#if BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_LINUX)
-// On ChromeOS and Linux we guard FD lifetime with a global table and hook into
-// libc close() to perform checks.
-struct BASE_EXPORT ScopedFDCloseTraits : public ScopedGenericOwnershipTracking {
-#else
-struct BASE_EXPORT ScopedFDCloseTraits {
-#endif
-  static int InvalidValue() {
-    return -1;
-  }
   static void Free(int fd);
-#if BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_LINUX)
-  static void Acquire(const ScopedGeneric<int, ScopedFDCloseTraits>&, int);
-  static void Release(const ScopedGeneric<int, ScopedFDCloseTraits>&, int);
-#endif
+  static void Acquire(const ScopedGeneric<int, ScopedFDCloseTraits>& owner,
+                      int fd);
+  static void Release(const ScopedGeneric<int, ScopedFDCloseTraits>& owner,
+                      int fd);
 };
+
+#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
+
+struct BASE_EXPORT ScopedFDCloseTraits {
+  static int InvalidValue() { return -1; }
+  static void Free(int fd);
+};
+
 #endif
 
-// Functor for |ScopedFILE| (below).
+// Functor for `ScopedFILE` (below).
 struct ScopedFILECloser {
   inline void operator()(FILE* x) const {
-    if (x)
+    if (x) {
       fclose(x);
+    }
   }
 };
 
@@ -98,11 +97,11 @@
 // should generally use base::File instead which can be constructed with a
 // handle, and in addition to handling ownership, has convenient cross-platform
 // file manipulation functions on it.
-typedef ScopedGeneric<int, internal::ScopedFDCloseTraits> ScopedFD;
+using ScopedFD = ScopedGeneric<int, internal::ScopedFDCloseTraits>;
 #endif
 
-// Automatically closes |FILE*|s.
-typedef std::unique_ptr<FILE, internal::ScopedFILECloser> ScopedFILE;
+// Automatically closes `FILE*`s.
+using ScopedFILE = std::unique_ptr<FILE, internal::ScopedFILECloser>;
 
 #if BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_LINUX)
 // Queries the ownership status of an FD, i.e. whether it is currently owned by
diff --git a/base/profiler/native_unwinder_android_unittest.cc b/base/profiler/native_unwinder_android_unittest.cc
index b5433cd..3ea95d6 100644
--- a/base/profiler/native_unwinder_android_unittest.cc
+++ b/base/profiler/native_unwinder_android_unittest.cc
@@ -137,14 +137,8 @@
   return sample;
 }
 
-// TODO(crbug.com/40156557): After fix, re-enable on all ASAN bots.
-#if defined(ADDRESS_SANITIZER)
-#define MAYBE_PlainFunction DISABLED_PlainFunction
-#else
-#define MAYBE_PlainFunction PlainFunction
-#endif
 // Checks that the expected information is present in sampled frames.
-TEST(NativeUnwinderAndroidTest, MAYBE_PlainFunction) {
+TEST(NativeUnwinderAndroidTest, PlainFunction) {
   const auto sdk_version = base::android::BuildInfo::GetInstance()->sdk_int();
   if (sdk_version < base::android::SDK_VERSION_NOUGAT) {
     GTEST_SKIP();
@@ -180,15 +174,9 @@
                                scenario.GetOuterFunctionAddressRange()});
 }
 
-// TODO(crbug.com/40156557): After fix, re-enable on all ASAN bots.
-#if defined(ADDRESS_SANITIZER)
-#define MAYBE_Alloca DISABLED_Alloca
-#else
-#define MAYBE_Alloca Alloca
-#endif
 // Checks that the unwinder handles stacks containing dynamically-allocated
 // stack memory.
-TEST(NativeUnwinderAndroidTest, MAYBE_Alloca) {
+TEST(NativeUnwinderAndroidTest, Alloca) {
   const auto sdk_version = base::android::BuildInfo::GetInstance()->sdk_int();
   if (sdk_version < base::android::SDK_VERSION_NOUGAT) {
     GTEST_SKIP();
@@ -225,15 +213,9 @@
                                scenario.GetOuterFunctionAddressRange()});
 }
 
-// TODO(crbug.com/40156557): After fix, re-enable on all ASAN bots.
-#if defined(ADDRESS_SANITIZER)
-#define MAYBE_OtherLibrary DISABLED_OtherLibrary
-#else
-#define MAYBE_OtherLibrary OtherLibrary
-#endif
 // Checks that a stack that runs through another library produces a stack with
 // the expected functions.
-TEST(NativeUnwinderAndroidTest, MAYBE_OtherLibrary) {
+TEST(NativeUnwinderAndroidTest, OtherLibrary) {
   const auto sdk_version = base::android::BuildInfo::GetInstance()->sdk_int();
   if (sdk_version < base::android::SDK_VERSION_NOUGAT) {
     GTEST_SKIP();
@@ -305,13 +287,7 @@
 }
 
 // Check that unwinding can be resumed after an incomplete unwind.
-#if defined(ADDRESS_SANITIZER)
-// TODO(crbug.com/40156557): Fix, re-enable.
-#define MAYBE_ResumeUnwinding DISABLED_ResumeUnwinding
-#else
-#define MAYBE_ResumeUnwinding ResumeUnwinding
-#endif
-TEST(NativeUnwinderAndroidTest, MAYBE_ResumeUnwinding) {
+TEST(NativeUnwinderAndroidTest, ResumeUnwinding) {
   NativeLibrary other_library = LoadOtherLibrary();
   UnwindScenario scenario(
       BindRepeating(&CallThroughOtherLibrary, Unretained(other_library)));
diff --git a/base/profiler/stack_sampler_unittest.cc b/base/profiler/stack_sampler_unittest.cc
index 3f6e41e..8c864ba 100644
--- a/base/profiler/stack_sampler_unittest.cc
+++ b/base/profiler/stack_sampler_unittest.cc
@@ -107,8 +107,16 @@
                  TimeTicks* timestamp,
                  RegisterContext* thread_context,
                  Delegate* delegate) override {
-    *stack_top = reinterpret_cast<uintptr_t>(stack_buffer->buffer()) +
-                 10;  // Make msan happy.
+    // Returning true means the various out params should be populated.
+    std::array<uintptr_t, 2> fake_stack;
+    std::memcpy(stack_buffer->buffer(), fake_stack.data(), sizeof(fake_stack));
+    *stack_top =
+        reinterpret_cast<uintptr_t>(stack_buffer->buffer() + fake_stack.size());
+    // Set the stack pointer to be consistent with the copied stack.
+    *thread_context = {};
+    RegisterContextStackPointer(thread_context) =
+        reinterpret_cast<uintptr_t>(stack_buffer->buffer());
+    *timestamp = TimeTicks::Now();
     delegate->OnStackCopy();
     return true;
   }
diff --git a/base/test/android/javatests/src/org/chromium/base/test/BaseJUnit4ClassRunner.java b/base/test/android/javatests/src/org/chromium/base/test/BaseJUnit4ClassRunner.java
index d65b228..aa37537 100644
--- a/base/test/android/javatests/src/org/chromium/base/test/BaseJUnit4ClassRunner.java
+++ b/base/test/android/javatests/src/org/chromium/base/test/BaseJUnit4ClassRunner.java
@@ -29,6 +29,7 @@
 import org.chromium.base.library_loader.LibraryLoader;
 import org.chromium.base.test.params.MethodParamAnnotationRule;
 import org.chromium.base.test.util.AndroidSdkLevelSkipCheck;
+import org.chromium.base.test.util.BaseRestrictions;
 import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.DisableIfSkipCheck;
@@ -95,6 +96,8 @@
         public void run(Context targetContext, FrameworkMethod testMethod);
     }
 
+    protected final RestrictionSkipCheck mRestrictionSkipCheck = new RestrictionSkipCheck();
+
     /**
      * Create a BaseJUnit4ClassRunner to run {@code klass} and initialize values.
      *
@@ -110,6 +113,7 @@
                         0L,
                         false));
 
+        BaseRestrictions.registerChecks(mRestrictionSkipCheck);
         assert InstrumentationRegistry.getInstrumentation()
                         instanceof BaseChromiumAndroidJUnitRunner
                 : "Must use BaseChromiumAndroidJUnitRunner instrumentation with "
@@ -152,15 +156,13 @@
     /**
      * Override this method to return a list of {@link SkipCheck}s}.
      *
-     * Additional hooks can be added to the list using {@link #addToList}:
-     * {@code return addToList(super.getSkipChecks(), check1, check2);}
+     * <p>Additional hooks can be added to the list using {@link #addToList}: {@code return
+     * addToList(super.getSkipChecks(), check1, check2);}
      */
     @CallSuper
     protected List<SkipCheck> getSkipChecks() {
         return Arrays.asList(
-                new RestrictionSkipCheck(InstrumentationRegistry.getTargetContext()),
-                new AndroidSdkLevelSkipCheck(),
-                new DisableIfSkipCheck());
+                mRestrictionSkipCheck, new AndroidSdkLevelSkipCheck(), new DisableIfSkipCheck());
     }
 
     /**
diff --git a/base/test/android/javatests/src/org/chromium/base/test/util/BaseRestrictions.java b/base/test/android/javatests/src/org/chromium/base/test/util/BaseRestrictions.java
new file mode 100644
index 0000000..ec0580f
--- /dev/null
+++ b/base/test/android/javatests/src/org/chromium/base/test/util/BaseRestrictions.java
@@ -0,0 +1,50 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base.test.util;
+
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.chromium.base.SysUtils;
+
+/** Restriction handlers for restrictions in Restrictions. */
+public class BaseRestrictions {
+    private static Boolean sNetworkAvailable;
+    private static Boolean sHasCamera;
+
+    private static boolean isNetworkAvailable() {
+        if (sNetworkAvailable == null) {
+            Context targetContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+            final ConnectivityManager connectivityManager =
+                    (ConnectivityManager)
+                            targetContext.getSystemService(Context.CONNECTIVITY_SERVICE);
+            final NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo();
+            sNetworkAvailable = activeNetworkInfo != null && activeNetworkInfo.isConnected();
+        }
+        return sNetworkAvailable;
+    }
+
+    private static boolean hasCamera() {
+        if (sHasCamera == null) {
+            Context targetContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+            sHasCamera = SysUtils.hasCamera(targetContext);
+        }
+        return sHasCamera;
+    }
+
+    public static void registerChecks(RestrictionSkipCheck restrictionSkipCheck) {
+        restrictionSkipCheck.addHandler(
+                Restriction.RESTRICTION_TYPE_LOW_END_DEVICE, () -> !SysUtils.isLowEndDevice());
+        restrictionSkipCheck.addHandler(
+                Restriction.RESTRICTION_TYPE_NON_LOW_END_DEVICE, SysUtils::isLowEndDevice);
+        restrictionSkipCheck.addHandler(
+                Restriction.RESTRICTION_TYPE_INTERNET, () -> !isNetworkAvailable());
+        restrictionSkipCheck.addHandler(
+                Restriction.RESTRICTION_TYPE_HAS_CAMERA, () -> !hasCamera());
+    }
+}
diff --git a/base/test/android/javatests/src/org/chromium/base/test/util/RestrictionSkipCheck.java b/base/test/android/javatests/src/org/chromium/base/test/util/RestrictionSkipCheck.java
index 236c106..463b3f1 100644
--- a/base/test/android/javatests/src/org/chromium/base/test/util/RestrictionSkipCheck.java
+++ b/base/test/android/javatests/src/org/chromium/base/test/util/RestrictionSkipCheck.java
@@ -4,29 +4,25 @@
 
 package org.chromium.base.test.util;
 
-import android.content.Context;
-import android.net.ConnectivityManager;
-import android.net.NetworkInfo;
-import android.text.TextUtils;
-
 import org.junit.runners.model.FrameworkMethod;
 
 import org.chromium.base.Log;
-import org.chromium.base.SysUtils;
+
+import java.util.HashMap;
+import java.util.Map;
 
 /** Checks if any restrictions exist and skip the test if it meets those restrictions. */
-public class RestrictionSkipCheck extends SkipCheck {
-
-    private static final String TAG = "base_test";
-
-    private final Context mTargetContext;
-
-    public RestrictionSkipCheck(Context targetContext) {
-        mTargetContext = targetContext;
+public final class RestrictionSkipCheck extends SkipCheck {
+    public interface RestrictionHandler {
+        boolean shouldSkip();
     }
 
-    protected Context getTargetContext() {
-        return mTargetContext;
+    private static final String TAG = "RestrictionSkipCheck";
+
+    private final Map<String, RestrictionHandler> mRestrictionHandlers = new HashMap<>();
+
+    public void addHandler(String restrictionValue, RestrictionHandler handler) {
+        mRestrictionHandlers.put(restrictionValue, handler);
     }
 
     @Override
@@ -37,46 +33,24 @@
                 AnnotationProcessingUtils.getAnnotations(
                         frameworkMethod.getMethod(), Restriction.class)) {
             for (String restrictionVal : restriction.value()) {
-                if (restrictionApplies(restrictionVal)) {
+                RestrictionHandler handler = mRestrictionHandlers.get(restrictionVal);
+                if (handler == null) {
+                    throw new IllegalStateException(
+                            "Unknown value for @Restriction: "
+                                    + restrictionVal
+                                    + "\nDid you perhaps use the wrong @RunWith?");
+                }
+                if (handler.shouldSkip()) {
                     Log.i(
                             TAG,
-                            "Test "
-                                    + frameworkMethod.getDeclaringClass().getName()
-                                    + "#"
-                                    + frameworkMethod.getName()
-                                    + " skipped because of restriction "
-                                    + restriction);
+                            "Test %s#%s skipped because of restriction %s",
+                            frameworkMethod.getDeclaringClass().getName(),
+                            frameworkMethod.getName(),
+                            restriction);
                     return true;
                 }
             }
         }
         return false;
     }
-
-    protected boolean restrictionApplies(String restriction) {
-        if (TextUtils.equals(restriction, Restriction.RESTRICTION_TYPE_LOW_END_DEVICE)
-                && !SysUtils.isLowEndDevice()) {
-            return true;
-        }
-        if (TextUtils.equals(restriction, Restriction.RESTRICTION_TYPE_NON_LOW_END_DEVICE)
-                && SysUtils.isLowEndDevice()) {
-            return true;
-        }
-        if (TextUtils.equals(restriction, Restriction.RESTRICTION_TYPE_INTERNET)
-                && !isNetworkAvailable()) {
-            return true;
-        }
-        if (TextUtils.equals(restriction, Restriction.RESTRICTION_TYPE_HAS_CAMERA)
-                && !SysUtils.hasCamera(mTargetContext)) {
-            return true;
-        }
-        return false;
-    }
-
-    private boolean isNetworkAvailable() {
-        final ConnectivityManager connectivityManager =
-                (ConnectivityManager) mTargetContext.getSystemService(Context.CONNECTIVITY_SERVICE);
-        final NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo();
-        return activeNetworkInfo != null && activeNetworkInfo.isConnected();
-    }
 }
diff --git a/base/test/android/junit/src/org/chromium/base/test/util/RestrictionSkipCheckTest.java b/base/test/android/junit/src/org/chromium/base/test/util/RestrictionSkipCheckTest.java
index 9ff10d7..ce8b316 100644
--- a/base/test/android/junit/src/org/chromium/base/test/util/RestrictionSkipCheckTest.java
+++ b/base/test/android/junit/src/org/chromium/base/test/util/RestrictionSkipCheckTest.java
@@ -4,8 +4,6 @@
 
 package org.chromium.base.test.util;
 
-import android.text.TextUtils;
-
 import org.junit.Assert;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -23,17 +21,6 @@
     private static final String TEST_RESTRICTION_DOES_NOT_APPLY =
             "org.chromium.base.test.util.RestrictionSkipCheckTest.TEST_RESTRICTION_DOES_NOT_APPLY";
 
-    private static class TestRestrictionSkipCheck extends RestrictionSkipCheck {
-        public TestRestrictionSkipCheck() {
-            super(null);
-        }
-
-        @Override
-        protected boolean restrictionApplies(String restriction) {
-            return TextUtils.equals(restriction, TEST_RESTRICTION_APPLIES);
-        }
-    }
-
     private static class UnannotatedBaseClass {
         @Restriction({TEST_RESTRICTION_APPLIES})
         public void restrictedMethod() {}
@@ -57,6 +44,11 @@
         public void unannotatedMethod() {}
     }
 
+    @Restriction({"unregisteredValue"})
+    private static class UnregisteredClass {
+        public void unannotatedMethod() {}
+    }
+
     private static class ExtendsRestrictedClass extends RestrictedClass {
         @Override
         public void unannotatedMethod() {}
@@ -69,10 +61,14 @@
 
     private static void expectShouldSkip(boolean shouldSkip, Class<?> testClass, String methodName)
             throws Exception {
+        RestrictionSkipCheck restrictionSkipCheck = new RestrictionSkipCheck();
+        restrictionSkipCheck.addHandler(TEST_RESTRICTION_APPLIES, () -> true);
+        restrictionSkipCheck.addHandler(TEST_RESTRICTION_DOES_NOT_APPLY, () -> false);
+
         Assert.assertEquals(
                 shouldSkip,
-                new TestRestrictionSkipCheck()
-                        .shouldSkip(new FrameworkMethod(testClass.getMethod(methodName))));
+                restrictionSkipCheck.shouldSkip(
+                        new FrameworkMethod(testClass.getMethod(methodName))));
     }
 
     @Test
@@ -109,4 +105,9 @@
     public void testSuperclassUnrestricted() throws Exception {
         expectShouldSkip(false, ExtendsUnrestrictedClass.class, "unannotatedMethod");
     }
+
+    @Test(expected = IllegalStateException.class)
+    public void testUnknownRestriction() throws Exception {
+        expectShouldSkip(false, UnregisteredClass.class, "unannotatedMethod");
+    }
 }
diff --git a/base/trace_event/OWNERS b/base/trace_event/OWNERS
index 6e13bb6..c6bf75a 100644
--- a/base/trace_event/OWNERS
+++ b/base/trace_event/OWNERS
@@ -1,9 +1,10 @@
 eseckler@chromium.org
 nuskos@chromium.org
+khokhlov@google.com
+etiennep@chromium.org
+altimin@chromium.org
 primiano@chromium.org
 skyostil@chromium.org
-altimin@chromium.org
-khokhlov@google.com
 
 # For memory-infra related changes
 ssid@chromium.org
diff --git a/base/trace_event/named_trigger.h b/base/trace_event/named_trigger.h
index 5553c38..f1fc860 100644
--- a/base/trace_event/named_trigger.h
+++ b/base/trace_event/named_trigger.h
@@ -12,6 +12,8 @@
 
 namespace base::trace_event {
 
+inline constexpr char kStartupTracingTriggerName[] = "startup";
+
 // Notifies that a manual trigger event has occurred. Returns true if the
 // trigger caused a scenario to either begin recording or finalize the trace
 // depending on the config, or false if the trigger had no effect. If the
diff --git a/build/android/pylib/junit/junit_test_instance.py b/build/android/pylib/junit/junit_test_instance.py
index 4b6b868..1be919b 100644
--- a/build/android/pylib/junit/junit_test_instance.py
+++ b/build/android/pylib/junit/junit_test_instance.py
@@ -21,7 +21,6 @@
     self._robolectric_runtime_deps_dir = args.robolectric_runtime_deps_dir
     self._runner_filter = args.runner_filter
     self._json_config = args.json_config
-    self._shadows_allowlist = args.shadows_allowlist
     self._shards = args.shards
     self._shard_filter = None
     if args.shard_filter:
@@ -74,10 +73,6 @@
     return self._runner_filter
 
   @property
-  def shadows_allowlist(self):
-    return self._shadows_allowlist
-
-  @property
   def test_filters(self):
     return self._test_filters
 
diff --git a/build/android/pylib/local/machine/local_machine_junit_test_run.py b/build/android/pylib/local/machine/local_machine_junit_test_run.py
index c35b6e5..553cc17 100644
--- a/build/android/pylib/local/machine/local_machine_junit_test_run.py
+++ b/build/android/pylib/local/machine/local_machine_junit_test_run.py
@@ -155,10 +155,7 @@
     return os.path.join(constants.GetOutDirectory(), 'bin', 'helper',
                         self._test_instance.suite)
 
-  def _QueryTestJsonConfig(self,
-                           temp_dir,
-                           allow_debugging=True,
-                           enable_shadow_allowlist=False):
+  def _QueryTestJsonConfig(self, temp_dir, allow_debugging=True):
     json_config_path = os.path.join(temp_dir, 'main_test_config.json')
     cmd = [self._wrapper_path]
     # Allow debugging of test listing when run as:
@@ -169,8 +166,6 @@
       cmd += ['--jvm-args', '"%s"' % ' '.join(jvm_args)]
     cmd += ['--classpath', self._CreatePropertiesJar(temp_dir)]
     cmd += ['--list-tests', '--json-config', json_config_path]
-    if enable_shadow_allowlist and self._test_instance.shadows_allowlist:
-      cmd += ['--shadows-allowlist', self._test_instance.shadows_allowlist]
     cmd += self._GetFilterArgs()
     subprocess.run(cmd, check=True)
     with open(json_config_path) as f:
@@ -232,9 +227,7 @@
       # TODO(crbug.com/40878339): This step can take 3-4 seconds for
       # chrome_junit_tests.
       try:
-        json_config = self._QueryTestJsonConfig(temp_dir,
-                                                allow_debugging=False,
-                                                enable_shadow_allowlist=True)
+        json_config = self._QueryTestJsonConfig(temp_dir, allow_debugging=False)
       except subprocess.CalledProcessError:
         results.append(_MakeUnknownFailureResult('Filter matched no tests'))
         return
diff --git a/build/android/test_runner.py b/build/android/test_runner.py
index 3bb2d41..8ec41cd 100755
--- a/build/android/test_runner.py
+++ b/build/android/test_runner.py
@@ -849,8 +849,6 @@
       '--resource-apk',
       required=True,
       help='Path to .ap_ containing binary resources for Robolectric.')
-  parser.add_argument('--shadows-allowlist',
-                      help='Path to Allowlist file for Shadows.')
 
 
 def AddLinkerTestOptions(parser):
diff --git a/build/config/android/internal_rules.gni b/build/config/android/internal_rules.gni
index 82184f6..31b2133 100644
--- a/build/config/android/internal_rules.gni
+++ b/build/config/android/internal_rules.gni
@@ -932,14 +932,6 @@
         "--robolectric-runtime-deps-dir",
         "@WrappedPath(${_rebased_robolectric_runtime_deps_dir})",
       ]
-      if (build_with_chromium) {
-        _allowlist = "//testing/android/junit/shadows-allowlist.txt"
-        data += [ _allowlist ]
-        executable_args += [
-          "--shadows-allowlist",
-          rebase_path(_allowlist, root_build_dir),
-        ]
-      }
       if (use_jacoco_coverage) {
         # Set a default coverage output directory (can be overridden by user
         # passing the same flag).
diff --git a/build/fuchsia/test/isolate_daemon.py b/build/fuchsia/test/isolate_daemon.py
index ea39564..ec6361e 100755
--- a/build/fuchsia/test/isolate_daemon.py
+++ b/build/fuchsia/test/isolate_daemon.py
@@ -28,8 +28,11 @@
             return self
 
         def __exit__(self, exc_type, exc_value, traceback):
-            self._temp_dir.__exit__(exc_type, exc_value, traceback)
-            # Ignore the errors when cleaning up the temporary folder.
+            try:
+                self._temp_dir.__exit__(exc_type, exc_value, traceback)
+            except OSError:
+                # Ignore the errors when cleaning up the temporary folder.
+                pass
             return True
 
         def name(self):
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridIphTest.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridIphTest.java
index 9a72c0a..dab27e3 100644
--- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridIphTest.java
+++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridIphTest.java
@@ -141,7 +141,6 @@
 
     @Test
     @MediumTest
-    @DisabledTest(message = "Reviving b/341267765")
     public void testShowAndHideIphDialog() {
         final ChromeTabbedActivity cta = mActivityTestRule.getActivity();
 
@@ -189,7 +188,6 @@
 
     @Test
     @MediumTest
-    @DisabledTest(message = "Reviving b/341267765")
     public void testIphItemShowingInIncognito() {
         final ChromeTabbedActivity cta = mActivityTestRule.getActivity();
 
@@ -202,7 +200,6 @@
 
     @Test
     @MediumTest
-    @DisabledTest(message = "Reviving b/341267765")
     public void testDismissIphItem() throws Exception {
         ChromeTabbedActivity cta = mActivityTestRule.getActivity();
 
@@ -234,7 +231,6 @@
     @Test
     @MediumTest
     @Feature({"RenderTest"})
-    @DisabledTest(message = "Reviving b/341267765")
     public void testRenderIph_Portrait() throws IOException {
         ChromeTabbedActivity cta = mActivityTestRule.getActivity();
 
@@ -250,7 +246,6 @@
     @Test
     @MediumTest
     @Feature({"RenderTest"})
-    @DisabledTest(message = "Reviving b/341267765")
     public void testRenderIph_Landscape() throws IOException {
         ChromeTabbedActivity cta = mActivityTestRule.getActivity();
 
@@ -273,7 +268,6 @@
     @Test
     @MediumTest
     @Feature({"RenderTest"})
-    @DisabledTest(message = "Reviving b/341267765")
     public void testRenderIphDialog_Portrait() throws IOException {
         ChromeTabbedActivity cta = mActivityTestRule.getActivity();
 
@@ -303,7 +297,6 @@
     @Test
     @MediumTest
     @Feature({"RenderTest"})
-    @DisabledTest(message = "Reviving b/341267765")
     public void testRenderIphDialog_Landscape() throws IOException {
         ChromeTabbedActivity cta = mActivityTestRule.getActivity();
 
@@ -338,7 +331,6 @@
 
     @Test
     @MediumTest
-    @DisabledTest(message = "Reviving b/341267765")
     public void testIphItemChangeWithLastTab() {
         ChromeTabbedActivity cta = mActivityTestRule.getActivity();
 
@@ -375,7 +367,7 @@
 
     @Test
     @MediumTest
-    @DisabledTest(message = "Reviving b/341267765")
+    @DisabledTest(message = "Consistent failures despite revival effort in b/341267765")
     public void testSwipeToDismiss_IPH() {
         ChromeTabbedActivity cta = mActivityTestRule.getActivity();
         enterTabSwitcher(cta);
@@ -399,7 +391,7 @@
 
     @Test
     @MediumTest
-    @DisabledTest(message = "Reviving b/341267765")
+    @DisabledTest(message = "Still flaky on arm builds despite revival effort in b/341267765")
     public void testNotShowIPHInMultiWindowMode() {
         ChromeTabbedActivity cta = mActivityTestRule.getActivity();
         enterTabSwitcher(cta);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/desktop_site/DesktopSiteSettingsIPHController.java b/chrome/android/java/src/org/chromium/chrome/browser/desktop_site/DesktopSiteSettingsIPHController.java
index 5e7fe9d..fa0515c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/desktop_site/DesktopSiteSettingsIPHController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/desktop_site/DesktopSiteSettingsIPHController.java
@@ -39,8 +39,6 @@
 import org.chromium.components.messages.MessageIdentifier;
 import org.chromium.components.messages.MessageScopeType;
 import org.chromium.components.messages.PrimaryActionClickBehavior;
-import org.chromium.content_public.browser.ContentFeatureList;
-import org.chromium.content_public.browser.ContentFeatureMap;
 import org.chromium.ui.base.DeviceFormFactor;
 import org.chromium.ui.base.WindowAndroid;
 import org.chromium.ui.modelutil.PropertyModel;
@@ -168,9 +166,6 @@
     @VisibleForTesting
     boolean showWindowSettingIPH(@NonNull Tab tab, Profile profile) {
         if (mMessageDispatcher == null) return false;
-        if (!ContentFeatureMap.isEnabled(ContentFeatureList.REQUEST_DESKTOP_SITE_WINDOW_SETTING)) {
-            return false;
-        }
 
         // Return early when the IPH triggering criteria is not satisfied.
         Tracker tracker = TrackerFactory.getTrackerForProfile(profile);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryManager.java b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryManager.java
index c0103cb..6092e0d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryManager.java
@@ -32,6 +32,8 @@
 import org.chromium.chrome.browser.document.ChromeLauncherActivity;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.history.HistoryManagerToolbar.InfoHeaderPref;
+import org.chromium.chrome.browser.incognito.IncognitoUtils;
+import org.chromium.chrome.browser.preferences.Pref;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.search_engines.TemplateUrlServiceFactory;
 import org.chromium.chrome.browser.settings.SettingsLauncherImpl;
@@ -230,7 +232,18 @@
                                         : R.menu.history_manager_menu,
                                 launchedForApp);
         mToolbar.setManager(this);
-        mToolbar.setPrefService(UserPrefs.get(profile));
+        mToolbar.setMenuDelegate(
+                new HistoryManagerToolbar.HistoryManagerMenuDelegate() {
+                    @Override
+                    public boolean supportsDeletingHistory() {
+                        return mPrefService.getBoolean(Pref.ALLOW_DELETING_BROWSER_HISTORY);
+                    }
+
+                    @Override
+                    public boolean supportsIncognito() {
+                        return IncognitoUtils.isIncognitoModeEnabled(profile);
+                    }
+                });
         mToolbar.initializeSearchView(this, R.string.history_manager_search, R.id.search_menu_id);
         mToolbar.setInfoMenuItem(R.id.info_menu_id);
         mToolbar.updateInfoMenuItem(shouldShowInfoButton(), shouldShowInfoHeaderIfAvailable());
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryManagerToolbar.java b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryManagerToolbar.java
index cfd2876..e8909f9b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryManagerToolbar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryManagerToolbar.java
@@ -13,18 +13,14 @@
 import androidx.annotation.VisibleForTesting;
 
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.incognito.IncognitoUtils;
-import org.chromium.chrome.browser.preferences.Pref;
-import org.chromium.chrome.browser.profiles.ProfileManager;
 import org.chromium.components.browser_ui.widget.selectable_list.SelectableListToolbar;
-import org.chromium.components.prefs.PrefService;
 
 import java.util.List;
 
 /** The SelectionToolbar for the browsing history UI. */
 public class HistoryManagerToolbar extends SelectableListToolbar<HistoryItem> {
     private HistoryManager mManager;
-    private PrefService mPrefService;
+    private HistoryManagerMenuDelegate mMenuDelegate;
 
     /**
      * Interface to the Chrome preference storage used to keep the last visibility state of the info
@@ -38,6 +34,15 @@
         default void setVisible(boolean visible) {}
     }
 
+    /** Delegate for menu capabilities of history management. */
+    public interface HistoryManagerMenuDelegate {
+        /** Return whether deleting history is currently supported. */
+        boolean supportsDeletingHistory();
+
+        /** Return whether incognito is currently supported. */
+        boolean supportsIncognito();
+    }
+
     public HistoryManagerToolbar(Context context, AttributeSet attrs) {
         super(context, attrs);
     }
@@ -54,10 +59,11 @@
     }
 
     /**
-     * @param prefService The {@link PrefService} associated with the current Profile.
+     * @param menuDelegate The {@link HistoryManagerMenuDelegate} that determines the availability
+     *     of various menu items.
      */
-    public void setPrefService(PrefService prefService) {
-        mPrefService = prefService;
+    public void setMenuDelegate(HistoryManagerMenuDelegate menuDelegate) {
+        mMenuDelegate = menuDelegate;
         updateMenuItemVisibility();
     }
 
@@ -125,11 +131,11 @@
         // be added back until the user refreshes the history UI. This could happen if the user is
         // signed in to an account that cannot remove browsing history or has incognito disabled and
         // signs out.
-        assert mPrefService != null;
-        if (!mPrefService.getBoolean(Pref.ALLOW_DELETING_BROWSER_HISTORY)) {
+        assert mMenuDelegate != null;
+        if (!mMenuDelegate.supportsDeletingHistory()) {
             getMenu().removeItem(R.id.selection_mode_delete_menu_id);
         }
-        if (!IncognitoUtils.isIncognitoModeEnabled(ProfileManager.getLastUsedRegularProfile())) {
+        if (!mMenuDelegate.supportsIncognito()) {
             getMenu().removeItem(R.id.selection_mode_open_in_incognito);
         }
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/RequestDesktopUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/RequestDesktopUtils.java
index 2dc5b6d..9e5e617 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/RequestDesktopUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/RequestDesktopUtils.java
@@ -46,8 +46,6 @@
 import org.chromium.components.profile_metrics.BrowserProfileType;
 import org.chromium.components.ukm.UkmRecorder;
 import org.chromium.components.user_prefs.UserPrefs;
-import org.chromium.content_public.browser.ContentFeatureList;
-import org.chromium.content_public.browser.ContentFeatureMap;
 import org.chromium.ui.base.DeviceFormFactor;
 import org.chromium.ui.display.DisplayAndroidManager;
 import org.chromium.ui.display.DisplayUtil;
@@ -165,15 +163,10 @@
         // For incognito profile, keep the domain level setting to override the settings from normal
         // profile.
         if (!isIncognito && useDesktopUserAgent == rdsGlobalSetting) {
-            if (ContentFeatureMap.isEnabled(
-                    ContentFeatureList.REQUEST_DESKTOP_SITE_WINDOW_SETTING)) {
-                // To support the window setting, keep the domain settings when the window setting
-                // is ON.
-                PrefService prefService = UserPrefs.get(profile);
-                if (!prefService.getBoolean(DESKTOP_SITE_WINDOW_SETTING_ENABLED)) {
-                    contentSettingValue = ContentSettingValues.DEFAULT;
-                }
-            } else {
+            // To support the window setting, keep the domain settings when the window setting
+            // is ON.
+            PrefService prefService = UserPrefs.get(profile);
+            if (!prefService.getBoolean(DESKTOP_SITE_WINDOW_SETTING_ENABLED)) {
                 contentSettingValue = ContentSettingValues.DEFAULT;
             }
         }
@@ -287,13 +280,11 @@
     /**
      * Default-enables the desktop site window setting if Chrome is opened on a tablet-sized
      * internal display.
+     *
      * @param activity The current {@link Activity}.
      * @param profile The current {@link Profile}.
      */
     public static void maybeDefaultEnableWindowSetting(Activity activity, Profile profile) {
-        if (!ContentFeatureMap.isEnabled(ContentFeatureList.REQUEST_DESKTOP_SITE_WINDOW_SETTING)) {
-            return;
-        }
         int smallestScreenWidthDp = DisplayUtil.getCurrentSmallestScreenWidth(activity);
         boolean isOnExternalDisplay = isOnExternalDisplay(activity);
         if (isOnExternalDisplay
@@ -443,13 +434,10 @@
     }
 
     /**
-     * Determine whether RDS window setting should be applied.
-     * When returning 'true' the mobile user agent should be used for the current window size.
+     * Determine whether RDS window setting should be applied. When returning 'true' the mobile user
+     * agent should be used for the current window size.
      */
     static boolean shouldApplyWindowSetting(Profile profile, GURL url, Context context) {
-        if (!ContentFeatureMap.isEnabled(ContentFeatureList.REQUEST_DESKTOP_SITE_WINDOW_SETTING)) {
-            return false;
-        }
         // Skip window setting on Automotive and revisit if / when they add split screen.
         if (BuildInfo.getInstance().isAutomotive) {
             return false;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabArchiver.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabArchiver.java
index 02228bf8..706456ec 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabArchiver.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabArchiver.java
@@ -186,6 +186,9 @@
     }
 
     private boolean isTabEligibleForArchive(Tab tab) {
+        // Explicitly prevent grouped tabs from getting archived.
+        if (tab.getTabGroupId() != null) return false;
+
         return isTimestampWithinTargetHours(
                 tab.getTimestampMillis(), mTabArchiveSettings.getArchiveTimeDeltaHours());
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabImpl.java
index 9703f6a..c4d5f91 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabImpl.java
@@ -71,8 +71,6 @@
 import org.chromium.components.security_state.SecurityStateModel;
 import org.chromium.components.url_formatter.UrlFormatter;
 import org.chromium.content_public.browser.ChildProcessImportance;
-import org.chromium.content_public.browser.ContentFeatureList;
-import org.chromium.content_public.browser.ContentFeatureMap;
 import org.chromium.content_public.browser.LoadUrlParams;
 import org.chromium.content_public.browser.NavigationHandle;
 import org.chromium.content_public.browser.SelectionPopupController;
@@ -731,11 +729,6 @@
             return true;
         }
 
-        // If desktop mode window setting is enabled, move switchUserAgentIfNeeded() from
-        // loadIfNeeded() to restoreIfNeeded(); to avoid reload without explicit user intent.
-        if (!ContentFeatureMap.isEnabled(ContentFeatureList.REQUEST_DESKTOP_SITE_WINDOW_SETTING)) {
-            switchUserAgentIfNeeded(UseDesktopUserAgentCaller.LOAD_IF_NEEDED + caller);
-        }
         restoreIfNeeded(caller);
         return true;
     }
@@ -1803,13 +1796,9 @@
             }
 
             if (mWebContents != null) {
-                // If desktop mode window setting is enabled, move switchUserAgentIfNeeded() from
-                // loadIfNeeded() to restoreIfNeeded(); to avoid reload without explicit user
-                // intent.
-                if (ContentFeatureMap.isEnabled(
-                        ContentFeatureList.REQUEST_DESKTOP_SITE_WINDOW_SETTING)) {
-                    switchUserAgentIfNeeded(UseDesktopUserAgentCaller.LOAD_IF_NEEDED + caller);
-                }
+                // Invoke switchUserAgentIfNeeded() from restoreIfNeeded() instead of loadIfNeeded()
+                // to avoid reload without explicit user intent.
+                switchUserAgentIfNeeded(UseDesktopUserAgentCaller.LOAD_IF_NEEDED + caller);
                 mWebContents.getNavigationController().loadIfNecessary();
             }
             mIsBeingRestored = true;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/BottomAttachedUiObserver.java b/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/BottomAttachedUiObserver.java
index 0d8f18a..9076481 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/BottomAttachedUiObserver.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/BottomAttachedUiObserver.java
@@ -325,7 +325,7 @@
     // Omnibox Suggestions
 
     @Override
-    public void onOmniboxSuggestionsVisibilityChanged(boolean visible) {
+    public void onOmniboxSessionStateChange(boolean visible) {
         mOmniboxSuggestionsVisible = visible;
         updateBottomAttachedColor();
     }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/accessibility/settings/AccessibilitySettingsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/accessibility/settings/AccessibilitySettingsTest.java
index fdc6799..dd7407ae 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/accessibility/settings/AccessibilitySettingsTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/accessibility/settings/AccessibilitySettingsTest.java
@@ -39,7 +39,6 @@
 import org.chromium.base.task.PostTask;
 import org.chromium.base.task.TaskTraits;
 import org.chromium.base.test.util.CriteriaHelper;
-import org.chromium.base.test.util.DisableIf;
 import org.chromium.base.test.util.Feature;
 import org.chromium.base.test.util.Features;
 import org.chromium.base.test.util.Features.DisableFeatures;
@@ -331,8 +330,6 @@
     @Test
     @SmallTest
     @Feature({"Accessibility"})
-    // Swipe action fails with espresso 3.2. b/329724184
-    @DisableIf.Build(sdk_is_greater_than = android.os.Build.VERSION_CODES.S)
     public void testPageZoomPreference_zoomSliderUpdatesValue() {
         getPageZoomPref();
         int startingVal = mPageZoomPref.getZoomSliderForTesting().getProgress();
@@ -486,8 +483,6 @@
     @SmallTest
     @Feature({"Accessibility"})
     @EnableFeatures({ContentFeatureList.SMART_ZOOM})
-    // Swipe action Fails with espresso 3.2. b/329724184
-    @DisableIf.Build(sdk_is_greater_than = android.os.Build.VERSION_CODES.S)
     public void testPageZoomPreference_smartZoom_zoomSliderUpdatesValue() {
         getPageZoomPref();
         int startingVal = mPageZoomPref.getTextSizeContrastSliderForTesting().getProgress();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/search_engines/TemplateUrlServiceTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/search_engines/TemplateUrlServiceTest.java
index 36a9a02..9732e42 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/search_engines/TemplateUrlServiceTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/search_engines/TemplateUrlServiceTest.java
@@ -18,7 +18,6 @@
 import org.junit.rules.TestRule;
 import org.junit.runner.RunWith;
 
-import org.chromium.base.test.BaseJUnit4ClassRunner;
 import org.chromium.base.test.util.CriteriaHelper;
 import org.chromium.base.test.util.Feature;
 import org.chromium.base.test.util.Features;
@@ -27,6 +26,7 @@
 import org.chromium.chrome.browser.profiles.ProfileManager;
 import org.chromium.chrome.browser.search_engines.settings.SearchEngineAdapter;
 import org.chromium.chrome.test.ChromeBrowserTestRule;
+import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.components.search_engines.TemplateUrl;
 import org.chromium.components.search_engines.TemplateUrlService;
 import org.chromium.components.search_engines.TemplateUrlService.LoadListener;
@@ -45,7 +45,7 @@
 import java.util.stream.Collectors;
 
 /** Tests for Chrome on Android's usage of the TemplateUrlService API. */
-@RunWith(BaseJUnit4ClassRunner.class)
+@RunWith(ChromeJUnit4ClassRunner.class)
 public class TemplateUrlServiceTest {
     @Rule public final ChromeBrowserTestRule mChromeBrowserTestRule = new ChromeBrowserTestRule();
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/site_settings/SiteSettingsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/site_settings/SiteSettingsTest.java
index 7ddfd00..3da65c18 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/site_settings/SiteSettingsTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/site_settings/SiteSettingsTest.java
@@ -138,7 +138,6 @@
 import org.chromium.components.prefs.PrefService;
 import org.chromium.components.user_prefs.UserPrefs;
 import org.chromium.content_public.browser.BrowserContextHandle;
-import org.chromium.content_public.browser.ContentFeatureList;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 import org.chromium.content_public.common.ContentSwitches;
 import org.chromium.device.geolocation.LocationProviderOverrider;
@@ -1763,26 +1762,7 @@
     @Test
     @SmallTest
     @Feature({"Preferences"})
-    @DisableFeatures(ContentFeatureList.REQUEST_DESKTOP_SITE_WINDOW_SETTING)
-    public void testOnlyExpectedPreferencesRequestDesktopSiteDomainSettings() {
-        testExpectedPreferences(
-                SiteSettingsCategory.Type.REQUEST_DESKTOP_SITE,
-                BINARY_TOGGLE_WITH_EXCEPTION,
-                BINARY_TOGGLE_WITH_EXCEPTION);
-        Assert.assertTrue(
-                "SharedPreference USER_ENABLED_DESKTOP_SITE_GLOBAL_SETTING_PREFERENCE_KEY should be"
-                        + " updated.",
-                ContextUtils.getAppSharedPreferences()
-                        .contains(
-                                SingleCategorySettingsConstants
-                                        .USER_ENABLED_DESKTOP_SITE_GLOBAL_SETTING_PREFERENCE_KEY));
-    }
-
-    @Test
-    @SmallTest
-    @Feature({"Preferences"})
-    @EnableFeatures(ContentFeatureList.REQUEST_DESKTOP_SITE_WINDOW_SETTING)
-    public void testOnlyExpectedPreferencesRequestDesktopSiteWindowSettings() {
+    public void testOnlyExpectedPreferencesRequestDesktopSite() {
         String[] rdsEnabled = {"binary_toggle", "desktop_site_window", "add_exception"};
         testExpectedPreferences(
                 SiteSettingsCategory.Type.REQUEST_DESKTOP_SITE,
@@ -2249,8 +2229,7 @@
     @Test
     @SmallTest
     @Feature({"Preferences"})
-    @EnableFeatures("RequestDesktopSiteWindowSetting")
-    public void testAllowRequestDesktopSiteDomainSetting() {
+    public void testAllowRequestDesktopSite() {
         new TwoStatePermissionTestCase(
                         "RequestDesktopSite",
                         SiteSettingsCategory.Type.REQUEST_DESKTOP_SITE,
@@ -2264,7 +2243,7 @@
     @Test
     @SmallTest
     @Feature({"Preferences"})
-    public void testBlockRequestDesktopSiteDomainSetting() {
+    public void testBlockRequestDesktopSite() {
         new TwoStatePermissionTestCase(
                         "RequestDesktopSite",
                         SiteSettingsCategory.Type.REQUEST_DESKTOP_SITE,
@@ -2581,7 +2560,6 @@
     @Test
     @SmallTest
     @Feature({"Preferences"})
-    @EnableFeatures(ContentFeatureList.REQUEST_DESKTOP_SITE_WINDOW_SETTING)
     public void testDesktopSiteWindowSettings() {
         final SettingsActivity settingsActivity =
                 SiteSettingsTestUtils.startSiteSettingsCategory(
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/tab/TabArchiverTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/tab/TabArchiverTest.java
index 1422c4c..214f4a7 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/tab/TabArchiverTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/tab/TabArchiverTest.java
@@ -17,6 +17,7 @@
 
 import androidx.test.filters.MediumTest;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.ClassRule;
 import org.junit.Rule;
@@ -27,6 +28,7 @@
 import org.mockito.junit.MockitoRule;
 import org.mockito.quality.Strictness;
 
+import org.chromium.base.Token;
 import org.chromium.base.shared_preferences.SharedPreferencesManager;
 import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.CallbackHelper;
@@ -96,10 +98,6 @@
                                                 .getOriginalProfile()));
         mArchivedTabModel = mArchivedTabModelOrchestrator.getTabModelSelector().getModel(false);
         mArchivedTabCreator = mArchivedTabModelOrchestrator.getArchivedTabCreatorForTesting();
-        runOnUiThreadBlocking(
-                () ->
-                        // Clear out all archived tabs between tests.
-                        mArchivedTabModel.closeAllTabs());
 
         mRegularTabModel = sActivityTestRule.getActivity().getCurrentTabModel();
         mRegularTabCreator = sActivityTestRule.getActivity().getTabCreator(false);
@@ -121,6 +119,15 @@
                                         mClock));
     }
 
+    @After
+    public void tearDown() {
+        runOnUiThreadBlocking(
+                () -> {
+                    // Clear out all archived tabs between tests.
+                    mArchivedTabModel.closeAllTabs();
+                });
+    }
+
     @Test
     @MediumTest
     public void testArchiveThenUnarchiveTab() throws Exception {
@@ -148,6 +155,44 @@
 
     @Test
     @MediumTest
+    public void testGroupedTabsAreNotArchived() throws Exception {
+        sActivityTestRule.loadUrlInNewTab(
+                sActivityTestRule.getTestServer().getURL(TEST_PATH), /* incognito= */ false);
+
+        // Set the tab to expire after 1 hour to simplify testing.
+        mTabArchiveSettings.setArchiveTimeDeltaHours(1);
+
+        // Set the clock to 1 hour after 0.
+        doReturn(TimeUnit.HOURS.toMillis(1)).when(mClock).currentTimeMillis();
+        // Set the timestamp for both tabs at 0, they should will be archived.
+        ((TabImpl) mRegularTabModel.getTabAt(0)).setTimestampMillisForTesting(0);
+        ((TabImpl) mRegularTabModel.getTabAt(1)).setTimestampMillisForTesting(0);
+
+        // Simulate the first tab being added to a group.
+        runOnUiThreadBlocking(
+                () -> mRegularTabModel.getTabAt(0).setTabGroupId(Token.createRandom()));
+
+        assertEquals(2, mRegularTabModel.getCount());
+        assertEquals(0, mArchivedTabModel.getCount());
+
+        // The grouped tab should be skipped.
+        runOnUiThreadBlocking(
+                () ->
+                        mTabArchiver.onTabModelSelectorAdded(
+                                sActivityTestRule
+                                        .getActivity()
+                                        .getTabModelSelectorSupplier()
+                                        .get()));
+
+        assertEquals(1, mRegularTabModel.getCount());
+        assertEquals(1, mArchivedTabModel.getCount());
+
+        // Reset the group to prevent affecting other tabs.
+        runOnUiThreadBlocking(() -> mRegularTabModel.getTabAt(0).setTabGroupId(null));
+    }
+
+    @Test
+    @MediumTest
     public void testTabModelSelectorInactiveTabsAreArchived() throws Exception {
         // Set the tab to expire after 1 hour to simplify testing.
         mTabArchiveSettings.setArchiveTimeDeltaHours(1);
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/desktop_site/DesktopSiteSettingsIPHControllerUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/desktop_site/DesktopSiteSettingsIPHControllerUnitTest.java
index de75b70..328a8cc 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/desktop_site/DesktopSiteSettingsIPHControllerUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/desktop_site/DesktopSiteSettingsIPHControllerUnitTest.java
@@ -35,7 +35,6 @@
 
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Features.EnableFeatures;
 import org.chromium.base.test.util.Features.JUnitProcessor;
 import org.chromium.base.test.util.JniMocker;
 import org.chromium.chrome.R;
@@ -61,7 +60,6 @@
 import org.chromium.components.messages.MessageDispatcher;
 import org.chromium.components.messages.MessageIdentifier;
 import org.chromium.components.messages.MessageScopeType;
-import org.chromium.content_public.browser.ContentFeatureList;
 import org.chromium.content_public.browser.NavigationController;
 import org.chromium.content_public.browser.WebContents;
 import org.chromium.ui.base.WindowAndroid;
@@ -78,7 +76,6 @@
 @Config(
         manifest = Config.NONE,
         shadows = {ShadowUrlUtilities.class, ShadowSysUtils.class})
-@EnableFeatures(ContentFeatureList.REQUEST_DESKTOP_SITE_WINDOW_SETTING)
 public class DesktopSiteSettingsIPHControllerUnitTest {
     @Rule public TestRule mFeaturesProcessor = new JUnitProcessor();
     @Rule public JniMocker mJniMocker = new JniMocker();
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/tab/RequestDesktopUtilsUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/tab/RequestDesktopUtilsUnitTest.java
index 9fc0a56..d42b91ed 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/tab/RequestDesktopUtilsUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/tab/RequestDesktopUtilsUnitTest.java
@@ -74,7 +74,6 @@
 import org.chromium.components.prefs.PrefService;
 import org.chromium.components.user_prefs.UserPrefs;
 import org.chromium.components.user_prefs.UserPrefsJni;
-import org.chromium.content_public.browser.ContentFeatureList;
 import org.chromium.ui.display.DisplayAndroid;
 import org.chromium.ui.display.DisplayAndroidManager;
 import org.chromium.ui.display.DisplayUtil;
@@ -267,7 +266,6 @@
         when(mDisplayAndroid.getYdpi()).thenReturn(276.5f);
         ShadowDisplayAndroidManager.setDisplay(mDisplay);
         when(mDisplay.getDisplayId()).thenReturn(Display.DEFAULT_DISPLAY);
-        enableFeature(ContentFeatureList.REQUEST_DESKTOP_SITE_WINDOW_SETTING, false);
         ShadowDisplayUtil.setCurrentSmallestScreenWidth(800);
         when(mUserPrefsJni.get(mProfile)).thenReturn(mPrefService);
         doAnswer(invocation -> mWindowSetting)
@@ -310,102 +308,6 @@
     }
 
     @Test
-    public void testSetRequestDesktopSiteContentSettingsForUrl_DefaultBlock_SiteBlock() {
-        // Regular profile type.
-        when(mProfile.isOffTheRecord()).thenReturn(false);
-        mRdsDefaultValue = ContentSettingValues.BLOCK;
-        // Pre-existing subdomain setting.
-        mContentSettingMap.put(mGoogleUrl.getHost(), ContentSettingValues.BLOCK);
-        RequestDesktopUtils.setRequestDesktopSiteContentSettingsForUrl(mProfile, mGoogleUrl, true);
-        Assert.assertEquals(
-                "Request Desktop Site domain level setting is not set correctly.",
-                ContentSettingValues.ALLOW,
-                mContentSettingMap.get(GOOGLE_COM).intValue());
-        Assert.assertEquals(
-                "Request Desktop Site subdomain level setting should be removed.",
-                ContentSettingValues.DEFAULT,
-                mContentSettingMap.get(mGoogleUrl.getHost()).intValue());
-
-        RequestDesktopUtils.setRequestDesktopSiteContentSettingsForUrl(mProfile, mMapsUrl, false);
-        Assert.assertEquals(
-                "Request Desktop Site domain level setting should be removed.",
-                ContentSettingValues.DEFAULT,
-                mContentSettingMap.get(GOOGLE_COM).intValue());
-    }
-
-    @Test
-    public void testSetRequestDesktopSiteContentSettingsForUrl_DefaultBlock_SiteAllow() {
-        // Regular profile type.
-        when(mProfile.isOffTheRecord()).thenReturn(false);
-        mRdsDefaultValue = ContentSettingValues.BLOCK;
-        // Pre-existing subdomain setting.
-        mContentSettingMap.put(mGoogleUrl.getHost(), ContentSettingValues.ALLOW);
-        RequestDesktopUtils.setRequestDesktopSiteContentSettingsForUrl(mProfile, mGoogleUrl, false);
-        Assert.assertEquals(
-                "Request Desktop Site domain level setting should be removed.",
-                ContentSettingValues.DEFAULT,
-                mContentSettingMap.get(GOOGLE_COM).intValue());
-        Assert.assertEquals(
-                "Request Desktop Site subdomain level setting should be removed.",
-                ContentSettingValues.DEFAULT,
-                mContentSettingMap.get(mGoogleUrl.getHost()).intValue());
-
-        RequestDesktopUtils.setRequestDesktopSiteContentSettingsForUrl(mProfile, mMapsUrl, true);
-        Assert.assertEquals(
-                "Request Desktop Site domain level setting is not set correctly.",
-                ContentSettingValues.ALLOW,
-                mContentSettingMap.get(GOOGLE_COM).intValue());
-    }
-
-    @Test
-    public void testSetRequestDesktopSiteContentSettingsForUrl_DefaultAllow_SiteAllow() {
-        // Regular profile type.
-        when(mProfile.isOffTheRecord()).thenReturn(false);
-        mRdsDefaultValue = ContentSettingValues.ALLOW;
-        // Pre-existing subdomain setting.
-        mContentSettingMap.put(mGoogleUrl.getHost(), ContentSettingValues.ALLOW);
-        RequestDesktopUtils.setRequestDesktopSiteContentSettingsForUrl(mProfile, mGoogleUrl, false);
-        Assert.assertEquals(
-                "Request Desktop Site domain level setting is not set correctly.",
-                ContentSettingValues.BLOCK,
-                mContentSettingMap.get(GOOGLE_COM).intValue());
-        Assert.assertEquals(
-                "Request Desktop Site subdomain level setting should be removed.",
-                ContentSettingValues.DEFAULT,
-                mContentSettingMap.get(mGoogleUrl.getHost()).intValue());
-
-        RequestDesktopUtils.setRequestDesktopSiteContentSettingsForUrl(mProfile, mMapsUrl, true);
-        Assert.assertEquals(
-                "Request Desktop Site domain level setting should be removed.",
-                ContentSettingValues.DEFAULT,
-                mContentSettingMap.get(GOOGLE_COM).intValue());
-    }
-
-    @Test
-    public void testSetRequestDesktopSiteContentSettingsForUrl_DefaultAllow_SiteBlock() {
-        // Regular profile type.
-        when(mProfile.isOffTheRecord()).thenReturn(false);
-        mRdsDefaultValue = ContentSettingValues.ALLOW;
-        // Pre-existing subdomain setting.
-        mContentSettingMap.put(mGoogleUrl.getHost(), ContentSettingValues.BLOCK);
-        RequestDesktopUtils.setRequestDesktopSiteContentSettingsForUrl(mProfile, mGoogleUrl, true);
-        Assert.assertEquals(
-                "Request Desktop Site domain level setting should be removed.",
-                ContentSettingValues.DEFAULT,
-                mContentSettingMap.get(GOOGLE_COM).intValue());
-        Assert.assertEquals(
-                "Request Desktop Site subdomain level setting should be removed.",
-                ContentSettingValues.DEFAULT,
-                mContentSettingMap.get(mGoogleUrl.getHost()).intValue());
-
-        RequestDesktopUtils.setRequestDesktopSiteContentSettingsForUrl(mProfile, mMapsUrl, false);
-        Assert.assertEquals(
-                "Request Desktop Site domain level setting is not set correctly.",
-                ContentSettingValues.BLOCK,
-                mContentSettingMap.get(GOOGLE_COM).intValue());
-    }
-
-    @Test
     public void testSetRequestDesktopSiteContentSettingsForUrl_DefaultBlock_Incognito() {
         // Incognito profile type.
         when(mProfile.isOffTheRecord()).thenReturn(true);
@@ -448,7 +350,6 @@
     @Test
     public void
             testSetRequestDesktopSiteContentSettingsForUrl_DefaultBlock_SiteBlock_WindowSettingOn() {
-        enableFeature(ContentFeatureList.REQUEST_DESKTOP_SITE_WINDOW_SETTING, true);
         mWindowSetting = true;
         // Regular profile type.
         when(mProfile.isOffTheRecord()).thenReturn(false);
@@ -476,7 +377,6 @@
     @Test
     public void
             testSetRequestDesktopSiteContentSettingsForUrl_DefaultBlock_SiteBlock_WindowSettingOff() {
-        enableFeature(ContentFeatureList.REQUEST_DESKTOP_SITE_WINDOW_SETTING, true);
         mWindowSetting = false;
         // Regular profile type.
         when(mProfile.isOffTheRecord()).thenReturn(false);
@@ -504,7 +404,6 @@
     @Test
     public void
             testSetRequestDesktopSiteContentSettingsForUrl_DefaultBlock_SiteAllow_WindowSettingOn() {
-        enableFeature(ContentFeatureList.REQUEST_DESKTOP_SITE_WINDOW_SETTING, true);
         mWindowSetting = true;
         // Regular profile type.
         when(mProfile.isOffTheRecord()).thenReturn(false);
@@ -532,7 +431,6 @@
     @Test
     public void
             testSetRequestDesktopSiteContentSettingsForUrl_DefaultBlock_SiteAllow_WindowSettingOff() {
-        enableFeature(ContentFeatureList.REQUEST_DESKTOP_SITE_WINDOW_SETTING, true);
         mWindowSetting = false;
         // Regular profile type.
         when(mProfile.isOffTheRecord()).thenReturn(false);
@@ -560,7 +458,6 @@
     @Test
     public void
             testSetRequestDesktopSiteContentSettingsForUrl_DefaultAllow_SiteAllow_WindowSettingOn() {
-        enableFeature(ContentFeatureList.REQUEST_DESKTOP_SITE_WINDOW_SETTING, true);
         mWindowSetting = true;
         // Regular profile type.
         when(mProfile.isOffTheRecord()).thenReturn(false);
@@ -588,7 +485,6 @@
     @Test
     public void
             testSetRequestDesktopSiteContentSettingsForUrl_DefaultAllow_SiteAllow_WindowSettingOff() {
-        enableFeature(ContentFeatureList.REQUEST_DESKTOP_SITE_WINDOW_SETTING, true);
         mWindowSetting = false;
         // Regular profile type.
         when(mProfile.isOffTheRecord()).thenReturn(false);
@@ -616,7 +512,6 @@
     @Test
     public void
             testSetRequestDesktopSiteContentSettingsForUrl_DefaultAllow_SiteBlock_WindowSettingOn() {
-        enableFeature(ContentFeatureList.REQUEST_DESKTOP_SITE_WINDOW_SETTING, true);
         mWindowSetting = true;
         // Regular profile type.
         when(mProfile.isOffTheRecord()).thenReturn(false);
@@ -644,7 +539,6 @@
     @Test
     public void
             testSetRequestDesktopSiteContentSettingsForUrl_DefaultAllow_SiteBlock_WindowSettingOff() {
-        enableFeature(ContentFeatureList.REQUEST_DESKTOP_SITE_WINDOW_SETTING, true);
         mWindowSetting = false;
         // Regular profile type.
         when(mProfile.isOffTheRecord()).thenReturn(false);
@@ -901,20 +795,9 @@
     }
 
     @Test
-    public void testShouldApplyWindowSetting_FeatureOff() {
-        mWindowSetting = true;
-        boolean shouldApplyWindowSetting =
-                RequestDesktopUtils.shouldApplyWindowSetting(mProfile, mGoogleUrl, mActivity);
-        Assert.assertFalse(
-                "Desktop site window setting should not be applied when feature is off",
-                shouldApplyWindowSetting);
-    }
-
-    @Test
     public void testShouldApplyWindowSetting_IsAutomotive() {
         mShadowPackageManager.setSystemFeature(
                 PackageManager.FEATURE_AUTOMOTIVE, /* supported= */ true);
-        enableFeature(ContentFeatureList.REQUEST_DESKTOP_SITE_WINDOW_SETTING, true);
         mWindowSetting = true;
         boolean shouldApplyWindowSetting =
                 RequestDesktopUtils.shouldApplyWindowSetting(mProfile, mGoogleUrl, mActivity);
@@ -925,7 +808,6 @@
 
     @Test
     public void testShouldApplyWindowSetting_SettingOff() {
-        enableFeature(ContentFeatureList.REQUEST_DESKTOP_SITE_WINDOW_SETTING, true);
         mWindowSetting = false;
         boolean shouldApplyWindowSetting =
                 RequestDesktopUtils.shouldApplyWindowSetting(mProfile, mGoogleUrl, mActivity);
@@ -936,7 +818,6 @@
 
     @Test
     public void testShouldApplyWindowSetting_isNotGlobalSetting() {
-        enableFeature(ContentFeatureList.REQUEST_DESKTOP_SITE_WINDOW_SETTING, true);
         mWindowSetting = true;
         ShadowTabUtils.setIsGlobalSetting(false);
         boolean shouldApplyWindowSetting =
@@ -949,7 +830,6 @@
 
     @Test
     public void testShouldApplyWindowSetting_windowAttributesWidthValid() {
-        enableFeature(ContentFeatureList.REQUEST_DESKTOP_SITE_WINDOW_SETTING, true);
         mWindowSetting = true;
         ShadowTabUtils.setIsGlobalSetting(true);
         mLayoutParams.width = 800;
@@ -971,7 +851,6 @@
 
     @Test
     public void testShouldApplyWindowSetting_windowAttributesWidthInvalid() {
-        enableFeature(ContentFeatureList.REQUEST_DESKTOP_SITE_WINDOW_SETTING, true);
         mWindowSetting = true;
         ShadowTabUtils.setIsGlobalSetting(true);
         mDisplayMetrics.density = 2.0f;
@@ -993,18 +872,7 @@
     }
 
     @Test
-    public void testMaybeDefaultEnableWindowSetting_FeatureOff() {
-        mWindowSetting = false;
-        mIsDefaultValuePreference = true;
-        RequestDesktopUtils.maybeDefaultEnableWindowSetting(mActivity, mProfile);
-        Assert.assertFalse(
-                "Desktop site window setting should not be default enabled when feature is off",
-                mWindowSetting);
-    }
-
-    @Test
     public void testMaybeDefaultEnableWindowSetting_PhoneSizedScreen() {
-        enableFeature(ContentFeatureList.REQUEST_DESKTOP_SITE_WINDOW_SETTING, true);
         mWindowSetting = false;
         mIsDefaultValuePreference = true;
         ShadowDisplayUtil.setCurrentSmallestScreenWidth(400);
@@ -1017,7 +885,6 @@
 
     @Test
     public void testMaybeDefaultEnableWindowSetting_ExternalDisplay() {
-        enableFeature(ContentFeatureList.REQUEST_DESKTOP_SITE_WINDOW_SETTING, true);
         mWindowSetting = false;
         mIsDefaultValuePreference = true;
         when(mDisplay.getDisplayId()).thenReturn(/*non built-in display*/ 2);
@@ -1030,7 +897,6 @@
 
     @Test
     public void testMaybeDefaultEnableWindowSetting_NotDefaultValuePreference() {
-        enableFeature(ContentFeatureList.REQUEST_DESKTOP_SITE_WINDOW_SETTING, true);
         mWindowSetting = false;
         mIsDefaultValuePreference = false;
         RequestDesktopUtils.maybeDefaultEnableWindowSetting(mActivity, mProfile);
@@ -1042,7 +908,6 @@
 
     @Test
     public void testMaybeDefaultEnableWindowSetting_ShouldDefaultEnable() {
-        enableFeature(ContentFeatureList.REQUEST_DESKTOP_SITE_WINDOW_SETTING, true);
         mWindowSetting = false;
         mIsDefaultValuePreference = true;
         RequestDesktopUtils.maybeDefaultEnableWindowSetting(mActivity, mProfile);
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/tabbed_mode/BottomAttachedUiObserverTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/tabbed_mode/BottomAttachedUiObserverTest.java
index e71dbe7..5fa167d 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/tabbed_mode/BottomAttachedUiObserverTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/tabbed_mode/BottomAttachedUiObserverTest.java
@@ -305,18 +305,18 @@
 
         mBottomAttachedUiObserver.onOmniboxSuggestionsBackgroundColorChanged(
                 OMNIBOX_SUGGESTIONS_COLOR);
-        mBottomAttachedUiObserver.onOmniboxSuggestionsVisibilityChanged(true);
+        mBottomAttachedUiObserver.onOmniboxSessionStateChange(true);
         mColorChangeObserver.assertState(OMNIBOX_SUGGESTIONS_COLOR, false);
 
-        mBottomAttachedUiObserver.onOmniboxSuggestionsVisibilityChanged(false);
+        mBottomAttachedUiObserver.onOmniboxSessionStateChange(false);
         mColorChangeObserver.assertState(null, false);
 
         mBottomAttachedUiObserver.onOmniboxSuggestionsBackgroundColorChanged(
                 OMNIBOX_SUGGESTIONS_COLOR_2);
-        mBottomAttachedUiObserver.onOmniboxSuggestionsVisibilityChanged(true);
+        mBottomAttachedUiObserver.onOmniboxSessionStateChange(true);
         mColorChangeObserver.assertState(OMNIBOX_SUGGESTIONS_COLOR_2, false);
 
-        mBottomAttachedUiObserver.onOmniboxSuggestionsVisibilityChanged(false);
+        mBottomAttachedUiObserver.onOmniboxSessionStateChange(false);
         mColorChangeObserver.assertState(null, false);
     }
 
@@ -346,11 +346,11 @@
         // Show omnibox suggestions.
         mBottomAttachedUiObserver.onOmniboxSuggestionsBackgroundColorChanged(
                 OMNIBOX_SUGGESTIONS_COLOR);
-        mBottomAttachedUiObserver.onOmniboxSuggestionsVisibilityChanged(true);
+        mBottomAttachedUiObserver.onOmniboxSessionStateChange(true);
         mColorChangeObserver.assertState(OMNIBOX_SUGGESTIONS_COLOR, false);
 
         // Hide omnibox suggestions.
-        mBottomAttachedUiObserver.onOmniboxSuggestionsVisibilityChanged(false);
+        mBottomAttachedUiObserver.onOmniboxSessionStateChange(false);
         mColorChangeObserver.assertState(BOTTOM_SHEET_YELLOW, false);
 
         // Hide bottom sheet.
diff --git a/chrome/app/chromeos_strings.grdp b/chrome/app/chromeos_strings.grdp
index cd3c310..a72f0636 100644
--- a/chrome/app/chromeos_strings.grdp
+++ b/chrome/app/chromeos_strings.grdp
@@ -1724,18 +1724,6 @@
   <message name="IDS_LOGIN_PASSWORD_CHANGED_INCORRECT_OLD_PASSWORD" desc="Error that is shown when incorrect old password was supplied for user cryptohome migration.">
     Incorrect password
   </message>
-  <message name="IDS_LOGIN_PASSWORD_CHANGED_TITLE" desc="Title for the password changed dialog box">
-    To unlock and restore your local data, please enter your old <ph name="DEVICE_TYPE">$1<ex>Chromebook</ex></ph> password.
-  </message>
-  <message name="IDS_LOGIN_PASSWORD_CHANGED_FORGOT_PASSWORD" desc="Text of the link that lets user skip old password input on the password changed dialog in the GAIA flow">
-    Forgot your old password?
-  </message>
-  <message name="IDS_LOGIN_PASSWORD_CHANGED_PROCEED_ANYWAY" desc="Button to proceed without providing old password on the password changed dialog in the GAIA flow">
-    You may proceed, but only your synced data and settings will be restored. All local data will be lost.
-  </message>
-  <message name="IDS_LOGIN_PASSWORD_CHANGED_TRY_AGAIN" desc="Label for the retry button on the proceed anyway step in the the GAIA password changed flow">
-    Try again
-  </message>
   <message name="IDS_LOGIN_PASSWORD_CHANGED_DATA_LOSS_WARNING_TITLE" desc="Title for the data loss warning screen if the user decided not to recover local data">
     Local data will be deleted
   </message>
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index ef4839e..513420e 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -16741,6 +16741,15 @@
       <ph name="APP_NAME">$1<ex>Google Photos</ex></ph> is blocked by admin. Ask admin for permission to use this app.
     </message>
 
+    <!-- App local block prompt -->
+    <message name="IDS_APP_LOCAL_BLOCK_PROMPT_TITLE" desc="Titlebar of the app local block prompt window">
+      <ph name="APP_NAME">$1<ex>Google Photos</ex></ph> is blocked on your <ph name="DEVICE_TYPE">$2<ex>Chromebook</ex></ph>
+    </message>
+
+    <message name="IDS_APP_LOCAL_BLOCK_HEADING" desc="Tells the child that parent can unblock the app in the settings.">
+      Ask your parent to allow access in Settings
+    </message>
+
     <!-- Extension request -->
     <if expr="not is_android">
       <message name="IDS_ENTERPRISE_EXTENSION_REQUEST_APPROVED_TITLE" desc="The notification title when there are some extension requests approved.">
diff --git a/chrome/app/generated_resources_grd/IDS_APP_LOCAL_BLOCK_HEADING.png.sha1 b/chrome/app/generated_resources_grd/IDS_APP_LOCAL_BLOCK_HEADING.png.sha1
new file mode 100644
index 0000000..4aa1f66b7
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_APP_LOCAL_BLOCK_HEADING.png.sha1
@@ -0,0 +1 @@
+938b9604c0a45636e9de5e929debbf8d8e2964c9
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_APP_LOCAL_BLOCK_PROMPT_TITLE.png.sha1 b/chrome/app/generated_resources_grd/IDS_APP_LOCAL_BLOCK_PROMPT_TITLE.png.sha1
new file mode 100644
index 0000000..4aa1f66b7
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_APP_LOCAL_BLOCK_PROMPT_TITLE.png.sha1
@@ -0,0 +1 @@
+938b9604c0a45636e9de5e929debbf8d8e2964c9
\ No newline at end of file
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index bec50b61..323de77d 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -9816,13 +9816,6 @@
      FEATURE_VALUE_TYPE(features::kEnableAndroidGamepadVibration)},
 #endif  // BUILDFLAG(IS_ANDROID)
 
-#if BUILDFLAG(IS_ANDROID)
-    {"request-desktop-site-window-setting",
-     flag_descriptions::kRequestDesktopSiteWindowSettingName,
-     flag_descriptions::kRequestDesktopSiteWindowSettingDescription, kOsAndroid,
-     FEATURE_VALUE_TYPE(features::kRequestDesktopSiteWindowSetting)},
-#endif  // BUILDFLAG(IS_ANDROID)
-
     {"autofill-enable-remade-downstream-metrics",
      flag_descriptions::kAutofillEnableRemadeDownstreamMetricsName,
      flag_descriptions::kAutofillEnableRemadeDownstreamMetricsDescription,
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 bf4260f..295a77d 100644
--- a/chrome/browser/apps/app_service/app_service_proxy_ash.cc
+++ b/chrome/browser/apps/app_service/app_service_proxy_ash.cc
@@ -892,15 +892,25 @@
     return false;
   }
 
-  // Return true, and load the icon for the app block dialog when the app
-  // is blocked by policy, or by local settings.
-  if (apps_util::IsDisabled(update.Readiness())) {
+  // Return true and load the icon for the app block dialog when the app
+  // is blocked by policy.
+  if (update.Readiness() == apps::Readiness::kDisabledByPolicy) {
     LoadIconForDialog(
         update, base::BindOnce(&AppServiceProxyAsh::OnLoadIconForBlockDialog,
                                weak_ptr_factory_.GetWeakPtr(), update.Name()));
     return true;
   }
 
+  // Return true and load the icon for the app local block dialog when the app
+  // is blocked by local settings.
+  if (update.Readiness() == apps::Readiness::kDisabledByLocalSettings) {
+    LoadIconForDialog(
+        update,
+        base::BindOnce(&AppServiceProxyAsh::OnLoadIconForLocalBlockDialog,
+                       weak_ptr_factory_.GetWeakPtr(), update.Name()));
+    return true;
+  }
+
   // Return true, and load the icon for the app pause dialog when the app
   // is paused.
   if (update.Paused().value_or(false) ||
@@ -991,6 +1001,22 @@
   }
 }
 
+void AppServiceProxyAsh::OnLoadIconForLocalBlockDialog(
+    const std::string& app_name,
+    IconValuePtr icon_value) {
+  if (icon_value->icon_type != IconType::kStandard) {
+    return;
+  }
+
+  AppServiceProxyAsh::CreateLocalBlockDialog(app_name,
+                                             icon_value->uncompressed);
+
+  // For browser tests, call the dialog created callback to stop the run loop.
+  if (!dialog_created_callback_.is_null()) {
+    std::move(dialog_created_callback_).Run();
+  }
+}
+
 void AppServiceProxyAsh::OnLoadIconForPauseDialog(apps::AppType app_type,
                                                   const std::string& app_id,
                                                   const std::string& app_name,
diff --git a/chrome/browser/apps/app_service/app_service_proxy_ash.h b/chrome/browser/apps/app_service/app_service_proxy_ash.h
index 06008b5..d8f1c1e7 100644
--- a/chrome/browser/apps/app_service/app_service_proxy_ash.h
+++ b/chrome/browser/apps/app_service/app_service_proxy_ash.h
@@ -333,6 +333,9 @@
                                 const gfx::ImageSkia& image,
                                 Profile* profile);
 
+  static void CreateLocalBlockDialog(const std::string& app_name,
+                                     const gfx::ImageSkia& image);
+
   static void CreatePauseDialog(apps::AppType app_type,
                                 const std::string& app_name,
                                 const gfx::ImageSkia& image,
@@ -392,6 +395,10 @@
   void OnLoadIconForBlockDialog(const std::string& app_name,
                                 IconValuePtr icon_value);
 
+  // Callback invoked when the icon is loaded for the local block app dialog.
+  void OnLoadIconForLocalBlockDialog(const std::string& app_name,
+                                     IconValuePtr icon_value);
+
   // Callback invoked when the icon is loaded for the pause app dialog.
   void OnLoadIconForPauseDialog(apps::AppType app_type,
                                 const std::string& app_id,
diff --git a/chrome/browser/ash/BUILD.gn b/chrome/browser/ash/BUILD.gn
index 6ff4680..6a44057 100644
--- a/chrome/browser/ash/BUILD.gn
+++ b/chrome/browser/ash/BUILD.gn
@@ -4254,6 +4254,7 @@
     "//chrome/browser/screen_ai:screen_ai_dlc_installer",
     "//chrome/browser/screen_ai/public:optical_character_recognizer",
     "//chrome/browser/ui/ash/system_web_apps",
+    "//chrome/browser/ui/chromeos/magic_boost:magic_boost",
     "//chrome/browser/ui/webui/ash/cloud_upload:mojo_bindings_shared",
     "//chrome/browser/ui/webui/ash/crostini_upgrader:mojo_bindings",
     "//chrome/browser/webshare:storage",
diff --git a/chrome/browser/ash/chromebox_for_meetings/hotlog2/log_file.cc b/chrome/browser/ash/chromebox_for_meetings/hotlog2/log_file.cc
index 7d5f87b..2efc8a6 100644
--- a/chrome/browser/ash/chromebox_for_meetings/hotlog2/log_file.cc
+++ b/chrome/browser/ash/chromebox_for_meetings/hotlog2/log_file.cc
@@ -10,17 +10,9 @@
 
 #include "base/logging.h"
 #include "base/threading/scoped_blocking_call.h"
-#include "third_party/re2/src/re2/re2.h"
 
 namespace ash::cfm {
 
-namespace {
-
-constexpr LazyRE2 kTimestampRegex = {
-    "^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9:\\.]+Z "};
-
-}  // namespace
-
 LogFile::LogFile(const std::string& filepath) : filepath_(filepath) {}
 
 LogFile::~LogFile() {
@@ -110,19 +102,8 @@
   std::string line;
   while (!IsAtEOF() && !IsInFailState() && num_read_lines < count &&
          std::getline(file_stream_, line)) {
-    // If the line doesn't contain a timestamp, consider it a part of
-    // the previous log line and concatenate it. If there is no previous
-    // log line (this really shouldn't happen), just drop it. We need
-    // a valid timestamp to associate with the log, so we can't do
-    // anything with the dangling log line anyway.
-    if (!RE2::PartialMatch(line, *kTimestampRegex)) {
-      if (!logs.empty()) {
-        logs.back() += ("\n" + line);
-      }
-    } else {
-      logs.push_back(std::move(line));
-      num_read_lines++;
-    }
+    logs.push_back(std::move(line));
+    num_read_lines++;
   }
 
   if (IsInFailState()) {
diff --git a/chrome/browser/ash/crostini/crostini_export_import.h b/chrome/browser/ash/crostini/crostini_export_import.h
index 6bd985ca..6b4cce6 100644
--- a/chrome/browser/ash/crostini/crostini_export_import.h
+++ b/chrome/browser/ash/crostini/crostini_export_import.h
@@ -281,7 +281,7 @@
       operation_data_storage_;
   // Trackers must have unique-per-profile identifiers.
   // A non-static member on a profile-keyed-service will suffice.
-  int next_status_tracker_id_;
+  int next_status_tracker_id_ = 0;
   base::ObserverList<Observer> observers_;
   // weak_ptr_factory_ should always be last member.
   base::WeakPtrFactory<CrostiniExportImport> weak_ptr_factory_{this};
diff --git a/chrome/browser/ash/guest_os/public/DEPS b/chrome/browser/ash/guest_os/public/DEPS
index 7bb540d..074bb43a 100644
--- a/chrome/browser/ash/guest_os/public/DEPS
+++ b/chrome/browser/ash/guest_os/public/DEPS
@@ -24,4 +24,5 @@
   "+chrome/browser/extensions/api/terminal",
   "+chrome/browser/profiles",
   "+chrome/test/base",
+  "+chrome/common/chrome_features.h",
 ]
diff --git a/chrome/browser/ash/guest_os/public/guest_os_mount_provider.cc b/chrome/browser/ash/guest_os/public/guest_os_mount_provider.cc
index e73d54c..c96b0259 100644
--- a/chrome/browser/ash/guest_os/public/guest_os_mount_provider.cc
+++ b/chrome/browser/ash/guest_os/public/guest_os_mount_provider.cc
@@ -6,6 +6,7 @@
 
 #include <memory>
 
+#include "base/feature_list.h"
 #include "base/functional/bind.h"
 #include "base/functional/callback.h"
 #include "base/functional/callback_forward.h"
@@ -15,9 +16,12 @@
 #include "base/strings/stringprintf.h"
 #include "chrome/browser/ash/file_manager/path_util.h"
 #include "chrome/browser/ash/file_manager/volume_manager.h"
+#include "chrome/browser/ash/guest_os/guest_id.h"
 #include "chrome/browser/ash/guest_os/infra/cached_callback.h"
+#include "chrome/browser/ash/guest_os/public/types.h"
 #include "chrome/browser/ash/policy/skyvault/policy_utils.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/common/chrome_features.h"
 #include "chromeos/ash/components/disks/disk_mount_manager.h"
 #include "storage/browser/file_system/external_mount_points.h"
 
@@ -169,10 +173,23 @@
 };
 
 void GuestOsMountProvider::Mount(base::OnceCallback<void(bool)> callback) {
-  if (!policy::local_user_files::LocalUserFilesAllowed() &&
-      vm_type() == VmType::ARCVM) {
-    LOG(ERROR)
-        << "Error mounting ARCVM container: local user files are disabled";
+  const bool local_files_allowed =
+      policy::local_user_files::LocalUserFilesAllowed();
+
+  // If SkyVaultV2 is enabled (GA version), block all VMs regardless of the
+  // type.
+  if (!local_files_allowed &&
+      base::FeatureList::IsEnabled(features::kSkyVaultV2)) {
+    LOG(ERROR) << "Error mounting Guest OS container with guest id="
+               << this->GuestId() << ": local user files are disabled";
+    std::move(callback).Run(false);
+    return;
+  }
+
+  // If SkyVaultV2 is disabled (TT version), only block ARC.
+  if (!local_files_allowed && vm_type() == VmType::ARCVM) {
+    LOG(ERROR) << "Error mounting Guest OS container with guest id="
+               << this->GuestId() << ": local user files are disabled";
     std::move(callback).Run(false);
     return;
   }
diff --git a/chrome/browser/ash/login/app_mode/DEPS b/chrome/browser/ash/login/app_mode/DEPS
index 21dca4d1..ac9c82b3 100644
--- a/chrome/browser/ash/login/app_mode/DEPS
+++ b/chrome/browser/ash/login/app_mode/DEPS
@@ -23,10 +23,11 @@
   "+chrome/browser/ash/policy/core",
   "+chrome/browser/ash/policy/remote_commands",
   "+chrome/browser/ash/policy/test_support",
+  "+chrome/browser/ash/profiles/profile_helper.h",
   "+chrome/browser/ash/settings",
   "+chrome/browser/browser_process.h",
-  "+chrome/browser/browser_process_platform_part_ash.h",
   "+chrome/browser/browser_process_platform_part.h",
+  "+chrome/browser/browser_process_platform_part_ash.h",
   "+chrome/browser/chromeos/app_mode",
   "+chrome/browser/device_identity",
   "+chrome/browser/extensions/browsertest_util.h",
diff --git a/chrome/browser/ash/login/app_mode/test/kiosk_base_test.cc b/chrome/browser/ash/login/app_mode/test/kiosk_base_test.cc
index 9c58c830..a3722f76 100644
--- a/chrome/browser/ash/login/app_mode/test/kiosk_base_test.cc
+++ b/chrome/browser/ash/login/app_mode/test/kiosk_base_test.cc
@@ -34,6 +34,7 @@
 #include "chrome/browser/ash/login/test/oobe_base_test.h"
 #include "chrome/browser/ash/login/test/oobe_window_visibility_waiter.h"
 #include "chrome/browser/ash/login/ui/login_display_host.h"
+#include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/extensions/browsertest_util.h"
 #include "chrome/browser/profiles/profile_impl.h"
 #include "chrome/browser/profiles/profile_manager.h"
diff --git a/chrome/browser/ash/login/debug_overlay_browsertest.cc b/chrome/browser/ash/login/debug_overlay_browsertest.cc
index 13469b5..df8eddc3 100644
--- a/chrome/browser/ash/login/debug_overlay_browsertest.cc
+++ b/chrome/browser/ash/login/debug_overlay_browsertest.cc
@@ -23,7 +23,7 @@
 
 constexpr int kCommonScreensCount = 50;
 constexpr int kOobeOnlyScreensCount = 10;
-constexpr int kLoginOnlyScreensCount = 7;
+constexpr int kLoginOnlyScreensCount = 6;
 
 constexpr int kOobeScreensCount = kCommonScreensCount + kOobeOnlyScreensCount;
 constexpr int kLoginScreensCount = kCommonScreensCount + kLoginOnlyScreensCount;
@@ -109,7 +109,8 @@
   test::OobeJS().ExpectEQ(ElementsInPanel(kScreensPanel), screens_count);
 }
 
-INSTANTIATE_TEST_SUITE_P(All, DebugOverlayScreensTest, testing::Bool());
+/* No makes it easier to run all tests with one filter */
+INSTANTIATE_TEST_SUITE_P(, DebugOverlayScreensTest, testing::Bool());
 
 IN_PROC_BROWSER_TEST_F(DebugOverlayOnLoginTest, ExpectScreenButtonsCount) {
   ASSERT_TRUE(LoginScreenTestApi::ClickAddUserButton());
diff --git a/chrome/browser/ash/login/oobe_quick_start/connectivity/qr_code.cc b/chrome/browser/ash/login/oobe_quick_start/connectivity/qr_code.cc
index 6807426..4a79a656 100644
--- a/chrome/browser/ash/login/oobe_quick_start/connectivity/qr_code.cc
+++ b/chrome/browser/ash/login/oobe_quick_start/connectivity/qr_code.cc
@@ -23,9 +23,7 @@
 }  // namespace
 
 QRCode::QRCode(AdvertisingId advertising_id, SharedSecret shared_secret)
-    : advertising_id_(advertising_id), shared_secret_(shared_secret) {
-  GeneratePixelData();
-}
+    : advertising_id_(advertising_id), shared_secret_(shared_secret) {}
 
 QRCode::QRCode(const QRCode& other) = default;
 
@@ -33,30 +31,30 @@
 
 QRCode::~QRCode() = default;
 
-void QRCode::GeneratePixelData() {
-  std::vector<uint8_t> blob = GetQRCodeData();
-  auto generated_code = qr_code_generator::GenerateCode(blob);
-  CHECK(generated_code.has_value()) << "generated_code has no value";
-  auto res =
-      PixelData{generated_code->data.begin(), generated_code->data.end()};
-  CHECK_EQ(res.size(), static_cast<size_t>(generated_code->qr_size *
-                                           generated_code->qr_size));
-  pixel_data_ = res;
-}
-
-std::vector<uint8_t> QRCode::GetQRCodeData() {
+std::string QRCode::GetQRCodeURLString() {
   std::string shared_secret_str(shared_secret_.begin(), shared_secret_.end());
   std::string shared_secret_base64 = base::Base64Encode(shared_secret_str);
   url::RawCanonOutputT<char> shared_secret_base64_uriencoded;
   url::EncodeURIComponent(shared_secret_base64,
                           &shared_secret_base64_uriencoded);
+  return base::StrCat({"https://signin.google/qs/", advertising_id_.ToString(),
+                       "?key=", shared_secret_base64_uriencoded.view(),
+                       "&t=", kDeviceTypeQueryParamValue});
+}
 
-  std::string url =
-      base::StrCat({"https://signin.google/qs/", advertising_id_.ToString(),
-                    "?key=", shared_secret_base64_uriencoded.view(),
-                    "&t=", kDeviceTypeQueryParamValue});
-
-  return std::vector<uint8_t>(url.begin(), url.end());
+QRCode::PixelData QRCode::GetPixelData() {
+  std::string url_string = GetQRCodeURLString();
+  std::vector<uint8_t> blob =
+      std::vector<uint8_t>(url_string.begin(), url_string.end());
+  base::expected<qr_code_generator::GeneratedCode, qr_code_generator::Error>
+      generated_code = qr_code_generator::GenerateCode(blob);
+  CHECK(generated_code.has_value()) << "generated_code has no value";
+  auto res =
+      PixelData{generated_code->data.begin(), generated_code->data.end()};
+  CHECK_EQ(res.size(), static_cast<size_t>(generated_code->qr_size *
+                                           generated_code->qr_size))
+      << "unexpected size for QR code data";
+  return res;
 }
 
 }  // namespace ash::quick_start
diff --git a/chrome/browser/ash/login/oobe_quick_start/connectivity/qr_code.h b/chrome/browser/ash/login/oobe_quick_start/connectivity/qr_code.h
index 145e628..1179e774 100644
--- a/chrome/browser/ash/login/oobe_quick_start/connectivity/qr_code.h
+++ b/chrome/browser/ash/login/oobe_quick_start/connectivity/qr_code.h
@@ -24,20 +24,12 @@
   QRCode& operator=(const QRCode& other);
   ~QRCode();
 
-  PixelData pixel_data() { return pixel_data_; }
+  std::string GetQRCodeURLString();
+  PixelData GetPixelData();
 
  private:
-  friend class QRCodeTest;
-
-  void GeneratePixelData();
-
-  // Returns a deep link URL as a vector of bytes that will form the QR code
-  // used to authenticate the connection.
-  std::vector<uint8_t> GetQRCodeData();
-
   AdvertisingId advertising_id_;
   SharedSecret shared_secret_;
-  PixelData pixel_data_;
 };
 
 }  // namespace ash::quick_start
diff --git a/chrome/browser/ash/login/oobe_quick_start/connectivity/qr_code_unittest.cc b/chrome/browser/ash/login/oobe_quick_start/connectivity/qr_code_unittest.cc
index 1fbc894..02f87e0 100644
--- a/chrome/browser/ash/login/oobe_quick_start/connectivity/qr_code_unittest.cc
+++ b/chrome/browser/ash/login/oobe_quick_start/connectivity/qr_code_unittest.cc
@@ -7,6 +7,7 @@
 #include <string>
 #include <vector>
 
+#include "base/strings/strcat.h"
 #include "chrome/browser/ash/login/oobe_quick_start/connectivity/advertising_id.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -14,29 +15,141 @@
 
 namespace {
 
-// Base qr code url ("https://signin.google/qs/") represented in a 25 byte
-// array.
-constexpr std::array<uint8_t, 25> kBaseUrl = {
-    0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x73,
-    0x69, 0x67, 0x6e, 0x69, 0x6e, 0x2e, 0x67, 0x6f, 0x6f,
-    0x67, 0x6c, 0x65, 0x2f, 0x71, 0x73, 0x2f};
-
-// Qr code key param ("?key=") represented in a 5 byte array.
-constexpr std::array<uint8_t, 5> kUrlKeyParam = {0x3f, 0x6b, 0x65, 0x79, 0x3d};
-
-// Qr code device type param ("&t=7") represented in a 4 byte array.
-constexpr std::array<uint8_t, 4> kUrlDeviceTypeParam = {0x26, 0x74, 0x3d, 0x37};
-
-// 32 random bytes to use as the shared secret when generating QR Code.
-constexpr std::array<uint8_t, 32> kSharedSecret = {
-    0x54, 0xbd, 0x40, 0xcf, 0x8a, 0x7c, 0x2f, 0x6a, 0xca, 0x15, 0x59,
-    0xcf, 0xf3, 0xeb, 0x31, 0x08, 0x90, 0x73, 0xef, 0xda, 0x87, 0xd4,
-    0x23, 0xc0, 0x55, 0xd5, 0x83, 0x5b, 0x04, 0x28, 0x49, 0xf2};
+// Components of the URL which will be encoded into the QR code.
+constexpr char kBaseUrlString[] = "https://signin.google/qs/";
+constexpr char kUrlKeyParameterString[] = "?key=";
+constexpr char kUrlDeviceTypeParameterString[] = "&t=7";
 
 // Base64 representation of kSharedSecret.
 constexpr char kSharedSecretBase64[] =
     "VL1Az4p8L2rKFVnP8%2BsxCJBz79qH1CPAVdWDWwQoSfI%3D";
 
+// 32 random bytes to use as the shared secret when generating QR Code.
+constexpr std::array<uint8_t, 32> kSharedSecretData = {
+    0x54, 0xbd, 0x40, 0xcf, 0x8a, 0x7c, 0x2f, 0x6a, 0xca, 0x15, 0x59,
+    0xcf, 0xf3, 0xeb, 0x31, 0x08, 0x90, 0x73, 0xef, 0xda, 0x87, 0xd4,
+    0x23, 0xc0, 0x55, 0xd5, 0x83, 0x5b, 0x04, 0x28, 0x49, 0xf2};
+
+// 6 random bytes to use as the advertising ID.
+constexpr std::array<uint8_t, 6> kAdvertisingIdData = {0xeb, 0x31, 0x08,
+                                                       0x90, 0x73, 0xef};
+
+// Expected final output for the QR code pixel data.
+const std::vector<uint8_t> kExpectedPixelData = {
+    0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1,
+    0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x1, 0x1, 0x1, 0x0, 0x0,
+    0x1, 0x1, 0x0, 0x0, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0,
+    0x0, 0x0, 0x1, 0x0, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x1,
+    0x1, 0x0, 0x1, 0x1, 0x0, 0x0, 0x1, 0x1, 0x0, 0x1, 0x0, 0x0, 0x1, 0x1, 0x0,
+    0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1, 0x0, 0x1, 0x1, 0x1, 0x0, 0x1, 0x0,
+    0x1, 0x0, 0x1, 0x1, 0x0, 0x1, 0x0, 0x1, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x1,
+    0x0, 0x1, 0x1, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x1, 0x1,
+    0x1, 0x0, 0x1, 0x1, 0x0, 0x1, 0x1, 0x1, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x1,
+    0x0, 0x1, 0x0, 0x0, 0x1, 0x0, 0x1, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1,
+    0x0, 0x0, 0x1, 0x1, 0x1, 0x1, 0x0, 0x1, 0x0, 0x1, 0x1, 0x1, 0x0, 0x1, 0x1,
+    0x0, 0x1, 0x1, 0x1, 0x0, 0x1, 0x0, 0x1, 0x1, 0x0, 0x1, 0x0, 0x1, 0x1, 0x1,
+    0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x1, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0,
+    0x1, 0x0, 0x0, 0x1, 0x0, 0x1, 0x1, 0x1, 0x0, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0,
+    0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0,
+    0x1, 0x1, 0x0, 0x1, 0x1, 0x1, 0x1, 0x0, 0x1, 0x0, 0x1, 0x0, 0x1, 0x0, 0x1,
+    0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x0, 0x1,
+    0x0, 0x1, 0x0, 0x1, 0x0, 0x1, 0x0, 0x1, 0x0, 0x1, 0x0, 0x1, 0x0, 0x1, 0x0,
+    0x1, 0x0, 0x1, 0x0, 0x1, 0x0, 0x1, 0x0, 0x1, 0x0, 0x1, 0x1, 0x1, 0x1, 0x1,
+    0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1, 0x1,
+    0x0, 0x1, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x1, 0x0, 0x1, 0x1, 0x1, 0x0,
+    0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0,
+    0x0, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x0, 0x1, 0x1, 0x0, 0x1, 0x1, 0x0, 0x1,
+    0x1, 0x1, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x1, 0x0, 0x0,
+    0x1, 0x1, 0x0, 0x0, 0x1, 0x0, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x0, 0x0,
+    0x0, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1, 0x0,
+    0x1, 0x1, 0x1, 0x0, 0x0, 0x1, 0x1, 0x0, 0x0, 0x1, 0x1, 0x0, 0x1, 0x0, 0x1,
+    0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x1, 0x0, 0x0,
+    0x0, 0x1, 0x0, 0x0, 0x1, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x1, 0x0,
+    0x0, 0x1, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x1, 0x0, 0x0, 0x1, 0x0,
+    0x0, 0x1, 0x0, 0x0, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1,
+    0x1, 0x0, 0x0, 0x1, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x1,
+    0x1, 0x0, 0x1, 0x1, 0x1, 0x0, 0x1, 0x1, 0x1, 0x0, 0x1, 0x0, 0x0, 0x1, 0x1,
+    0x0, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x1, 0x1, 0x1,
+    0x0, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0,
+    0x0, 0x1, 0x0, 0x1, 0x0, 0x0, 0x1, 0x1, 0x0, 0x1, 0x0, 0x0, 0x1, 0x0, 0x0,
+    0x0, 0x1, 0x1, 0x1, 0x1, 0x0, 0x1, 0x0, 0x0, 0x1, 0x0, 0x0, 0x1, 0x0, 0x0,
+    0x0, 0x0, 0x1, 0x0, 0x1, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x1, 0x1,
+    0x0, 0x1, 0x1, 0x1, 0x0, 0x0, 0x1, 0x1, 0x0, 0x0, 0x1, 0x1, 0x0, 0x1, 0x1,
+    0x0, 0x0, 0x0, 0x1, 0x0, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1,
+    0x0, 0x0, 0x1, 0x1, 0x1, 0x1, 0x0, 0x1, 0x1, 0x0, 0x1, 0x0, 0x0, 0x1, 0x1,
+    0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x1, 0x0, 0x1, 0x1, 0x0, 0x0, 0x1, 0x0,
+    0x1, 0x1, 0x0, 0x0, 0x1, 0x0, 0x0, 0x1, 0x0, 0x1, 0x0, 0x1, 0x1, 0x0, 0x0,
+    0x1, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x1, 0x0, 0x1, 0x0, 0x1, 0x1, 0x0,
+    0x1, 0x0, 0x1, 0x0, 0x1, 0x0, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0,
+    0x1, 0x0, 0x0, 0x1, 0x1, 0x1, 0x1, 0x1, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x1,
+    0x0, 0x0, 0x0, 0x0, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x1, 0x0, 0x1,
+    0x0, 0x0, 0x1, 0x0, 0x0, 0x1, 0x0, 0x1, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x1,
+    0x1, 0x0, 0x1, 0x1, 0x1, 0x0, 0x1, 0x0, 0x0, 0x1, 0x1, 0x0, 0x0, 0x1, 0x0,
+    0x1, 0x0, 0x1, 0x0, 0x1, 0x0, 0x1, 0x1, 0x1, 0x1, 0x1, 0x0, 0x0, 0x1, 0x0,
+    0x0, 0x0, 0x0, 0x1, 0x1, 0x0, 0x0, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x0, 0x0,
+    0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x0, 0x1, 0x1,
+    0x1, 0x0, 0x1, 0x1, 0x1, 0x0, 0x0, 0x1, 0x1, 0x1, 0x0, 0x1, 0x0, 0x1, 0x0,
+    0x0, 0x1, 0x1, 0x1, 0x0, 0x0, 0x1, 0x0, 0x1, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0,
+    0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x1, 0x1, 0x0, 0x1, 0x1, 0x0, 0x0, 0x1, 0x1,
+    0x0, 0x1, 0x0, 0x1, 0x1, 0x1, 0x1, 0x0, 0x1, 0x0, 0x1, 0x1, 0x0, 0x0, 0x1,
+    0x0, 0x1, 0x1, 0x0, 0x0, 0x1, 0x1, 0x0, 0x1, 0x0, 0x1, 0x1, 0x0, 0x0, 0x1,
+    0x1, 0x0, 0x1, 0x1, 0x1, 0x0, 0x0, 0x1, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0,
+    0x1, 0x0, 0x0, 0x1, 0x0, 0x1, 0x1, 0x1, 0x1, 0x1, 0x0, 0x0, 0x1, 0x0, 0x1,
+    0x0, 0x1, 0x1, 0x0, 0x0, 0x1, 0x1, 0x1, 0x0, 0x1, 0x0, 0x0, 0x1, 0x1, 0x1,
+    0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0,
+    0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x0, 0x1,
+    0x0, 0x1, 0x1, 0x0, 0x0, 0x1, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+    0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x1, 0x1, 0x1, 0x0, 0x1, 0x1, 0x0, 0x0, 0x1,
+    0x1, 0x0, 0x1, 0x0, 0x0, 0x1, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x1, 0x0, 0x1,
+    0x1, 0x1, 0x0, 0x0, 0x1, 0x1, 0x0, 0x0, 0x0, 0x1, 0x1, 0x1, 0x0, 0x0, 0x1,
+    0x1, 0x1, 0x0, 0x0, 0x0, 0x1, 0x1, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x1, 0x0,
+    0x0, 0x0, 0x1, 0x1, 0x0, 0x0, 0x1, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x1, 0x1,
+    0x1, 0x1, 0x1, 0x1, 0x0, 0x1, 0x0, 0x1, 0x1, 0x1, 0x1, 0x0, 0x1, 0x0, 0x0,
+    0x1, 0x1, 0x1, 0x0, 0x0, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x1, 0x0,
+    0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x0, 0x1, 0x1, 0x1, 0x1, 0x1, 0x0, 0x1, 0x1,
+    0x0, 0x0, 0x1, 0x0, 0x0, 0x1, 0x0, 0x1, 0x0, 0x1, 0x1, 0x0, 0x1, 0x1, 0x1,
+    0x0, 0x0, 0x1, 0x1, 0x0, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x1, 0x1,
+    0x0, 0x1, 0x1, 0x1, 0x0, 0x1, 0x0, 0x0, 0x1, 0x0, 0x0, 0x1, 0x1, 0x1, 0x1,
+    0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x1, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0,
+    0x1, 0x1, 0x0, 0x0, 0x1, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0,
+    0x0, 0x1, 0x0, 0x1, 0x1, 0x1, 0x1, 0x0, 0x1, 0x0, 0x0, 0x1, 0x0, 0x1, 0x1,
+    0x1, 0x1, 0x1, 0x0, 0x1, 0x1, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0,
+    0x0, 0x0, 0x1, 0x1, 0x0, 0x0, 0x1, 0x0, 0x1, 0x1, 0x0, 0x1, 0x0, 0x1, 0x1,
+    0x1, 0x0, 0x0, 0x1, 0x1, 0x0, 0x1, 0x0, 0x1, 0x1, 0x0, 0x1, 0x1, 0x1, 0x1,
+    0x0, 0x0, 0x0, 0x0, 0x1, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0,
+    0x1, 0x0, 0x1, 0x0, 0x0, 0x1, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x1, 0x1, 0x1,
+    0x1, 0x1, 0x1, 0x1, 0x1, 0x0, 0x1, 0x1, 0x1, 0x1, 0x0, 0x0, 0x1, 0x0, 0x1,
+    0x1, 0x1, 0x1, 0x1, 0x1, 0x0, 0x1, 0x0, 0x0, 0x1, 0x1, 0x1, 0x1, 0x0, 0x0,
+    0x1, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x1, 0x1, 0x1, 0x0, 0x1,
+    0x0, 0x0, 0x0, 0x0, 0x1, 0x1, 0x1, 0x0, 0x1, 0x0, 0x0, 0x1, 0x1, 0x0, 0x0,
+    0x1, 0x1, 0x0, 0x0, 0x1, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0,
+    0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x1, 0x1, 0x1, 0x0, 0x0, 0x1, 0x1, 0x1, 0x0,
+    0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1, 0x1, 0x1, 0x0, 0x1, 0x0, 0x1, 0x0,
+    0x1, 0x0, 0x1, 0x1, 0x1, 0x0, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1,
+    0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x1, 0x1,
+    0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x1,
+    0x1, 0x1, 0x1, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x1, 0x1, 0x1, 0x0, 0x1,
+    0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x0, 0x1, 0x0, 0x1, 0x0, 0x0, 0x1, 0x1, 0x1,
+    0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x1, 0x1, 0x0, 0x0, 0x1, 0x1, 0x1, 0x0, 0x0,
+    0x0, 0x1, 0x0, 0x1, 0x0, 0x1, 0x1, 0x1, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0,
+    0x0, 0x1, 0x0, 0x1, 0x1, 0x0, 0x1, 0x0, 0x0, 0x1, 0x0, 0x1, 0x1, 0x0, 0x1,
+    0x1, 0x1, 0x1, 0x0, 0x1, 0x0, 0x1, 0x1, 0x0, 0x0, 0x1, 0x1, 0x1, 0x0, 0x0,
+    0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x1, 0x1, 0x1, 0x0, 0x1, 0x0, 0x1,
+    0x1, 0x1, 0x0, 0x1, 0x1, 0x1, 0x1, 0x0, 0x1, 0x1, 0x1, 0x0, 0x1, 0x0, 0x1,
+    0x0, 0x1, 0x1, 0x1, 0x0, 0x1, 0x0, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x0, 0x0,
+    0x0, 0x1, 0x1, 0x0, 0x1, 0x1, 0x1, 0x0, 0x1, 0x0, 0x1, 0x1, 0x1, 0x0, 0x0,
+    0x0, 0x0, 0x1, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1, 0x1, 0x0, 0x0,
+    0x1, 0x0, 0x1, 0x1, 0x1, 0x0, 0x0, 0x1, 0x0, 0x1, 0x0, 0x1, 0x0, 0x1, 0x0,
+    0x1, 0x1, 0x1, 0x0, 0x1, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x1, 0x1, 0x0,
+    0x1, 0x1, 0x1, 0x0, 0x1, 0x0, 0x0, 0x1, 0x0, 0x1, 0x1, 0x0, 0x1, 0x0, 0x1,
+    0x1, 0x1, 0x0, 0x1, 0x1, 0x0, 0x0, 0x0, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0,
+    0x1, 0x0, 0x0, 0x1, 0x1, 0x0, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1,
+    0x0, 0x0, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x1, 0x1, 0x0,
+    0x1, 0x0, 0x1, 0x0, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x0, 0x1, 0x1,
+    0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1,
+    0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1, 0x1, 0x0, 0x1, 0x0, 0x0, 0x0,
+    0x0};
+
 }  // namespace
 
 class QRCodeTest : public testing::Test {
@@ -46,33 +159,24 @@
   QRCodeTest& operator=(const QRCodeTest&) = delete;
 
   void SetUp() override {
-    advertising_id_ = AdvertisingId();
-    qr_code_ = std::make_unique<QRCode>(advertising_id_, kSharedSecret);
+    advertising_id_ = AdvertisingId(kAdvertisingIdData);
+    qr_code_ = std::make_unique<QRCode>(advertising_id_, kSharedSecretData);
   }
 
  protected:
-  std::vector<uint8_t> GetQRCodeData() { return qr_code_->GetQRCodeData(); }
-
   AdvertisingId advertising_id_;
   std::unique_ptr<QRCode> qr_code_;
 };
 
+TEST_F(QRCodeTest, GetQRCodeURLString) {
+  std::string expected_url = base::StrCat(
+      {kBaseUrlString, advertising_id_.ToString(), kUrlKeyParameterString,
+       kSharedSecretBase64, kUrlDeviceTypeParameterString});
+  EXPECT_EQ(expected_url, qr_code_->GetQRCodeURLString());
+}
+
 TEST_F(QRCodeTest, GetQRCodeData) {
-  std::string advertising_id = advertising_id_.ToString();
-  std::string encoded_shared_secret(kSharedSecretBase64);
-
-  std::vector<uint8_t> expected_data(std::begin(kBaseUrl), std::end(kBaseUrl));
-  expected_data.insert(expected_data.end(), advertising_id.begin(),
-                       advertising_id.end());
-  expected_data.insert(expected_data.end(), std::begin(kUrlKeyParam),
-                       std::end(kUrlKeyParam));
-  expected_data.insert(expected_data.end(), encoded_shared_secret.begin(),
-                       encoded_shared_secret.end());
-  expected_data.insert(expected_data.end(), std::begin(kUrlDeviceTypeParam),
-                       std::end(kUrlDeviceTypeParam));
-
-  std::vector<uint8_t> actual_data = GetQRCodeData();
-  EXPECT_EQ(expected_data, actual_data);
+  EXPECT_EQ(kExpectedPixelData, qr_code_->GetPixelData());
 }
 
 }  // namespace ash::quick_start
diff --git a/chrome/browser/ash/login/oobe_quick_start/target_device_bootstrap_controller.cc b/chrome/browser/ash/login/oobe_quick_start/target_device_bootstrap_controller.cc
index 5ff38204..814c84b8 100644
--- a/chrome/browser/ash/login/oobe_quick_start/target_device_bootstrap_controller.cc
+++ b/chrome/browser/ash/login/oobe_quick_start/target_device_bootstrap_controller.cc
@@ -98,10 +98,10 @@
   if (use_pin_authentication || session_context_.is_resume_after_update()) {
     status_.step = Step::ADVERTISING_WITHOUT_QR_CODE;
   } else {
-    auto qr_code = std::make_unique<QRCode>(session_context_.advertising_id(),
-                                            session_context_.shared_secret());
     status_.step = Step::ADVERTISING_WITH_QR_CODE;
-    status_.payload.emplace<QRCode::PixelData>(qr_code->pixel_data());
+    QRCode qr_code{session_context_.advertising_id(),
+                   session_context_.shared_secret()};
+    status_.payload = std::move(qr_code);
   }
 
   connection_broker_->StartAdvertising(
diff --git a/chrome/browser/ash/login/oobe_quick_start/target_device_bootstrap_controller.h b/chrome/browser/ash/login/oobe_quick_start/target_device_bootstrap_controller.h
index 96531c3..82da97e 100644
--- a/chrome/browser/ash/login/oobe_quick_start/target_device_bootstrap_controller.h
+++ b/chrome/browser/ash/login/oobe_quick_start/target_device_bootstrap_controller.h
@@ -85,7 +85,7 @@
 
   using Payload = absl::variant<absl::monostate,
                                 ErrorCode,
-                                QRCode::PixelData,
+                                QRCode,
                                 PinString,
                                 EmailString,
                                 mojom::WifiCredentials,
diff --git a/chrome/browser/ash/login/oobe_quick_start/target_device_bootstrap_controller_unittest.cc b/chrome/browser/ash/login/oobe_quick_start/target_device_bootstrap_controller_unittest.cc
index c343eac..b9f5502 100644
--- a/chrome/browser/ash/login/oobe_quick_start/target_device_bootstrap_controller_unittest.cc
+++ b/chrome/browser/ash/login/oobe_quick_start/target_device_bootstrap_controller_unittest.cc
@@ -235,8 +235,8 @@
   fake_target_device_connection_broker_->on_start_advertising_callback().Run(
       /*success=*/true);
   EXPECT_EQ(fake_observer_->last_status.step, Step::ADVERTISING_WITH_QR_CODE);
-  EXPECT_TRUE(absl::holds_alternative<QRCode::PixelData>(
-      fake_observer_->last_status.payload));
+  EXPECT_TRUE(
+      absl::holds_alternative<QRCode>(fake_observer_->last_status.payload));
 }
 
 TEST_F(TargetDeviceBootstrapControllerTest,
@@ -258,8 +258,8 @@
   bootstrap_controller_->StartAdvertisingAndMaybeGetQRCode();
   EXPECT_EQ(fake_observer_->last_status.step,
             Step::ADVERTISING_WITHOUT_QR_CODE);
-  EXPECT_FALSE(absl::holds_alternative<QRCode::PixelData>(
-      fake_observer_->last_status.payload));
+  EXPECT_FALSE(
+      absl::holds_alternative<QRCode>(fake_observer_->last_status.payload));
   EXPECT_EQ(
       1u, fake_target_device_connection_broker_->num_start_advertising_calls());
   ASSERT_TRUE(fake_target_device_connection_broker_
@@ -310,14 +310,14 @@
   fake_target_device_connection_broker_->on_start_advertising_callback().Run(
       /*success=*/true);
   ASSERT_EQ(fake_observer_->last_status.step, Step::ADVERTISING_WITH_QR_CODE);
-  EXPECT_TRUE(absl::holds_alternative<QRCode::PixelData>(
-      fake_observer_->last_status.payload));
+  EXPECT_TRUE(
+      absl::holds_alternative<QRCode>(fake_observer_->last_status.payload));
 
   fake_target_device_connection_broker_->InitiateConnection(kSourceDeviceId);
   // Status shouldn't change.
   EXPECT_EQ(fake_observer_->last_status.step, Step::ADVERTISING_WITH_QR_CODE);
-  EXPECT_TRUE(absl::holds_alternative<QRCode::PixelData>(
-      fake_observer_->last_status.payload));
+  EXPECT_TRUE(
+      absl::holds_alternative<QRCode>(fake_observer_->last_status.payload));
 }
 
 TEST_F(TargetDeviceBootstrapControllerTest, InitiateConnection_Pin) {
@@ -340,8 +340,8 @@
 
 TEST_F(TargetDeviceBootstrapControllerTest, AuthenticateConnection) {
   BootstrapConnection();
-  EXPECT_TRUE(absl::holds_alternative<QRCode::PixelData>(
-      fake_observer_->last_status.payload));
+  EXPECT_TRUE(
+      absl::holds_alternative<QRCode>(fake_observer_->last_status.payload));
 }
 
 TEST_F(TargetDeviceBootstrapControllerTest, FeatureSupportStatus) {
@@ -470,8 +470,8 @@
       kSourceDeviceId, QuickStartMetrics::AuthenticationMethod::kQRCode);
 
   EXPECT_EQ(fake_observer_->last_status.step, Step::ADVERTISING_WITH_QR_CODE);
-  EXPECT_TRUE(absl::holds_alternative<QRCode::PixelData>(
-      fake_observer_->last_status.payload));
+  EXPECT_TRUE(
+      absl::holds_alternative<QRCode>(fake_observer_->last_status.payload));
 
   fake_target_device_connection_broker_->GetFakeConnection()->VerifyUser(
       mojom::UserVerificationResponse(
@@ -527,8 +527,8 @@
       kSourceDeviceId, QuickStartMetrics::AuthenticationMethod::kQRCode);
 
   EXPECT_EQ(fake_observer_->last_status.step, Step::ADVERTISING_WITH_QR_CODE);
-  EXPECT_TRUE(absl::holds_alternative<QRCode::PixelData>(
-      fake_observer_->last_status.payload));
+  EXPECT_TRUE(
+      absl::holds_alternative<QRCode>(fake_observer_->last_status.payload));
 
   fake_target_device_connection_broker_->GetFakeConnection()->VerifyUser(
       mojom::UserVerificationResponse(
@@ -550,8 +550,8 @@
       kSourceDeviceId, QuickStartMetrics::AuthenticationMethod::kQRCode);
 
   EXPECT_EQ(fake_observer_->last_status.step, Step::ADVERTISING_WITH_QR_CODE);
-  EXPECT_TRUE(absl::holds_alternative<QRCode::PixelData>(
-      fake_observer_->last_status.payload));
+  EXPECT_TRUE(
+      absl::holds_alternative<QRCode>(fake_observer_->last_status.payload));
 
   fake_target_device_connection_broker_->GetFakeConnection()->VerifyUser(
       std::nullopt);
@@ -762,8 +762,8 @@
       kSourceDeviceId, QuickStartMetrics::AuthenticationMethod::kQRCode);
 
   EXPECT_EQ(fake_observer_->last_status.step, Step::ADVERTISING_WITH_QR_CODE);
-  EXPECT_TRUE(absl::holds_alternative<QRCode::PixelData>(
-      fake_observer_->last_status.payload));
+  EXPECT_TRUE(
+      absl::holds_alternative<QRCode>(fake_observer_->last_status.payload));
 
   bootstrap_controller_->StopAdvertising();
   fake_target_device_connection_broker_->on_stop_advertising_callback().Run();
@@ -822,8 +822,8 @@
   bootstrap_controller_->StartAdvertisingAndMaybeGetQRCode();
   EXPECT_EQ(fake_observer_->last_status.step,
             Step::ADVERTISING_WITHOUT_QR_CODE);
-  EXPECT_FALSE(absl::holds_alternative<QRCode::PixelData>(
-      fake_observer_->last_status.payload));
+  EXPECT_FALSE(
+      absl::holds_alternative<QRCode>(fake_observer_->last_status.payload));
   fake_target_device_connection_broker_->on_start_advertising_callback().Run(
       /*success=*/true);
   fake_target_device_connection_broker_->InitiateConnection(kSourceDeviceId);
diff --git a/chrome/browser/ash/login/password_change_browsertest.cc b/chrome/browser/ash/login/password_change_browsertest.cc
index 4a8b6c1..6dcc2d9 100644
--- a/chrome/browser/ash/login/password_change_browsertest.cc
+++ b/chrome/browser/ash/login/password_change_browsertest.cc
@@ -8,58 +8,49 @@
 #include <utility>
 
 #include "ash/public/cpp/login_screen_test_api.h"
+#include "ash/public/cpp/reauth_reason.h"
 #include "base/auto_reset.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
-#include "base/functional/bind.h"
 #include "base/functional/callback.h"
 #include "base/run_loop.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/threading/thread_restrictions.h"
-#include "chrome/browser/ash/login/existing_user_controller.h"
 #include "chrome/browser/ash/login/login_manager_test.h"
 #include "chrome/browser/ash/login/reauth_stats.h"
 #include "chrome/browser/ash/login/session/user_session_manager.h"
-#include "chrome/browser/ash/login/session/user_session_manager_test_api.h"
 #include "chrome/browser/ash/login/signin/signin_error_notifier.h"
 #include "chrome/browser/ash/login/signin/token_handle_util.h"
-#include "chrome/browser/ash/login/signin_specifics.h"
 #include "chrome/browser/ash/login/test/auth_ui_utils.h"
 #include "chrome/browser/ash/login/test/cryptohome_mixin.h"
-#include "chrome/browser/ash/login/test/js_checker.h"
 #include "chrome/browser/ash/login/test/local_state_mixin.h"
 #include "chrome/browser/ash/login/test/login_manager_mixin.h"
 #include "chrome/browser/ash/login/test/oobe_screen_waiter.h"
 #include "chrome/browser/ash/login/test/oobe_window_visibility_waiter.h"
-#include "chrome/browser/ash/login/test/session_manager_state_waiter.h"
 #include "chrome/browser/ash/login/test/user_auth_config.h"
 #include "chrome/browser/ash/login/ui/login_display_host.h"
-#include "chrome/browser/ash/login/wizard_context.h"
 #include "chrome/browser/ash/net/delay_network_call.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/lifetime/termination_notification.h"
 #include "chrome/browser/notifications/notification_display_service_tester.h"
+#include "chrome/browser/notifications/notification_handler.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/profiles/profile_manager_observer.h"
-#include "chrome/browser/ui/webui/ash/login/gaia_password_changed_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/gaia_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/oobe_ui.h"
 #include "chrome/test/base/fake_gaia_mixin.h"
 #include "chrome/test/base/interactive_test_utils.h"
+#include "chrome/test/base/mixin_based_in_process_browser_test.h"
 #include "chromeos/ash/components/cryptohome/cryptohome_parameters.h"
-#include "chromeos/ash/components/dbus/cryptohome/UserDataAuth.pb.h"
-#include "chromeos/ash/components/dbus/userdataauth/fake_cryptohome_misc_client.h"
 #include "chromeos/ash/components/dbus/userdataauth/fake_userdataauth_client.h"
-#include "chromeos/ash/components/dbus/userdataauth/userdataauth_client.h"
 #include "chromeos/ash/components/login/auth/public/user_context.h"
 #include "chromeos/ash/components/login/auth/stub_authenticator.h"
-#include "chromeos/ash/components/login/auth/stub_authenticator_builder.h"
 #include "components/account_id/account_id.h"
 #include "components/user_manager/known_user.h"
 #include "components/user_manager/user_manager.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/test_launcher.h"
-#include "content/public/test/test_utils.h"
+#include "ui/events/keycodes/keyboard_codes_posix.h"
 
 namespace ash {
 
diff --git a/chrome/browser/ash/login/quickstart_controller.cc b/chrome/browser/ash/login/quickstart_controller.cc
index 3144f33ad..51980cf 100644
--- a/chrome/browser/ash/login/quickstart_controller.cc
+++ b/chrome/browser/ash/login/quickstart_controller.cc
@@ -418,9 +418,9 @@
   switch (status.step) {
     case Step::ADVERTISING_WITH_QR_CODE:
       controller_state_ = ControllerState::ADVERTISING;
-      CHECK(absl::holds_alternative<QRCode::PixelData>(status.payload))
+      CHECK(absl::holds_alternative<QRCode>(status.payload))
           << "Missing expected QR Code data";
-      qr_code_data_ = absl::get<QRCode::PixelData>(status.payload);
+      qr_code_ = absl::get<QRCode>(status.payload);
       UpdateUiState(UiState::SHOWING_QR);
       return;
     case Step::ADVERTISING_WITHOUT_QR_CODE:
@@ -771,8 +771,8 @@
 
 void QuickStartController::ResetState() {
   entry_point_.reset();
-  qr_code_data_.reset();
   fallback_url_.reset();
+  qr_code_.reset();
   pin_.reset();
   user_info_ = UserInfo();
   gaia_creds_ = TargetDeviceBootstrapController::GaiaCredentials();
diff --git a/chrome/browser/ash/login/quickstart_controller.h b/chrome/browser/ash/login/quickstart_controller.h
index ff2feeab..29bc8db7 100644
--- a/chrome/browser/ash/login/quickstart_controller.h
+++ b/chrome/browser/ash/login/quickstart_controller.h
@@ -130,7 +130,7 @@
 
   // Accessors methods to be used by the UI for retrieving data. It is an error
   // to retrieve these values when they do not exist.
-  QRCode::PixelData GetQrCode() { return qr_code_data_.value(); }
+  QRCode GetQrCode() { return qr_code_.value(); }
   std::string GetPin() { return pin_.value(); }
   std::string GetDiscoverableName() { return discoverable_name_.value(); }
   UserInfo GetUserInfo() { return user_info_; }
@@ -247,7 +247,7 @@
   std::optional<std::string> discoverable_name_;
 
   // QR Code to be shown on the UI when requested.
-  std::optional<QRCode::PixelData> qr_code_data_;
+  std::optional<QRCode> qr_code_;
 
   // PIN to be shown on the UI when requested.
   std::optional<std::string> pin_;
diff --git a/chrome/browser/ash/login/screens/osauth/cryptohome_recovery_screen_browsertest.cc b/chrome/browser/ash/login/screens/osauth/cryptohome_recovery_screen_browsertest.cc
index 657f9c5..4b20250 100644
--- a/chrome/browser/ash/login/screens/osauth/cryptohome_recovery_screen_browsertest.cc
+++ b/chrome/browser/ash/login/screens/osauth/cryptohome_recovery_screen_browsertest.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include <optional>
+#include <string>
 #include <utility>
 
 #include "ash/public/cpp/login_screen_test_api.h"
@@ -13,7 +14,6 @@
 #include "chrome/browser/ash/login/test/auth_ui_utils.h"
 #include "chrome/browser/ash/login/test/cryptohome_mixin.h"
 #include "chrome/browser/ash/login/test/fake_recovery_service_mixin.h"
-#include "chrome/browser/ash/login/test/js_checker.h"
 #include "chrome/browser/ash/login/test/login_manager_mixin.h"
 #include "chrome/browser/ash/login/test/oobe_base_test.h"
 #include "chrome/browser/ash/login/test/oobe_screen_waiter.h"
@@ -25,10 +25,8 @@
 #include "chrome/browser/ash/login/wizard_controller.h"
 #include "chrome/browser/ui/webui/ash/login/cryptohome_recovery_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/enter_old_password_screen_handler.h"
-#include "chrome/browser/ui/webui/ash/login/gaia_password_changed_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/gaia_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/osauth/local_data_loss_warning_screen_handler.h"
-#include "chrome/browser/ui/webui/ash/login/user_creation_screen_handler.h"
 #include "chrome/test/base/fake_gaia_mixin.h"
 #include "chromeos/ash/components/dbus/cryptohome/UserDataAuth.pb.h"
 #include "chromeos/ash/components/dbus/userdataauth/fake_userdataauth_client.h"
@@ -36,7 +34,6 @@
 #include "components/account_id/account_id.h"
 #include "components/user_manager/user_type.h"
 #include "content/public/test/browser_test.h"
-#include "net/http/http_status_code.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace ash {
diff --git a/chrome/browser/ash/login/screens/quick_start_screen.cc b/chrome/browser/ash/login/screens/quick_start_screen.cc
index 1ad1044e..efe22e4 100644
--- a/chrome/browser/ash/login/screens/quick_start_screen.cc
+++ b/chrome/browser/ash/login/screens/quick_start_screen.cc
@@ -120,7 +120,8 @@
   switch (state) {
     case ash::quick_start::QuickStartController::UiState::SHOWING_QR:
       view_->SetWillRequestWiFi(controller_->WillRequestWiFi());
-      view_->SetQRCode(ConvertQrCode(controller_->GetQrCode()));
+      view_->SetQRCode(ConvertQrCode(controller_->GetQrCode().GetPixelData()),
+                       controller_->GetQrCode().GetQRCodeURLString());
       break;
     case quick_start::QuickStartController::UiState::SHOWING_PIN:
       view_->SetWillRequestWiFi(controller_->WillRequestWiFi());
diff --git a/chrome/browser/ash/login/screens/quick_start_screen_browsertest.cc b/chrome/browser/ash/login/screens/quick_start_screen_browsertest.cc
index 6525376..94891487 100644
--- a/chrome/browser/ash/login/screens/quick_start_screen_browsertest.cc
+++ b/chrome/browser/ash/login/screens/quick_start_screen_browsertest.cc
@@ -495,6 +495,7 @@
   const int qr_code_size = WizardController::default_controller()
                                ->quick_start_controller()
                                ->GetQrCode()
+                               .GetPixelData()
                                .size();
 
   // Get the number of cells per row/column (CELL_COUNT) exposed on <canvas>
diff --git a/chrome/browser/ash/login/test/auth_ui_utils.cc b/chrome/browser/ash/login/test/auth_ui_utils.cc
index 5a7e182..e669274 100644
--- a/chrome/browser/ash/login/test/auth_ui_utils.cc
+++ b/chrome/browser/ash/login/test/auth_ui_utils.cc
@@ -21,7 +21,6 @@
 #include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ui/webui/ash/login/cryptohome_recovery_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/enter_old_password_screen_handler.h"
-#include "chrome/browser/ui/webui/ash/login/gaia_password_changed_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/gaia_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/local_password_setup_handler.h"
 #include "chrome/browser/ui/webui/ash/login/osauth/factor_setup_success_screen_handler.h"
diff --git a/chrome/browser/ash/login/ui/login_display_host_mojo.cc b/chrome/browser/ash/login/ui/login_display_host_mojo.cc
index 547b327..96d3def 100644
--- a/chrome/browser/ash/login/ui/login_display_host_mojo.cc
+++ b/chrome/browser/ash/login/ui/login_display_host_mojo.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/ash/login/ui/login_display_host_mojo.h"
 
+#include <optional>
 #include <utility>
 
 #include "ash/constants/ash_features.h"
@@ -11,22 +12,28 @@
 #include "ash/constants/ash_switches.h"
 #include "ash/public/cpp/input_device_settings_controller.h"
 #include "ash/public/cpp/login/local_authentication_request_controller.h"
-#include "ash/public/cpp/login/login_utils.h"
+#include "ash/public/cpp/login_accelerators.h"
 #include "ash/public/cpp/login_screen.h"
 #include "ash/public/cpp/login_screen_model.h"
+#include "ash/public/cpp/login_types.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/shell.h"
 #include "ash/style/color_palette_controller.h"
 #include "ash/system/model/enterprise_domain_model.h"
 #include "ash/system/model/system_tray_model.h"
+#include "base/check.h"
+#include "base/check_op.h"
 #include "base/command_line.h"
+#include "base/feature_list.h"
 #include "base/functional/bind.h"
 #include "base/functional/callback.h"
+#include "base/location.h"
 #include "base/logging.h"
 #include "base/metrics/histogram_macros.h"
-#include "base/task/single_thread_task_runner.h"
+#include "base/notreached.h"
 #include "base/time/time.h"
-#include "chrome/browser/ash/accessibility/accessibility_manager.h"
+#include "base/values.h"
+#include "chrome/browser/ash/login/challenge_response_auth_keys_loader.h"
 #include "chrome/browser/ash/login/existing_user_controller.h"
 #include "chrome/browser/ash/login/helper.h"
 #include "chrome/browser/ash/login/mojo_system_info_dispatcher.h"
@@ -35,45 +42,54 @@
 #include "chrome/browser/ash/login/reauth_stats.h"
 #include "chrome/browser/ash/login/screens/chrome_user_selection_screen.h"
 #include "chrome/browser/ash/login/screens/gaia_screen.h"
+#include "chrome/browser/ash/login/screens/user_selection_screen.h"
 #include "chrome/browser/ash/login/security_token_session_controller.h"
+#include "chrome/browser/ash/login/ui/login_display_host.h"
+#include "chrome/browser/ash/login/ui/login_display_host_common.h"
+#include "chrome/browser/ash/login/ui/signin_ui.h"
+#include "chrome/browser/ash/login/wizard_context.h"
 #include "chrome/browser/ash/login/wizard_controller.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/certificate_provider/certificate_provider_service.h"
 #include "chrome/browser/certificate_provider/certificate_provider_service_factory.h"
-#include "chrome/browser/certificate_provider/pin_dialog_manager.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/ash/login_screen_client_impl.h"
 #include "chrome/browser/ui/ash/system_tray_client_impl.h"
 #include "chrome/browser/ui/ash/wallpaper_controller_client_impl.h"
 #include "chrome/browser/ui/webui/ash/login/enable_adb_sideloading_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/enable_debugging_screen_handler.h"
-#include "chrome/browser/ui/webui/ash/login/gaia_password_changed_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/gaia_screen_handler.h"
+#include "chrome/browser/ui/webui/ash/login/guest_tos_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/kiosk_autolaunch_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/lacros_data_migration_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/os_install_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/remote_activity_notification_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/reset_screen_handler.h"
-#include "chrome/browser/ui/webui/ash/login/signin_fatal_error_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/user_creation_screen_handler.h"
-#include "chrome/common/channel_info.h"
 #include "chrome/common/pref_names.h"
 #include "chromeos/ash/components/browser_context_helper/browser_context_helper.h"
+#include "chromeos/ash/components/login/auth/public/auth_failure.h"
 #include "chromeos/ash/components/login/auth/public/auth_types.h"
+#include "chromeos/ash/components/login/auth/public/key.h"
 #include "chromeos/ash/components/login/auth/public/user_context.h"
 #include "chromeos/ash/components/osauth/public/auth_hub.h"
+#include "chromeos/ash/components/osauth/public/common_types.h"
 #include "chromeos/ash/components/settings/cros_settings.h"
+#include "chromeos/ash/components/settings/cros_settings_names.h"
 #include "components/account_id/account_id.h"
 #include "components/startup_metric_utils/common/startup_metric_utils.h"
 #include "components/user_manager/known_user.h"
 #include "components/user_manager/user.h"
 #include "components/user_manager/user_manager.h"
 #include "components/user_manager/user_names.h"
-#include "components/version_info/version_info.h"
-#include "content/public/browser/browser_thread.h"
+#include "components/user_manager/user_type.h"
+#include "content/public/browser/web_contents.h"
 #include "google_apis/gaia/gaia_auth_util.h"
 #include "ui/aura/window.h"
 #include "ui/compositor/layer.h"
+#include "ui/events/event.h"
+#include "ui/events/keycodes/keyboard_codes_posix.h"
+#include "ui/gfx/native_widget_types.h"
 #include "ui/views/view.h"
 
 namespace ash {
diff --git a/chrome/browser/ash/preferences.cc b/chrome/browser/ash/preferences.cc
index 4a8dd27..12870d9 100644
--- a/chrome/browser/ash/preferences.cc
+++ b/chrome/browser/ash/preferences.cc
@@ -50,6 +50,7 @@
 #include "chrome/browser/download/download_prefs.h"
 #include "chrome/browser/prefs/pref_service_syncable_util.h"
 #include "chrome/browser/ui/ash/system_tray_client_impl.h"
+#include "chrome/browser/ui/chromeos/magic_boost/magic_boost_controller.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/pref_names.h"
 #include "chromeos/ash/components/dbus/pciguard/pciguard_client.h"
@@ -571,6 +572,12 @@
 
   registry->RegisterBooleanPref(prefs::kMahiEnabled, true);
 
+  registry->RegisterIntegerPref(
+      prefs::kHMRConsentStatus,
+      base::to_underlying(chromeos::HMRConsentStatus::kUnset));
+
+  registry->RegisterIntegerPref(prefs::kHMRConsentWindowDismissCount, 0);
+
   registry->RegisterBooleanPref(
       prefs::kLauncherResultEverLaunched, false,
       user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF);
diff --git a/chrome/browser/ash/printing/usb_printer_detector.cc b/chrome/browser/ash/printing/usb_printer_detector.cc
index 5ca8671..7bff4a0 100644
--- a/chrome/browser/ash/printing/usb_printer_detector.cc
+++ b/chrome/browser/ash/printing/usb_printer_detector.cc
@@ -10,6 +10,8 @@
 #include <utility>
 #include <vector>
 
+#include "ash/public/cpp/session/session_controller.h"
+#include "ash/public/cpp/session/session_observer.h"
 #include "base/functional/bind.h"
 #include "base/functional/callback_helpers.h"
 #include "base/sequence_checker.h"
@@ -40,14 +42,54 @@
 namespace ash {
 namespace {
 
+// Helper class, redirect calls to SessionController provided as a constructor's
+// parameter. If the parameter is nullptr, the pointer returned by
+// ash::SessionController::Get() is used (if not nullptr).
+class SessionControllerWrapper {
+ public:
+  explicit SessionControllerWrapper(SessionController* session_controller)
+      : session_controller_(session_controller) {}
+  SessionControllerWrapper(const SessionControllerWrapper&) = delete;
+  SessionControllerWrapper& operator=(const SessionControllerWrapper&) = delete;
+
+  void AddObserver(SessionObserver* observer) const {
+    if (SessionController* controller = GetSessionController(); controller) {
+      controller->AddObserver(observer);
+    }
+  }
+  void RemoveObserver(SessionObserver* observer) const {
+    if (SessionController* controller = GetSessionController(); controller) {
+      controller->RemoveObserver(observer);
+    }
+  }
+  bool IsScreenLocked() const {
+    if (SessionController* controller = GetSessionController(); controller) {
+      return controller->IsScreenLocked();
+    }
+    return false;
+  }
+
+ private:
+  SessionController* GetSessionController() const {
+    if (session_controller_) {
+      return session_controller_;
+    }
+    return SessionController::Get();
+  }
+  raw_ptr<SessionController> session_controller_;
+};
+
 // The PrinterDetector that drives the flow for setting up a USB printer to use
 // CUPS backend.
 class UsbPrinterDetectorImpl : public UsbPrinterDetector,
-                               public device::mojom::UsbDeviceManagerClient {
+                               public device::mojom::UsbDeviceManagerClient,
+                               public ash::SessionObserver {
  public:
   explicit UsbPrinterDetectorImpl(
-      mojo::PendingRemote<device::mojom::UsbDeviceManager> device_manager)
-      : device_manager_(std::move(device_manager)) {
+      mojo::PendingRemote<device::mojom::UsbDeviceManager> device_manager,
+      ash::SessionController* session_controller = nullptr)
+      : device_manager_(std::move(device_manager)),
+        session_controller_(session_controller) {
     device_manager_.set_disconnect_handler(
         base::BindOnce(&UsbPrinterDetectorImpl::OnDeviceManagerConnectionError,
                        weak_factory_.GetWeakPtr()));
@@ -57,10 +99,13 @@
         client_receiver_.BindNewEndpointAndPassRemote(),
         base::BindOnce(&UsbPrinterDetectorImpl::OnGetDevices,
                        weak_factory_.GetWeakPtr()));
+
+    session_controller_.AddObserver(this);
   }
 
   ~UsbPrinterDetectorImpl() override {
     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_);
+    session_controller_.RemoveObserver(this);
   }
 
   // PrinterDetector override.
@@ -74,8 +119,8 @@
   std::vector<DetectedPrinter> GetPrinters() override {
     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_);
     std::vector<DetectedPrinter> printers_list;
-    printers_list.reserve(printers_.size());
-    for (const auto& entry : printers_) {
+    printers_list.reserve(printers_ready_.size());
+    for (const auto& entry : printers_ready_) {
       printers_list.push_back(entry.second);
     }
     return printers_list;
@@ -94,7 +139,8 @@
     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_);
     device_manager_.reset();
     client_receiver_.reset();
-    printers_.clear();
+    printers_ready_.clear();
+    printers_locked_screen_.clear();
   }
 
   void DoAddDevice(const device::mojom::UsbDeviceInfo& device_info) {
@@ -143,9 +189,13 @@
     entry.ppd_search_data.printer_id = std::move(printer_id);
 
     // Add detected printer.
-    printers_[guid] = entry;
-    if (on_printers_found_callback_) {
-      on_printers_found_callback_.Run(GetPrinters());
+    if (session_controller_.IsScreenLocked()) {
+      printers_locked_screen_[guid] = entry;
+    } else {
+      printers_ready_[guid] = entry;
+      if (on_printers_found_callback_) {
+        on_printers_found_callback_.Run(GetPrinters());
+      }
     }
   }
 
@@ -163,7 +213,22 @@
     if (!UsbDeviceIsPrinter(*device_info)) {
       return;
     }
-    printers_.erase(device_info->guid);
+    if (printers_ready_.erase(device_info->guid)) {
+      if (on_printers_found_callback_) {
+        on_printers_found_callback_.Run(GetPrinters());
+      }
+    } else {
+      printers_locked_screen_.erase(device_info->guid);
+    }
+  }
+
+  // ash::SessionObserver implementation.
+  void OnLockStateChanged(bool locked) override {
+    if (locked || printers_locked_screen_.empty()) {
+      return;
+    }
+    printers_ready_.merge(printers_locked_screen_);
+    printers_locked_screen_.clear();
     if (on_printers_found_callback_) {
       on_printers_found_callback_.Run(GetPrinters());
     }
@@ -171,14 +236,20 @@
 
   SEQUENCE_CHECKER(sequence_);
 
-  // Map from USB GUID to DetectedPrinter for all detected printers.
-  std::map<std::string, DetectedPrinter> printers_;
+  // Map from USB GUID to DetectedPrinter for all detected printers. Printers
+  // detected when the screen is locked are saved in `printers_locked_screen_`.
+  // They are later moved to `printers_ready_` when the screen is unlocked.
+  // This is required because locking the screen activates usbguard that blocks
+  // access to USB ports, so we have to defer installation of USB printers.
+  std::map<std::string, DetectedPrinter> printers_ready_;
+  std::map<std::string, DetectedPrinter> printers_locked_screen_;
 
   OnPrintersFoundCallback on_printers_found_callback_;
 
   mojo::Remote<device::mojom::UsbDeviceManager> device_manager_;
   mojo::AssociatedReceiver<device::mojom::UsbDeviceManagerClient>
       client_receiver_{this};
+  SessionControllerWrapper session_controller_;
   base::WeakPtrFactory<UsbPrinterDetectorImpl> weak_factory_{this};
 };
 
@@ -194,8 +265,10 @@
 }
 
 std::unique_ptr<UsbPrinterDetector> UsbPrinterDetector::CreateForTesting(
-    mojo::PendingRemote<device::mojom::UsbDeviceManager> usb_manager) {
-  return std::make_unique<UsbPrinterDetectorImpl>(std::move(usb_manager));
+    mojo::PendingRemote<device::mojom::UsbDeviceManager> usb_manager,
+    ash::SessionController* session_controller) {
+  return std::make_unique<UsbPrinterDetectorImpl>(std::move(usb_manager),
+                                                  session_controller);
 }
 
 }  // namespace ash
diff --git a/chrome/browser/ash/printing/usb_printer_detector.h b/chrome/browser/ash/printing/usb_printer_detector.h
index f0d7f82..cd7295a 100644
--- a/chrome/browser/ash/printing/usb_printer_detector.h
+++ b/chrome/browser/ash/printing/usb_printer_detector.h
@@ -13,6 +13,8 @@
 
 namespace ash {
 
+class SessionController;
+
 // Observes device::UsbService for addition of USB printers, and implements the
 // PrinterDetector interface to export this to print system consumers.
 class UsbPrinterDetector : public PrinterDetector {
@@ -21,7 +23,8 @@
   static std::unique_ptr<UsbPrinterDetector> Create();
 
   static std::unique_ptr<UsbPrinterDetector> CreateForTesting(
-      mojo::PendingRemote<device::mojom::UsbDeviceManager> usb_manager);
+      mojo::PendingRemote<device::mojom::UsbDeviceManager> usb_manager,
+      ash::SessionController* session_controller);
 
   UsbPrinterDetector(const UsbPrinterDetector&) = delete;
   UsbPrinterDetector& operator=(const UsbPrinterDetector&) = delete;
diff --git a/chrome/browser/ash/printing/usb_printer_detector_unittest.cc b/chrome/browser/ash/printing/usb_printer_detector_unittest.cc
index 205e4ec..456b43b 100644
--- a/chrome/browser/ash/printing/usb_printer_detector_unittest.cc
+++ b/chrome/browser/ash/printing/usb_printer_detector_unittest.cc
@@ -7,7 +7,11 @@
 #include <utility>
 #include <vector>
 
+#include "ash/public/cpp/session/session_controller.h"
+#include "ash/public/cpp/session/session_observer.h"
+#include "ash/public/cpp/test/mock_session_controller.h"
 #include "base/functional/bind.h"
+#include "base/memory/raw_ptr.h"
 #include "base/run_loop.h"
 #include "base/test/task_environment.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
@@ -15,6 +19,7 @@
 #include "services/device/public/cpp/test/fake_usb_device_manager.h"
 #include "services/device/public/mojom/usb_device.mojom.h"
 #include "services/device/public/mojom/usb_manager.mojom.h"
+#include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace ash {
@@ -69,10 +74,23 @@
   };
 
   UsbPrinterDetectorTest() {
+    ON_CALL(this->session_controller_, IsScreenLocked).WillByDefault([this]() {
+      return this->screen_locked_;
+    });
+    ON_CALL(this->session_controller_, AddObserver)
+        .WillByDefault([this](ash::SessionObserver* observer) {
+          this->observer_ = observer;
+        });
+    ON_CALL(this->session_controller_, RemoveObserver)
+        .WillByDefault([this](ash::SessionObserver* observer) {
+          this->observer_ = nullptr;
+        });
+
     mojo::PendingRemote<device::mojom::UsbDeviceManager> manager;
     usb_manager_.AddReceiver(manager.InitWithNewPipeAndPassReceiver());
 
-    detector_ = UsbPrinterDetector::CreateForTesting(std::move(manager));
+    detector_ = UsbPrinterDetector::CreateForTesting(std::move(manager),
+                                                     &session_controller_);
     detector_->RegisterPrintersFoundCallback(
         base::BindRepeating(&FakePrinterDetectorClient::OnPrintersFound,
                             base::Unretained(&detector_client_)));
@@ -86,6 +104,9 @@
   }
 
   base::test::TaskEnvironment task_environment_;
+  bool screen_locked_ = false;
+  raw_ptr<ash::SessionObserver> observer_ = nullptr;
+  testing::NiceMock<ash::MockSessionController> session_controller_;
   std::unique_ptr<UsbPrinterDetector> detector_;
   FakePrinterDetectorClient detector_client_;
   device::FakeUsbDeviceManager usb_manager_;
@@ -134,5 +155,27 @@
   EXPECT_EQ(0u, detector_->GetPrinters().size());
 }
 
+// New printers are not announced as long as the screen is locked.
+TEST_F(UsbPrinterDetectorTest, NewPrintersHoldWhenTheScreenIsLocked) {
+  screen_locked_ = true;
+
+  usb_manager_.AddDevice(fake_printer_);
+  usb_manager_.AddDevice(fake_printer_ipp_);
+
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(0u, detector_->GetPrinters().size());
+
+  ASSERT_NE(observer_, nullptr);
+  observer_->OnLockStateChanged(screen_locked_);
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(0u, detector_->GetPrinters().size());
+
+  screen_locked_ = false;
+  observer_->OnLockStateChanged(screen_locked_);
+
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(2u, detector_->GetPrinters().size());
+}
+
 }  // namespace
 }  // namespace ash
diff --git a/chrome/browser/ash/system_web_apps/apps/personalization_app/personalization_app_wallpaper_info_browsertest.cc b/chrome/browser/ash/system_web_apps/apps/personalization_app/personalization_app_wallpaper_info_browsertest.cc
index a8b8461..dbc2d8f 100644
--- a/chrome/browser/ash/system_web_apps/apps/personalization_app/personalization_app_wallpaper_info_browsertest.cc
+++ b/chrome/browser/ash/system_web_apps/apps/personalization_app/personalization_app_wallpaper_info_browsertest.cc
@@ -144,7 +144,6 @@
   }
 
  private:
-  base::test::ScopedFeatureList scoped_feature_list_;
   TestChromeWebUIControllerFactory test_chrome_webui_controller_factory_;
   TestPersonalizationAppWebUIProvider test_webui_provider_;
   content::ScopedWebUIControllerFactoryRegistration
@@ -353,5 +352,45 @@
   EXPECT_EQ(new_info.user_file_path, synced_info.user_file_path);
 }
 
+class PersonalizationAppVersionedWallpaperInfoBrowserTest
+    : public PersonalizationAppWallpaperInfoBrowserTest {
+ public:
+  PersonalizationAppVersionedWallpaperInfoBrowserTest() {
+    scoped_feature_list_.InitAndEnableFeature(
+        features::kVersionedWallpaperInfo);
+  }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+IN_PROC_BROWSER_TEST_F(PersonalizationAppVersionedWallpaperInfoBrowserTest,
+                       OnlineWallpaperFetchMissingUnitIdSuccessfully) {
+  WallpaperInfo info =
+      WallpaperInfo(std::string(), WALLPAPER_LAYOUT_CENTER_CROPPED,
+                    WallpaperType::kOnline, base::Time::Now());
+  info.collection_id = kDummyCollectionId;
+  info.location = kDummyUrl;
+
+  base::RunLoop loop;
+  WallpaperChangedWaiter waiter(loop.QuitClosure());
+  const AccountId account_id = GetAccountId(browser()->profile());
+  PutWallpaperInfoInPrefs(account_id, info, browser()->profile()->GetPrefs(),
+                          prefs::kSyncableVersionedWallpaperInfo);
+  loop.Run();
+
+  WallpaperInfo new_info =
+      *wallpaper_controller()->GetActiveUserWallpaperInfo();
+
+  // Expects asset_id to be empty.
+  EXPECT_FALSE(new_info.asset_id.has_value());
+  // Expects unit_id, and variants to be set.
+  EXPECT_TRUE(new_info.unit_id.has_value());
+  EXPECT_EQ(new_info.variants.size(), 1u);
+  EXPECT_EQ(new_info.collection_id, kDummyCollectionId);
+  EXPECT_TRUE(new_info.version.IsValid());
+  EXPECT_EQ(new_info.version, GetSupportedVersion(new_info.type));
+}
+
 }  // namespace
 }  // namespace ash::personalization_app
diff --git a/chrome/browser/chrome_browser_main.cc b/chrome/browser/chrome_browser_main.cc
index 9245d5d..eedcfd7 100644
--- a/chrome/browser/chrome_browser_main.cc
+++ b/chrome/browser/chrome_browser_main.cc
@@ -46,6 +46,7 @@
 #include "base/threading/platform_thread.h"
 #include "base/time/default_tick_clock.h"
 #include "base/time/time.h"
+#include "base/trace_event/named_trigger.h"
 #include "base/trace_event/trace_event.h"
 #include "base/values.h"
 #include "build/branding_buildflags.h"
@@ -1223,6 +1224,8 @@
   tracing::MaybeSetupSystemTracingFromFieldTrial();
   tracing::SetupBackgroundTracingFromCommandLine();
   tracing::SetupPresetTracingFromFieldTrial();
+  base::trace_event::EmitNamedTrigger(
+      base::trace_event::kStartupTracingTriggerName);
 
   for (auto& chrome_extra_part : chrome_extra_parts_)
     chrome_extra_part->PostCreateThreads();
diff --git a/chrome/browser/commerce/product_specifications/product_specifications_service_factory.cc b/chrome/browser/commerce/product_specifications/product_specifications_service_factory.cc
index 54aa0fe..d865bee5f 100644
--- a/chrome/browser/commerce/product_specifications/product_specifications_service_factory.cc
+++ b/chrome/browser/commerce/product_specifications/product_specifications_service_factory.cc
@@ -66,11 +66,10 @@
 ProductSpecificationsServiceFactory::BuildServiceInstanceForBrowserContext(
     content::BrowserContext* context) const {
   return std::make_unique<commerce::ProductSpecificationsService>(
-      std::make_unique<ProductSpecificationsSyncBridge>(
-          ModelTypeStoreServiceFactory::GetForProfile(
-              Profile::FromBrowserContext(context))
-              ->GetStoreFactory(),
-          CreateChangeProcessor()));
+      ModelTypeStoreServiceFactory::GetForProfile(
+          Profile::FromBrowserContext(context))
+          ->GetStoreFactory(),
+      CreateChangeProcessor());
 }
 
 }  // namespace commerce
diff --git a/chrome/browser/compose/proactive_nudge_tracker.cc b/chrome/browser/compose/proactive_nudge_tracker.cc
index 5ee3e82c..dee0bf8 100644
--- a/chrome/browser/compose/proactive_nudge_tracker.cc
+++ b/chrome/browser/compose/proactive_nudge_tracker.cc
@@ -70,6 +70,10 @@
 ProactiveNudgeTracker::State::State() = default;
 ProactiveNudgeTracker::State::~State() = default;
 
+float ProactiveNudgeTracker::Delegate::SegmentationFallbackShowResult() {
+  return base::RandFloat();
+}
+
 ProactiveNudgeTracker::ProactiveNudgeTracker(
     segmentation_platform::SegmentationPlatformService* segmentation_service,
     Delegate* delegate)
@@ -151,6 +155,7 @@
     return true;
   }
   return state.segmentation_result &&
+         !state.segmentation_result->ordered_labels.empty() &&
          state.segmentation_result->ordered_labels[0] ==
              segmentation_platform::kComposePrmotionLabelShow;
 }
@@ -227,13 +232,21 @@
     return;
   }
 
-  if (result.status != segmentation_platform::PredictionStatus::kSucceeded) {
-    // Do not want to continue with proactive nudge if the segmentation platform
-    // had a failure.
-    ResetState();
-    return;
+  state_->segmentation_result = result;
+
+  switch (result.status) {
+    case segmentation_platform::PredictionStatus::kFailed:
+    case segmentation_platform::PredictionStatus::kNotReady:
+      if (delegate_->SegmentationFallbackShowResult() <
+          compose::GetComposeConfig().proactive_nudge_show_probability) {
+        // Override default DontShow decision.
+        state_->segmentation_result->ordered_labels.emplace_back(
+            segmentation_platform::kComposePrmotionLabelShow);
+      }
+      break;
+    case segmentation_platform::PredictionStatus::kSucceeded:
+      break;
   }
-  state->segmentation_result = std::move(result);
 
   MaybeShowProactiveNudge();
 }
diff --git a/chrome/browser/compose/proactive_nudge_tracker.h b/chrome/browser/compose/proactive_nudge_tracker.h
index 0d0f962..8dd896b 100644
--- a/chrome/browser/compose/proactive_nudge_tracker.h
+++ b/chrome/browser/compose/proactive_nudge_tracker.h
@@ -45,10 +45,16 @@
 //   showing the nudge.
 class ProactiveNudgeTracker : public autofill::AutofillManager::Observer {
  public:
+  using FallbackShowResult = base::RepeatingCallback<float()>;
+
   class Delegate {
    public:
     virtual void ShowProactiveNudge(autofill::FormGlobalId form,
                                     autofill::FieldGlobalId field) = 0;
+
+    // Compared with compose's Config random nudge probability to determine if
+    // we should show the nudge if segmentation fails.
+    virtual float SegmentationFallbackShowResult();
   };
 
   enum class ShowState { kWaiting, kCanBeShown, kShown };
diff --git a/chrome/browser/compose/proactive_nudge_tracker_unittest.cc b/chrome/browser/compose/proactive_nudge_tracker_unittest.cc
index 38ea9a3..c8b20aae 100644
--- a/chrome/browser/compose/proactive_nudge_tracker_unittest.cc
+++ b/chrome/browser/compose/proactive_nudge_tracker_unittest.cc
@@ -6,6 +6,7 @@
 
 #include <memory>
 
+#include "base/test/bind.h"
 #include "base/test/task_environment.h"
 #include "base/test/test_future.h"
 #include "components/autofill/core/common/autofill_test_utils.h"
@@ -48,6 +49,7 @@
   MOCK_METHOD(void,
               ShowProactiveNudge,
               (autofill::FormGlobalId, autofill::FieldGlobalId));
+  MOCK_METHOD(float, SegmentationFallbackShowResult, ());
 };
 
 class ProactiveNudgeTrackerTestBase : public testing::Test {
@@ -256,34 +258,6 @@
   }
 }
 
-TEST_P(ProactiveNudgeTrackerTest, SegmentationDoesNotSucceed) {
-  base::test::TestFuture<segmentation_platform::ClassificationResultCallback>
-      future;
-  auto field = CreateTestFormFieldData();
-  if (uses_segmentation()) {
-    BindFutureToSegmentationRequest(future);
-  }
-
-  EXPECT_CALL(delegate(),
-              ShowProactiveNudge(field.renderer_form_id(), field.global_id()))
-      .Times(uses_segmentation() ? 0 : 1);
-
-  EXPECT_FALSE(nudge_tracker().ProactiveNudgeRequestedForFormField(field));
-  task_environment().FastForwardBy(GetComposeConfig().proactive_nudge_delay);
-
-  if (uses_segmentation()) {
-    EXPECT_FALSE(nudge_tracker().ProactiveNudgeRequestedForFormField(field));
-    auto result = segmentation_platform::ClassificationResult(
-        segmentation_platform::PredictionStatus::kSucceeded);
-    result.ordered_labels = {
-        segmentation_platform::kComposePrmotionLabelDontShow};
-    future.Take().Run(result);
-  }
-
-  EXPECT_NE(uses_segmentation(),
-            nudge_tracker().ProactiveNudgeRequestedForFormField(field));
-}
-
 INSTANTIATE_TEST_SUITE_P(,
                          ProactiveNudgeTrackerTest,
                          ::testing::Bool(),
@@ -292,6 +266,89 @@
                                              : "SegmentationOFF";
                          });
 
+class ProactiveNudgeTrackerSegmentationTest
+    : public ProactiveNudgeTrackerTestBase {
+ public:
+  void SetUp() override {
+    ProactiveNudgeTrackerTestBase::SetUpNudgeTrackerTest(true);
+  }
+};
+
+TEST_F(ProactiveNudgeTrackerSegmentationTest, SegmentationDontShow) {
+  base::test::TestFuture<segmentation_platform::ClassificationResultCallback>
+      future;
+  auto field = CreateTestFormFieldData();
+  BindFutureToSegmentationRequest(future);
+
+  EXPECT_CALL(delegate(),
+              ShowProactiveNudge(field.renderer_form_id(), field.global_id()))
+      .Times(0);
+
+  ASSERT_FALSE(nudge_tracker().ProactiveNudgeRequestedForFormField(field));
+  task_environment().FastForwardBy(GetComposeConfig().proactive_nudge_delay);
+
+  ASSERT_FALSE(nudge_tracker().ProactiveNudgeRequestedForFormField(field));
+  auto result = segmentation_platform::ClassificationResult(
+      segmentation_platform::PredictionStatus::kSucceeded);
+  result.ordered_labels = {
+      segmentation_platform::kComposePrmotionLabelDontShow};
+  future.Take().Run(result);
+
+  EXPECT_FALSE(nudge_tracker().ProactiveNudgeRequestedForFormField(field));
+}
+
+TEST_F(ProactiveNudgeTrackerSegmentationTest,
+       SegmentationNotReadyFallbackDontShow) {
+  base::test::TestFuture<segmentation_platform::ClassificationResultCallback>
+      future;
+  auto field = CreateTestFormFieldData();
+
+  // Cause fallback to set DontShow.
+  EXPECT_CALL(delegate(), SegmentationFallbackShowResult)
+      .WillOnce(testing::Return(1.0f));
+  BindFutureToSegmentationRequest(future);
+
+  EXPECT_CALL(delegate(),
+              ShowProactiveNudge(field.renderer_form_id(), field.global_id()))
+      .Times(0);
+
+  ASSERT_FALSE(nudge_tracker().ProactiveNudgeRequestedForFormField(field));
+  task_environment().FastForwardBy(GetComposeConfig().proactive_nudge_delay);
+
+  ASSERT_FALSE(nudge_tracker().ProactiveNudgeRequestedForFormField(field));
+  auto result = segmentation_platform::ClassificationResult(
+      segmentation_platform::PredictionStatus::kNotReady);
+  future.Take().Run(result);
+
+  EXPECT_FALSE(nudge_tracker().ProactiveNudgeRequestedForFormField(field));
+}
+
+TEST_F(ProactiveNudgeTrackerSegmentationTest,
+       SegmentationNotReadyFallbackShow) {
+  base::test::TestFuture<segmentation_platform::ClassificationResultCallback>
+      future;
+  auto field = CreateTestFormFieldData();
+
+  // Cause fallback to set Show.
+  EXPECT_CALL(delegate(), SegmentationFallbackShowResult)
+      .WillOnce(testing::Return(0.0f));
+  BindFutureToSegmentationRequest(future);
+
+  EXPECT_CALL(delegate(),
+              ShowProactiveNudge(field.renderer_form_id(), field.global_id()))
+      .Times(1);
+
+  ASSERT_FALSE(nudge_tracker().ProactiveNudgeRequestedForFormField(field));
+  task_environment().FastForwardBy(GetComposeConfig().proactive_nudge_delay);
+
+  ASSERT_FALSE(nudge_tracker().ProactiveNudgeRequestedForFormField(field));
+  auto result = segmentation_platform::ClassificationResult(
+      segmentation_platform::PredictionStatus::kNotReady);
+  future.Take().Run(result);
+
+  EXPECT_TRUE(nudge_tracker().ProactiveNudgeRequestedForFormField(field));
+}
+
 class ProactiveNudgeTrackerDerivedEngagementTest
     : public ProactiveNudgeTrackerTestBase {
  public:
@@ -474,4 +531,5 @@
 }
 
 }  // namespace
+
 }  // namespace compose
diff --git a/chrome/browser/content_settings/request_desktop_site_web_contents_observer_android.cc b/chrome/browser/content_settings/request_desktop_site_web_contents_observer_android.cc
index 4e55b8e..8cde1c49 100644
--- a/chrome/browser/content_settings/request_desktop_site_web_contents_observer_android.cc
+++ b/chrome/browser/content_settings/request_desktop_site_web_contents_observer_android.cc
@@ -33,10 +33,7 @@
       Profile::FromBrowserContext(web_contents()->GetBrowserContext());
   host_content_settings_map_ =
       HostContentSettingsMapFactory::GetForProfile(profile);
-  if (base::FeatureList::IsEnabled(
-          features::kRequestDesktopSiteWindowSetting)) {
-    pref_service_ = profile->GetPrefs();
-  }
+  pref_service_ = profile->GetPrefs();
 }
 
 RequestDesktopSiteWebContentsObserverAndroid::
@@ -68,9 +65,7 @@
   bool is_global_setting = setting_info.primary_pattern.MatchesAllHosts();
 
   // RDS Window Setting support.
-  if (base::FeatureList::IsEnabled(
-          features::kRequestDesktopSiteWindowSetting) &&
-      !base::android::BuildInfo::GetInstance()->is_automotive() &&
+  if (!base::android::BuildInfo::GetInstance()->is_automotive() &&
       pref_service_->GetBoolean(prefs::kDesktopSiteWindowSettingEnabled) &&
       desktop_mode && !always_request_desktop_site && is_global_setting) {
     int web_contents_width_dp =
diff --git a/chrome/browser/download/download_item_model.cc b/chrome/browser/download/download_item_model.cc
index 74aa81b..977c34a6 100644
--- a/chrome/browser/download/download_item_model.cc
+++ b/chrome/browser/download/download_item_model.cc
@@ -870,8 +870,17 @@
 #if BUILDFLAG(FULL_SAFE_BROWSING)
       CompleteSafeBrowsingScan();
 #endif
-      LogDeepScanEvent(download_,
-                       safe_browsing::DeepScanEvent::kPromptBypassed);
+      if (download_->GetDangerType() ==
+              download::DOWNLOAD_DANGER_TYPE_ASYNC_LOCAL_PASSWORD_SCANNING ||
+          download_->GetDangerType() ==
+              download::
+                  DOWNLOAD_DANGER_TYPE_PROMPT_FOR_LOCAL_PASSWORD_SCANNING) {
+        safe_browsing::LogLocalDecryptionEvent(
+            safe_browsing::DeepScanEvent::kPromptBypassed);
+      } else {
+        LogDeepScanEvent(download_,
+                         safe_browsing::DeepScanEvent::kPromptBypassed);
+      }
       [[fallthrough]];
     case DownloadCommands::KEEP:
 #if BUILDFLAG(FULL_SAFE_BROWSING)
diff --git a/chrome/browser/enterprise/util/android/java/src/org/chromium/chrome/browser/enterprise/util/ManagedBrowserUtils.java b/chrome/browser/enterprise/util/android/java/src/org/chromium/chrome/browser/enterprise/util/ManagedBrowserUtils.java
index e8a6399..d6b1fc3 100644
--- a/chrome/browser/enterprise/util/android/java/src/org/chromium/chrome/browser/enterprise/util/ManagedBrowserUtils.java
+++ b/chrome/browser/enterprise/util/android/java/src/org/chromium/chrome/browser/enterprise/util/ManagedBrowserUtils.java
@@ -18,6 +18,11 @@
         return ManagedBrowserUtilsJni.get().isBrowserManaged(profile);
     }
 
+    /** Wrapper around native call to determine if the profile is managed. */
+    public static boolean isProfileManaged(Profile profile) {
+        return ManagedBrowserUtilsJni.get().isProfileManaged(profile);
+    }
+
     /** Wrapper around native call to get profile manager's representation string. */
     public static String getTitle(Profile profile) {
         return (profile != null) ? ManagedBrowserUtilsJni.get().getTitle(profile) : "";
@@ -32,6 +37,8 @@
     public interface Natives {
         boolean isBrowserManaged(@JniType("Profile*") Profile profile);
 
+        boolean isProfileManaged(@JniType("Profile*") Profile profile);
+
         String getTitle(@JniType("Profile*") Profile profile);
 
         boolean isReportingEnabled();
diff --git a/chrome/browser/enterprise/util/managed_browser_utils.cc b/chrome/browser/enterprise/util/managed_browser_utils.cc
index b9cc2649..f134c7d 100644
--- a/chrome/browser/enterprise/util/managed_browser_utils.cc
+++ b/chrome/browser/enterprise/util/managed_browser_utils.cc
@@ -661,7 +661,15 @@
 // static
 jboolean JNI_ManagedBrowserUtils_IsBrowserManaged(JNIEnv* env,
                                                   Profile* profile) {
-  return IsBrowserManaged(profile);
+  return policy::ManagementServiceFactory::GetForProfile(profile)
+      ->IsBrowserManaged();
+}
+
+// static
+jboolean JNI_ManagedBrowserUtils_IsProfileManaged(JNIEnv* env,
+                                                  Profile* profile) {
+  return policy::ManagementServiceFactory::GetForProfile(profile)
+      ->IsAccountManaged();
 }
 
 // static
diff --git a/chrome/browser/feed/android/java/res/values-sw600dp/dimens.xml b/chrome/browser/feed/android/java/res/values-sw600dp/dimens.xml
index 359eced..3069809 100644
--- a/chrome/browser/feed/android/java/res/values-sw600dp/dimens.xml
+++ b/chrome/browser/feed/android/java/res/values-sw600dp/dimens.xml
@@ -6,5 +6,5 @@
 -->
 
 <resources xmlns:tools="http://schemas.android.com/tools">
-    <dimen name="feed_containment_feed_header_top_margin">16dp</dimen>
+    <dimen name="feed_containment_feed_header_top_margin">8dp</dimen>
 </resources>
diff --git a/chrome/browser/feed/android/java/res/values/dimens.xml b/chrome/browser/feed/android/java/res/values/dimens.xml
index 541b00b..a76046f 100644
--- a/chrome/browser/feed/android/java/res/values/dimens.xml
+++ b/chrome/browser/feed/android/java/res/values/dimens.xml
@@ -50,13 +50,15 @@
     <dimen name="feed_header_tab_layout_width_max">165dp</dimen>
     <dimen name="feed_header_tab_layout_lateral_margin">8dp</dimen>
     <dimen name="feed_header_menu_width_polished">48dp</dimen>
-    <dimen name="feed_header_menu_end_margin">20dp</dimen>
+    <dimen name="feed_header_menu_start_margin">20dp</dimen>
+    <dimen name="feed_header_menu_end_margin">0dp</dimen>
     <dimen name="feed_header_top_margin">16dp</dimen>
     <dimen name="feed_header_title_view_margin_start">4dp</dimen>
 
     <!-- Feed containment. -->
     <dimen name="feed_containment_margin">16dp</dimen>
     <dimen name="feed_containment_feed_header_top_margin">8dp</dimen>
+    <dimen name="feed_containment_feed_header_menu_end_margin">4dp</dimen>
 
     <!-- Feed unread dot dimens -->
     <dimen name="feed_badge_radius">2.5dp</dimen>
diff --git a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/sections/SectionHeaderView.java b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/sections/SectionHeaderView.java
index ebb28d17..9512e6c8 100644
--- a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/sections/SectionHeaderView.java
+++ b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/sections/SectionHeaderView.java
@@ -215,9 +215,15 @@
 
         if (mIsSurfacePolishEnabled) {
             // Add 20dp padding at each sides for the SectionHeaderView.
-            int lateralPadding =
-                    getResources().getDimensionPixelSize(R.dimen.feed_header_menu_end_margin);
-            mContent.setPadding(lateralPadding, 0, 0, 0);
+            int startLateralPadding =
+                    getResources().getDimensionPixelSize(R.dimen.feed_header_menu_start_margin);
+            int endLateralPadding =
+                    getResources()
+                            .getDimensionPixelSize(
+                                    ChromeFeatureList.isEnabled(ChromeFeatureList.FEED_CONTAINMENT)
+                                            ? R.dimen.feed_containment_feed_header_menu_end_margin
+                                            : R.dimen.feed_header_menu_end_margin);
+            mContent.setPadding(startLateralPadding, 0, endLateralPadding, 0);
             MarginLayoutParams contentMarginLayoutParams =
                     (MarginLayoutParams) mContent.getLayoutParams();
             contentMarginLayoutParams.topMargin =
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 832a8ac..ea364cd 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -7457,11 +7457,6 @@
       "expiry_milestone": 130
   },
   {
-    "name": "request-desktop-site-window-setting",
-    "owners": [ "shuyng@google.com", "skavuluru@google.com", "clank-app-team@google.com" ],
-    "expiry_milestone": 130
-  },
-  {
     "name": "reset-shortcut-customizations",
     "owners": [ "jimmyxgong@chromium.org", "zentaro@chromium.org", "cros-peripherals@google.com"],
     "expiry_milestone": 130
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index c2f4591..ce99dfd3 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -4428,12 +4428,6 @@
 const char kRelatedSearchesAllLanguageDescription[] =
     "Enables requesting related searches suggestions for all the languages.";
 
-const char kRequestDesktopSiteWindowSettingName[] =
-    "Window setting for request desktop site on Android.";
-const char kRequestDesktopSiteWindowSettingDescription[] =
-    "Secondary option in `Site settings` to request the desktop version of "
-    "websites based on window width.";
-
 const char kRevokeNotificationsPermissionIfDisabledOnAppLevelName[] =
     "Revoke site-level notification permission on Android";
 const char kRevokeNotificationsPermissionIfDisabledOnAppLevelDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 019c377..d7ebc76 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -2628,9 +2628,6 @@
 extern const char kRelatedSearchesAllLanguageName[];
 extern const char kRelatedSearchesAllLanguageDescription[];
 
-extern const char kRequestDesktopSiteWindowSettingName[];
-extern const char kRequestDesktopSiteWindowSettingDescription[];
-
 extern const char kRevokeNotificationsPermissionIfDisabledOnAppLevelName[];
 extern const char
     kRevokeNotificationsPermissionIfDisabledOnAppLevelDescription[];
diff --git a/chrome/browser/gesturenav/android/java/src/org/chromium/chrome/browser/gesturenav/NativePageBitmapCapturer.java b/chrome/browser/gesturenav/android/java/src/org/chromium/chrome/browser/gesturenav/NativePageBitmapCapturer.java
index 8469456..57ec7082 100644
--- a/chrome/browser/gesturenav/android/java/src/org/chromium/chrome/browser/gesturenav/NativePageBitmapCapturer.java
+++ b/chrome/browser/gesturenav/android/java/src/org/chromium/chrome/browser/gesturenav/NativePageBitmapCapturer.java
@@ -14,6 +14,8 @@
 import org.chromium.base.UnownedUserData;
 import org.chromium.base.UnownedUserDataHost;
 import org.chromium.base.UnownedUserDataKey;
+import org.chromium.base.task.PostTask;
+import org.chromium.base.task.TaskTraits;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.ui.resources.dynamics.CaptureObserver;
@@ -65,7 +67,11 @@
                     @Override
                     public void onCaptureEnd() {}
                 },
-                callback);
+                (bitmap) -> {
+                    // The screenshot callback must be dispatched asynchronously. See
+                    // WebContentsDelegateAndroid#maybeCopyContentAreaAsBitmap.
+                    PostTask.postTask(TaskTraits.UI_USER_VISIBLE, () -> callback.onResult(bitmap));
+                });
         return true;
     }
 
diff --git a/chrome/browser/new_tab_page/modules/v2/most_relevant_tab_resumption/DEPS b/chrome/browser/new_tab_page/modules/v2/most_relevant_tab_resumption/DEPS
new file mode 100644
index 0000000..802323c
--- /dev/null
+++ b/chrome/browser/new_tab_page/modules/v2/most_relevant_tab_resumption/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  "+components/visited_url_ranking"
+]
diff --git a/chrome/browser/new_tab_page/modules/v2/most_relevant_tab_resumption/most_relevant_tab_resumption_page_handler.cc b/chrome/browser/new_tab_page/modules/v2/most_relevant_tab_resumption/most_relevant_tab_resumption_page_handler.cc
index a450af1..9ab47fbc 100644
--- a/chrome/browser/new_tab_page/modules/v2/most_relevant_tab_resumption/most_relevant_tab_resumption_page_handler.cc
+++ b/chrome/browser/new_tab_page/modules/v2/most_relevant_tab_resumption/most_relevant_tab_resumption_page_handler.cc
@@ -15,18 +15,28 @@
 #include "base/time/time.h"
 #include "chrome/browser/new_tab_page/modules/v2/most_relevant_tab_resumption/most_relevant_tab_resumption.mojom.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/visited_url_ranking/visited_url_ranking_service_factory.h"
 #include "chrome/grit/generated_resources.h"
 #include "components/history/core/browser/history_types.h"
 #include "components/history/core/browser/mojom/history_types.mojom.h"
 #include "components/search/ntp_features.h"
 #include "components/strings/grit/components_strings.h"
 #include "components/sync_device_info/device_info.h"
+#include "components/visited_url_ranking/public/fetch_options.h"
+#include "components/visited_url_ranking/public/url_visit.h"
+#include "components/visited_url_ranking/public/visited_url_ranking_service.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_ui.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/l10n/time_format.h"
 #include "url/gurl.h"
 
+using visited_url_ranking::Fetcher;
+using visited_url_ranking::FetchOptions;
+using visited_url_ranking::URLVisitAggregate;
+using visited_url_ranking::URLVisitAggregatesTransformType;
+using Source = visited_url_ranking::URLVisit::Source;
+
 namespace {
 std::u16string FormatRelativeTime(const base::Time& time) {
   // Return a time like "1 hour ago", "2 days ago", etc.
@@ -88,4 +98,36 @@
     std::move(callback).Run(std::move(tabs_mojom));
     return;
   }
+
+  auto fetch_options =
+      FetchOptions::CreateDefaultFetchOptionsForTabResumption();
+  // Filter certain content categories, generally for use cases where a device
+  // and profile may be shared by multiple family members.
+  fetch_options.transforms.insert(
+      fetch_options.transforms.begin(),
+      URLVisitAggregatesTransformType::kHistoryCategoriesFilter);
+  auto* visited_url_ranking_service =
+      visited_url_ranking::VisitedURLRankingServiceFactory::GetForProfile(
+          profile_);
+  // TODO (crbug.com/329243396): Wire call to `RankURLVisitAggregates`.
+  visited_url_ranking_service->FetchURLVisitAggregates(
+      fetch_options,
+      base::BindOnce(
+          &MostRelevantTabResumptionPageHandler::OnGotRankedURLVisitAggregates,
+          weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+}
+
+void MostRelevantTabResumptionPageHandler::OnGotRankedURLVisitAggregates(
+    GetTabsCallback callback,
+    visited_url_ranking::ResultStatus status,
+    std::vector<visited_url_ranking::URLVisitAggregate> url_visit_aggregates) {
+  std::vector<history::mojom::TabPtr> tabs_mojom;
+  for (const auto& url_visit_aggregate : url_visit_aggregates) {
+    auto tab_mojom = history::mojom::Tab::New();
+    // TODO(crbug.com/338622450): Wire fields to be displayed on the UI.
+    tab_mojom->url = **url_visit_aggregate.GetAssociatedURLs().begin();
+    tabs_mojom.push_back(std::move(tab_mojom));
+  }
+
+  std::move(callback).Run(std::move(tabs_mojom));
 }
diff --git a/chrome/browser/new_tab_page/modules/v2/most_relevant_tab_resumption/most_relevant_tab_resumption_page_handler.h b/chrome/browser/new_tab_page/modules/v2/most_relevant_tab_resumption/most_relevant_tab_resumption_page_handler.h
index f497a78..7c07322 100644
--- a/chrome/browser/new_tab_page/modules/v2/most_relevant_tab_resumption/most_relevant_tab_resumption_page_handler.h
+++ b/chrome/browser/new_tab_page/modules/v2/most_relevant_tab_resumption/most_relevant_tab_resumption_page_handler.h
@@ -5,8 +5,12 @@
 #ifndef CHROME_BROWSER_NEW_TAB_PAGE_MODULES_V2_MOST_RELEVANT_TAB_RESUMPTION_MOST_RELEVANT_TAB_RESUMPTION_PAGE_HANDLER_H_
 #define CHROME_BROWSER_NEW_TAB_PAGE_MODULES_V2_MOST_RELEVANT_TAB_RESUMPTION_MOST_RELEVANT_TAB_RESUMPTION_PAGE_HANDLER_H_
 
+#include <vector>
+
 #include "chrome/browser/new_tab_page/modules/v2/most_relevant_tab_resumption/most_relevant_tab_resumption.mojom.h"
 #include "components/history/core/browser/history_types.h"
+#include "components/visited_url_ranking/public/url_visit.h"
+#include "components/visited_url_ranking/public/visited_url_ranking_service.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/receiver.h"
 
@@ -36,6 +40,12 @@
   // most_relevant_tab_resumption::mojom::PageHandler:
   void GetTabs(GetTabsCallback callback) override;
 
+  // Invoked when the URL visit aggregates have been fetched.
+  void OnGotRankedURLVisitAggregates(
+      GetTabsCallback callback,
+      visited_url_ranking::ResultStatus status,
+      std::vector<visited_url_ranking::URLVisitAggregate> url_visit_aggregates);
+
  private:
   raw_ptr<Profile> profile_;
   raw_ptr<content::WebContents> web_contents_;
diff --git a/chrome/browser/new_tab_page/modules/v2/most_relevant_tab_resumption/most_relevant_tab_resumption_page_handler_unittest.cc b/chrome/browser/new_tab_page/modules/v2/most_relevant_tab_resumption/most_relevant_tab_resumption_page_handler_unittest.cc
index c0b5c46..d97ecaa 100644
--- a/chrome/browser/new_tab_page/modules/v2/most_relevant_tab_resumption/most_relevant_tab_resumption_page_handler_unittest.cc
+++ b/chrome/browser/new_tab_page/modules/v2/most_relevant_tab_resumption/most_relevant_tab_resumption_page_handler_unittest.cc
@@ -4,18 +4,48 @@
 
 #include "chrome/browser/new_tab_page/modules/v2/most_relevant_tab_resumption/most_relevant_tab_resumption_page_handler.h"
 
+#include <vector>
+
 #include "base/test/gmock_callback_support.h"
 #include "base/test/mock_callback.h"
+#include "chrome/browser/visited_url_ranking/visited_url_ranking_service_factory.h"
 #include "chrome/test/base/browser_with_test_window_test.h"
 #include "chrome/test/base/test_browser_window.h"
 #include "chrome/test/base/testing_profile.h"
 #include "components/history/core/browser/mojom/history_types.mojom.h"
 #include "components/search/ntp_features.h"
+#include "components/visited_url_ranking/public/testing/mock_visited_url_ranking_service.h"
+#include "components/visited_url_ranking/public/visited_url_ranking_service.h"
 #include "content/public/test/test_web_contents_factory.h"
+#include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+using visited_url_ranking::Fetcher;
+using visited_url_ranking::FetchOptions;
+using visited_url_ranking::ResultStatus;
+using visited_url_ranking::URLVisit;
+using visited_url_ranking::URLVisitAggregate;
+using visited_url_ranking::VisitedURLRankingService;
+using visited_url_ranking::VisitedURLRankingServiceFactory;
+
 namespace {
 
+inline constexpr char kSampleSearchUrl[] =
+    "https://www.google.com/search?q=sample";
+
+URLVisitAggregate CreateSampleURLVisitAggregate() {
+  URLVisitAggregate visit_aggregate = {};
+  visit_aggregate.fetcher_data_map.emplace(
+      Fetcher::kSession,
+      URLVisitAggregate::TabData(URLVisitAggregate::Tab(
+          1,
+          URLVisit(GURL(kSampleSearchUrl), u"sample_title", base::Time::Now(),
+                   syncer::DeviceInfo::FormFactor::kUnknown,
+                   URLVisit::Source::kLocal),
+          "sample_tag", "sample_session_name")));
+  return visit_aggregate;
+}
+
 class MostRelevantTabResumptionPageHandlerTest
     : public BrowserWithTestWindowTest {
  public:
@@ -32,16 +62,19 @@
         web_contents_.get());
   }
 
-  void SetUpMockCalls(
-      std::vector<history::mojom::TabPtr>& tabs_mojom,
-      base::MockCallback<MostRelevantTabResumptionPageHandler::GetTabsCallback>&
-          callback) {
-    EXPECT_CALL(callback, Run(testing::_))
-        .Times(1)
-        .WillOnce(testing::Invoke(
-            [&tabs_mojom](std::vector<history::mojom::TabPtr> tabs_arg) {
-              tabs_mojom = std::move(tabs_arg);
-            }));
+  std::vector<history::mojom::TabPtr> RunGetTabs() {
+    std::vector<history::mojom::TabPtr> tabs_mojom;
+    base::RunLoop wait_loop;
+    handler_->GetTabs(base::BindOnce(
+        [](base::OnceClosure stop_waiting,
+           std::vector<history::mojom::TabPtr>* tabs,
+           std::vector<history::mojom::TabPtr> tabs_arg) {
+          *tabs = std::move(tabs_arg);
+          std::move(stop_waiting).Run();
+        },
+        wait_loop.QuitClosure(), &tabs_mojom));
+    wait_loop.Run();
+    return tabs_mojom;
   }
 
   void TearDown() override {
@@ -50,13 +83,27 @@
     BrowserWithTestWindowTest::TearDown();
   }
 
-  MostRelevantTabResumptionPageHandler& handler() { return *handler_; }
-
  private:
+  // BrowserWithTestWindowTest:
+  TestingProfile::TestingFactories GetTestingFactories() override {
+    return {
+        {VisitedURLRankingServiceFactory::GetInstance(),
+         base::BindRepeating([](content::BrowserContext* context)
+                                 -> std::unique_ptr<KeyedService> {
+           return std::make_unique<
+               visited_url_ranking::MockVisitedURLRankingService>();
+         })},
+    };
+  }
+
   std::unique_ptr<content::WebContents> web_contents_;
   std::unique_ptr<MostRelevantTabResumptionPageHandler> handler_;
 };
 
+}  // namespace
+
+using testing::_;
+
 TEST_F(MostRelevantTabResumptionPageHandlerTest, GetFakeTabs) {
   base::test::ScopedFeatureList features;
   features.InitWithFeaturesAndParameters(
@@ -66,20 +113,34 @@
              "Fake Data"}}},
       },
       {});
-  std::vector<history::mojom::TabPtr> tabs_mojom;
-  base::MockCallback<MostRelevantTabResumptionPageHandler::GetTabsCallback>
-      callback;
 
-  SetUpMockCalls(tabs_mojom, callback);
-
-  handler().GetTabs(callback.Get());
-
+  auto tabs_mojom = RunGetTabs();
   ASSERT_EQ(3u, tabs_mojom.size());
-
   for (const auto& tab_mojom : tabs_mojom) {
     ASSERT_EQ("Test Session", tab_mojom->session_name);
     ASSERT_EQ("5 mins ago", tab_mojom->relative_time_text);
     ASSERT_EQ(GURL("https://www.google.com"), tab_mojom->url);
   }
 }
-}  // namespace
+
+TEST_F(MostRelevantTabResumptionPageHandlerTest, GetTabs) {
+  visited_url_ranking::MockVisitedURLRankingService*
+      mock_visited_url_ranking_service =
+          static_cast<visited_url_ranking::MockVisitedURLRankingService*>(
+              VisitedURLRankingServiceFactory::GetForProfile(profile()));
+
+  EXPECT_CALL(*mock_visited_url_ranking_service, FetchURLVisitAggregates(_, _))
+      .Times(1)
+      .WillOnce(testing::Invoke(
+          [](const FetchOptions& options,
+             VisitedURLRankingService::GetURLVisitAggregatesCallback callback) {
+            std::vector<URLVisitAggregate> url_visit_aggregates = {};
+            url_visit_aggregates.emplace_back(CreateSampleURLVisitAggregate());
+
+            std::move(callback).Run(ResultStatus::kSuccess,
+                                    std::move(url_visit_aggregates));
+          }));
+
+  auto tabs_mojom = RunGetTabs();
+  ASSERT_EQ(1u, tabs_mojom.size());
+}
diff --git a/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc b/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
index df8ef45..4c1c86c 100644
--- a/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
+++ b/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
@@ -209,6 +209,7 @@
 #include "chrome/browser/unified_consent/unified_consent_service_factory.h"
 #include "chrome/browser/updates/announcement_notification/announcement_notification_service_factory.h"
 #include "chrome/browser/usb/usb_chooser_context_factory.h"
+#include "chrome/browser/visited_url_ranking/visited_url_ranking_service_factory.h"
 #include "chrome/browser/web_applications/isolated_web_apps/isolated_web_app_reader_registry_factory.h"
 #include "chrome/browser/webauthn/enclave_manager_factory.h"
 #include "chrome/browser/webdata_services/web_data_service_factory.h"
@@ -264,7 +265,6 @@
 #include "chrome/browser/media/android/cdm/media_drm_origin_id_manager_factory.h"
 #include "chrome/browser/search_resumption/start_suggest_service_factory.h"
 #include "chrome/browser/signin/signin_manager_android_factory.h"
-#include "chrome/browser/visited_url_ranking/visited_url_ranking_service_factory.h"
 #include "components/commerce/core/commerce_feature_list.h"
 #include "components/commerce/core/proto/merchant_signal_db_content.pb.h"
 #else
@@ -869,6 +869,7 @@
   HttpsFirstModeServiceFactory::GetInstance();
   IdentityManagerFactory::EnsureFactoryAndDependeeFactoriesBuilt();
   InMemoryURLIndexFactory::GetInstance();
+  visited_url_ranking::VisitedURLRankingServiceFactory::GetInstance();
 #if !BUILDFLAG(IS_ANDROID)
   InstantServiceFactory::GetInstance();
 #endif
@@ -1267,11 +1268,6 @@
   user_notes::UserNoteServiceFactory::EnsureFactoryBuilt();
   UserEducationServiceFactory::GetInstance();
 #endif
-#if BUILDFLAG(IS_ANDROID)
-  // TODO(crbug.com/340893655) Fix dangling pointer issue (on Mac) and enable
-  // for non-Android architectures.
-  visited_url_ranking::VisitedURLRankingServiceFactory::GetInstance();
-#endif
 #if BUILDFLAG(ENABLE_EXTENSIONS)
   web_app::IsolatedWebAppReaderRegistryFactory::GetInstance();
   web_app::WebAppMetricsFactory::GetInstance();
diff --git a/chrome/browser/push_notification/prefs/push_notification_prefs.cc b/chrome/browser/push_notification/prefs/push_notification_prefs.cc
index 99b69fa..e466fd34 100644
--- a/chrome/browser/push_notification/prefs/push_notification_prefs.cc
+++ b/chrome/browser/push_notification/prefs/push_notification_prefs.cc
@@ -13,14 +13,17 @@
 
 const char kPushNotificationRegistrationAttemptBackoffSchedulerPrefName[] =
     "push_notification.scheduler.registration_attempt_backoff";
+const char kPushNotificationRepresentativeTargetIdPrefName[] =
+    "push_notification.representative_target_id";
 
 }  // namespace prefs
 
-// TODO(b/306399332): Add pref for `representative_target_id` to be used when
-// registering with Chime.
 void RegisterPushNotificationPrefs(PrefRegistrySimple* registry) {
   registry->RegisterDictionaryPref(
       prefs::kPushNotificationRegistrationAttemptBackoffSchedulerPrefName);
+  registry->RegisterStringPref(
+      prefs::kPushNotificationRepresentativeTargetIdPrefName,
+      /*default_value=*/std::string());
 }
 
 }  // namespace push_notification
diff --git a/chrome/browser/push_notification/prefs/push_notification_prefs.h b/chrome/browser/push_notification/prefs/push_notification_prefs.h
index f7de5ab..b23fb0eb 100644
--- a/chrome/browser/push_notification/prefs/push_notification_prefs.h
+++ b/chrome/browser/push_notification/prefs/push_notification_prefs.h
@@ -13,6 +13,7 @@
 
 extern const char
     kPushNotificationRegistrationAttemptBackoffSchedulerPrefName[];
+extern const char kPushNotificationRepresentativeTargetIdPrefName[];
 
 }  // namespace prefs
 
diff --git a/chrome/browser/push_notification/push_notification_service_desktop_impl.cc b/chrome/browser/push_notification/push_notification_service_desktop_impl.cc
index 0a48ac9..9a2198e 100644
--- a/chrome/browser/push_notification/push_notification_service_desktop_impl.cc
+++ b/chrome/browser/push_notification/push_notification_service_desktop_impl.cc
@@ -67,7 +67,9 @@
 }
 
 void PushNotificationServiceDesktopImpl::OnStoreReset() {
-  // TODO(b/337874846): Clear prefs here.
+  // Reset prefs.
+  pref_service_->SetString(
+      prefs::kPushNotificationRepresentativeTargetIdPrefName, std::string());
 }
 
 void PushNotificationServiceDesktopImpl::OnMessage(
@@ -154,6 +156,9 @@
       ->gcm_driver()
       ->AddAppHandler(kPushNotificationAppId, this);
 
+  std::string representative_target_id = pref_service_->GetString(
+      prefs::kPushNotificationRepresentativeTargetIdPrefName);
+
   // Create the `NotificationsMultiLoginUpdateRequest` proto which is used to
   // make the registration API call.
   push_notification::proto::NotificationsMultiLoginUpdateRequest request_proto;
@@ -167,6 +172,16 @@
       ->mutable_delivery_address()
       ->mutable_gcm_device_address()
       ->set_application_id(kPushNotificationAppId);
+
+  // `representative_target_id` is left empty the first time we register with
+  // the Push Notification Service. It is then returned to us in the response
+  // proto and stored in prefs. When we have a stored representative target id,
+  // we use it to help the Push Notification Service stablize the target across
+  // registrations if the GCM registration token changes.
+  if (!representative_target_id.empty()) {
+    request_proto.mutable_target()->set_representative_target_id(
+        representative_target_id);
+  }
   request_proto.add_registrations();
   request_proto.set_registration_reason(
       push_notification::proto::RegistrationReason::COLLABORATOR_API_CALL);
@@ -194,9 +209,10 @@
   is_initialized_ = true;
   server_client_.reset();
   initialization_on_demand_scheduler_->HandleResult(/*success=*/true);
-
-  // TODO(b/321305351): Use response proto to update prefs with response
-  // information for later calls.
+  CHECK(response.registration_results_size() == 1);
+  pref_service_->SetString(
+      prefs::kPushNotificationRepresentativeTargetIdPrefName,
+      response.registration_results(0).target().representative_target_id());
 }
 
 void PushNotificationServiceDesktopImpl::OnPushNotificationRegistrationFailure(
diff --git a/chrome/browser/push_notification/push_notification_service_desktop_impl_unittest.cc b/chrome/browser/push_notification/push_notification_service_desktop_impl_unittest.cc
index 27ad379..4cb5b9e5 100644
--- a/chrome/browser/push_notification/push_notification_service_desktop_impl_unittest.cc
+++ b/chrome/browser/push_notification/push_notification_service_desktop_impl_unittest.cc
@@ -28,6 +28,7 @@
 const char kSenderIdFCMToken[] = "sharing_fcm_token";
 const char kSharingSenderID[] = "745476177629";
 const char kTestMessage[] = "This is a test message";
+const char kTestRepresentativeTargetId[] = "0123456789";
 
 class FakeInstanceID : public instance_id::InstanceID {
  public:
@@ -123,6 +124,7 @@
 
   // testing::Test:
   void SetUp() override {
+    RegisterPushNotificationPrefs(pref_service_.registry());
     ash::nearby::NearbySchedulerFactory::SetFactoryForTesting(
         &scheduler_factory_);
     PushNotificationServerClientDesktopImpl::Factory::SetFactoryForTesting(
@@ -159,6 +161,9 @@
         registration_result->mutable_status();
     status->set_code(0);
     status->set_message("OK");
+    push_notification::proto::Target* target =
+        registration_result->mutable_target();
+    target->set_representative_target_id(kTestRepresentativeTargetId);
     return response_proto;
   }
 
@@ -170,6 +175,9 @@
         ->InvokeRegisterWithPushNotificationServiceSuccessCallback(
             CreateResponseProto());
     EXPECT_TRUE(push_notification_service_->IsServiceInitialized());
+    EXPECT_EQ(kTestRepresentativeTargetId,
+              pref_service_.GetString(
+                  prefs::kPushNotificationRepresentativeTargetIdPrefName));
   }
 
   void CheckForFailedRegistration(
@@ -200,7 +208,6 @@
 TEST_F(PushNotificationServiceDesktopImplTest, StartService) {
   fake_instance_id_->SetFCMResult(instance_id::InstanceID::Result::SUCCESS);
   fake_instance_id_->SetFCMToken(kSenderIdFCMToken);
-
   ash::nearby::FakeNearbyScheduler* registration_scheduler =
       scheduler_factory_.pref_name_to_on_demand_instance()
           .find(
@@ -208,10 +215,59 @@
                   kPushNotificationRegistrationAttemptBackoffSchedulerPrefName)
           ->second.fake_scheduler;
   registration_scheduler->InvokeRequestCallback();
-
+  EXPECT_EQ(std::string(), fake_client_factory_.fake_server_client()
+                               ->GetRequestProto()
+                               .target()
+                               .representative_target_id());
   CheckForSuccessfulRegistration();
 }
 
+TEST_F(PushNotificationServiceDesktopImplTest, StartServiceWithPref) {
+  fake_instance_id_->SetFCMResult(instance_id::InstanceID::Result::SUCCESS);
+  fake_instance_id_->SetFCMToken(kSenderIdFCMToken);
+  pref_service_.SetString(
+      prefs::kPushNotificationRepresentativeTargetIdPrefName,
+      kTestRepresentativeTargetId);
+  ash::nearby::FakeNearbyScheduler* registration_scheduler =
+      scheduler_factory_.pref_name_to_on_demand_instance()
+          .find(
+              prefs::
+                  kPushNotificationRegistrationAttemptBackoffSchedulerPrefName)
+          ->second.fake_scheduler;
+  registration_scheduler->InvokeRequestCallback();
+  EXPECT_EQ(kTestRepresentativeTargetId,
+            fake_client_factory_.fake_server_client()
+                ->GetRequestProto()
+                .target()
+                .representative_target_id());
+  CheckForSuccessfulRegistration();
+}
+
+TEST_F(PushNotificationServiceDesktopImplTest, StartServiceWithPrefStoreReset) {
+  fake_instance_id_->SetFCMResult(instance_id::InstanceID::Result::SUCCESS);
+  fake_instance_id_->SetFCMToken(kSenderIdFCMToken);
+  pref_service_.SetString(
+      prefs::kPushNotificationRepresentativeTargetIdPrefName,
+      kTestRepresentativeTargetId);
+  ash::nearby::FakeNearbyScheduler* registration_scheduler =
+      scheduler_factory_.pref_name_to_on_demand_instance()
+          .find(
+              prefs::
+                  kPushNotificationRegistrationAttemptBackoffSchedulerPrefName)
+          ->second.fake_scheduler;
+  registration_scheduler->InvokeRequestCallback();
+  EXPECT_EQ(kTestRepresentativeTargetId,
+            fake_client_factory_.fake_server_client()
+                ->GetRequestProto()
+                .target()
+                .representative_target_id());
+  CheckForSuccessfulRegistration();
+  push_notification_service_->OnStoreReset();
+  EXPECT_EQ(std::string(),
+            pref_service_.GetString(
+                prefs::kPushNotificationRepresentativeTargetIdPrefName));
+}
+
 TEST_F(PushNotificationServiceDesktopImplTest, StartServiceTokenFailure) {
   fake_instance_id_->SetFCMResult(
       instance_id::InstanceID::Result::SERVER_ERROR);
diff --git a/chrome/browser/push_notification/server_client/fake_push_notification_server_client.cc b/chrome/browser/push_notification/server_client/fake_push_notification_server_client.cc
index abfd4ba..4cb7bdd 100644
--- a/chrome/browser/push_notification/server_client/fake_push_notification_server_client.cc
+++ b/chrome/browser/push_notification/server_client/fake_push_notification_server_client.cc
@@ -48,11 +48,17 @@
     const proto::NotificationsMultiLoginUpdateRequest& request,
     RegisterWithPushNotificationServiceCallback&& callback,
     ErrorCallback&& error_callback) {
+  request_ = request;
   register_with_push_notification_service_callback_ = std::move(callback);
   register_with_push_notification_service_error_callback_ =
       std::move(error_callback);
 }
 
+const proto::NotificationsMultiLoginUpdateRequest&
+FakePushNotificationServerClient::GetRequestProto() {
+  return request_;
+}
+
 std::optional<std::string>
 FakePushNotificationServerClient::GetAccessTokenUsed() {
   return access_token_used_;
diff --git a/chrome/browser/push_notification/server_client/fake_push_notification_server_client.h b/chrome/browser/push_notification/server_client/fake_push_notification_server_client.h
index c3bd83c6..92d20e0 100644
--- a/chrome/browser/push_notification/server_client/fake_push_notification_server_client.h
+++ b/chrome/browser/push_notification/server_client/fake_push_notification_server_client.h
@@ -57,6 +57,8 @@
       PushNotificationDesktopApiCallFlow::PushNotificationApiCallFlowError
           error);
 
+  const proto::NotificationsMultiLoginUpdateRequest& GetRequestProto();
+
   bool HasRegisterWithPushNotificationServiceCallback() {
     return !register_with_push_notification_service_callback_.is_null();
   }
@@ -73,6 +75,7 @@
   std::optional<std::string> GetAccessTokenUsed() override;
 
   std::string access_token_used_;
+  proto::NotificationsMultiLoginUpdateRequest request_;
   RegisterWithPushNotificationServiceCallback
       register_with_push_notification_service_callback_;
   ErrorCallback register_with_push_notification_service_error_callback_;
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/facegaze/facegaze_test.js b/chrome/browser/resources/chromeos/accessibility/accessibility_common/facegaze/facegaze_test.js
index 726f58b..8d33c2f0 100644
--- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/facegaze/facegaze_test.js
+++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/facegaze/facegaze_test.js
@@ -662,13 +662,18 @@
           .set(FacialGesture.EYE_SQUINT_RIGHT, MacroName.KEY_PRESS_UP)
           .set(FacialGesture.MOUTH_SMILE, MacroName.KEY_PRESS_DOWN)
           .set(FacialGesture.MOUTH_UPPER_UP, MacroName.KEY_PRESS_LEFT)
-          .set(FacialGesture.EYES_BLINK, MacroName.KEY_PRESS_RIGHT);
+          .set(FacialGesture.EYES_BLINK, MacroName.KEY_PRESS_RIGHT)
+          .set(FacialGesture.JAW_OPEN, MacroName.KEY_PRESS_TOGGLE_OVERVIEW)
+          .set(
+              FacialGesture.MOUTH_PUCKER, MacroName.KEY_PRESS_MEDIA_PLAY_PAUSE);
   const gestureToConfidence = new Map()
                                   .set(FacialGesture.EYE_SQUINT_LEFT, 0.7)
                                   .set(FacialGesture.EYE_SQUINT_RIGHT, 0.7)
                                   .set(FacialGesture.MOUTH_SMILE, 0.7)
                                   .set(FacialGesture.MOUTH_UPPER_UP, 0.7)
-                                  .set(FacialGesture.EYES_BLINK, 0.7);
+                                  .set(FacialGesture.EYES_BLINK, 0.7)
+                                  .set(FacialGesture.JAW_OPEN, 0.7)
+                                  .set(FacialGesture.MOUTH_PUCKER, 0.7);
   const config = new Config()
                      .withMouseLocation({x: 600, y: 400})
                      .withGestureToMacroName(gestureToMacroName)
@@ -701,7 +706,13 @@
                            gestures.blinkLeft ? gestures.blinkLeft : 0.3)
                        .addGestureWithConfidence(
                            MediapipeFacialGesture.EYE_BLINK_RIGHT,
-                           gestures.blinkRight ? gestures.blinkRight : 0.3);
+                           gestures.blinkRight ? gestures.blinkRight : 0.3)
+                       .addGestureWithConfidence(
+                           MediapipeFacialGesture.JAW_OPEN,
+                           gestures.jawOpen ? gestures.jawOpen : 0.3)
+                       .addGestureWithConfidence(
+                           MediapipeFacialGesture.MOUTH_PUCKER,
+                           gestures.mouthPucker ? gestures.mouthPucker : 0.3);
     this.processFaceLandmarkerResult(
         result, /*triggerMouseControllerInterval=*/ true);
     return this.mockAccessibilityPrivate.syntheticKeyEvents_;
@@ -792,4 +803,36 @@
       chrome.accessibilityPrivate.SyntheticKeyboardEventType.KEYUP,
       keyEvents[9].type);
   assertEquals(KeyCode.LEFT, keyEvents[9].keyCode);
+
+  // Jaw open for toggle overview key press.
+  keyEvents = makeResultAndProcess({jawOpen: .75});
+  assertEquals(11, keyEvents.length);
+  assertEquals(
+      chrome.accessibilityPrivate.SyntheticKeyboardEventType.KEYDOWN,
+      keyEvents[10].type);
+  assertEquals(KeyCode.MEDIA_LAUNCH_APP1, keyEvents[10].keyCode);
+
+  // Jaw close for toggle overview key release.
+  keyEvents = makeResultAndProcess({});
+  assertEquals(12, keyEvents.length);
+  assertEquals(
+      chrome.accessibilityPrivate.SyntheticKeyboardEventType.KEYUP,
+      keyEvents[11].type);
+  assertEquals(KeyCode.MEDIA_LAUNCH_APP1, keyEvents[11].keyCode);
+
+  // Mouth pucker for media play/pause key press.
+  keyEvents = makeResultAndProcess({mouthPucker: .75});
+  assertEquals(13, keyEvents.length);
+  assertEquals(
+      chrome.accessibilityPrivate.SyntheticKeyboardEventType.KEYDOWN,
+      keyEvents[12].type);
+  assertEquals(KeyCode.MEDIA_PLAY_PAUSE, keyEvents[12].keyCode);
+
+  // Stop mouth pucker for media play/pause key release.
+  keyEvents = makeResultAndProcess({});
+  assertEquals(14, keyEvents.length);
+  assertEquals(
+      chrome.accessibilityPrivate.SyntheticKeyboardEventType.KEYUP,
+      keyEvents[13].type);
+  assertEquals(KeyCode.MEDIA_PLAY_PAUSE, keyEvents[13].keyCode);
 });
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/facegaze/gesture_handler.ts b/chrome/browser/resources/chromeos/accessibility/accessibility_common/facegaze/gesture_handler.ts
index 484f82d..3a20366 100644
--- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/facegaze/gesture_handler.ts
+++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/facegaze/gesture_handler.ts
@@ -176,6 +176,8 @@
       case MacroName.KEY_PRESS_LEFT:
       case MacroName.KEY_PRESS_RIGHT:
       case MacroName.KEY_PRESS_UP:
+      case MacroName.KEY_PRESS_TOGGLE_OVERVIEW:
+      case MacroName.KEY_PRESS_MEDIA_PLAY_PAUSE:
         return new KeyPressMacro(name);
       default:
         return;
diff --git a/chrome/browser/resources/chromeos/accessibility/common/action_fulfillment/macros/key_press_macro.ts b/chrome/browser/resources/chromeos/accessibility/common/action_fulfillment/macros/key_press_macro.ts
index 8b52cd2..010830e 100644
--- a/chrome/browser/resources/chromeos/accessibility/common/action_fulfillment/macros/key_press_macro.ts
+++ b/chrome/browser/resources/chromeos/accessibility/common/action_fulfillment/macros/key_press_macro.ts
@@ -36,6 +36,14 @@
       case MacroName.KEY_PRESS_UP:
         this.key_ = KeyCode.UP;
         break;
+      case MacroName.KEY_PRESS_TOGGLE_OVERVIEW:
+        // The MEDIA_LAUNCH_APP1 key is bound to the kToggleOverview accelerator
+        // action in accelerators.cc.
+        this.key_ = KeyCode.MEDIA_LAUNCH_APP1;
+        break;
+      case MacroName.KEY_PRESS_MEDIA_PLAY_PAUSE:
+        this.key_ = KeyCode.MEDIA_PLAY_PAUSE;
+        break;
       default:
         console.error('Macro ' + macroName + ' is not a key press macro.');
     }
diff --git a/chrome/browser/resources/chromeos/login/BUILD.gn b/chrome/browser/resources/chromeos/login/BUILD.gn
index ef8b647..8366709f 100644
--- a/chrome/browser/resources/chromeos/login/BUILD.gn
+++ b/chrome/browser/resources/chromeos/login/BUILD.gn
@@ -146,7 +146,6 @@
     "screens/osauth/cryptohome_recovery_setup.ts",
     "screens/osauth/factor_setup_success.ts",
     "screens/osauth/fingerprint_setup.ts",
-    "screens/osauth/gaia_password_changed.ts",
     "screens/osauth/local_password_setup.ts",
     "screens/osauth/local_data_loss_warning.ts",
     "screens/osauth/enter_old_password.ts",
diff --git a/chrome/browser/resources/chromeos/login/components/oobe_categories_list.html b/chrome/browser/resources/chromeos/login/components/oobe_categories_list.html
index dfcdb19..6c50925 100644
--- a/chrome/browser/resources/chromeos/login/components/oobe_categories_list.html
+++ b/chrome/browser/resources/chromeos/login/components/oobe_categories_list.html
@@ -130,7 +130,7 @@
         <div class="selected-icon">
           <iron-icon icon="oobe-40:category-selected"></iron-icon>
         </div>
-        <div class="text-container" aria-hidden="true">
+        <div class="text-container">
           <div class="category-title"> [[item.title]]</div>
           <div class="category-subtitle">[[item.subtitle]]</div>
         </div>
diff --git a/chrome/browser/resources/chromeos/login/components/oobe_personalized_apps_list.html b/chrome/browser/resources/chromeos/login/components/oobe_personalized_apps_list.html
index e32c385..d0693a2 100644
--- a/chrome/browser/resources/chromeos/login/components/oobe_personalized_apps_list.html
+++ b/chrome/browser/resources/chromeos/login/components/oobe_personalized_apps_list.html
@@ -94,7 +94,11 @@
     <template is="dom-repeat"
         items="{{getCategories(catgoriesMapApps)}}" as="category">
       <div class="category-title"
-          hidden="[[shouldShowCategoriesTitle()]]" >{{category}}</div>
+          tabindex="0"
+          role="heading" aria-level="2"
+          hidden="[[shouldShowCategoriesTitle(catgoriesMapApps)]]">
+        {{category}}
+      </div>
       <div id="appsList">
           <template is="dom-repeat" rendered-item-count="{{itemRendered}}"
                 items="{{getApps(category)}}">
@@ -120,6 +124,8 @@
                   <div class="app-subtitle">[[item.subname]]</div>
                 </div>
                   <cr-checkbox checked="{{item.selected}}"
+                      aria-label$="[[item.name]]"
+                      aria-description$="[[item.subname]]"
                       on-change="updateCount"></cr-checkbox>
               </div>
           </div>
diff --git a/chrome/browser/resources/chromeos/login/components/oobe_personalized_apps_list.ts b/chrome/browser/resources/chromeos/login/components/oobe_personalized_apps_list.ts
index d734bb3..085f5585 100644
--- a/chrome/browser/resources/chromeos/login/components/oobe_personalized_apps_list.ts
+++ b/chrome/browser/resources/chromeos/login/components/oobe_personalized_apps_list.ts
@@ -115,8 +115,9 @@
   /**
    * Return if categories title should be shown.
    */
-  private shouldShowCategoriesTitle(): boolean {
-    return !(Object.keys(this.catgoriesMapApps).length > 1);
+  private shouldShowCategoriesTitle(catgoriesMapApps: CategoriesAppsMap):
+      boolean {
+    return !(Object.keys(catgoriesMapApps).length > 1);
   }
 
   /**
diff --git a/chrome/browser/resources/chromeos/login/debug/debug.js b/chrome/browser/resources/chromeos/login/debug/debug.js
index bc1d5a4..f2f5e02 100644
--- a/chrome/browser/resources/chromeos/login/debug/debug.js
+++ b/chrome/browser/resources/chromeos/login/debug/debug.js
@@ -843,36 +843,6 @@
       ],
     },
     {
-      // GAIA password changed.
-      id: 'gaia-password-changed',
-      kind: ScreenKind.OTHER,
-      handledSteps: 'password',
-      data: {
-        email: 'someone@example.com',
-      },
-      states: [
-        {
-          // No error
-          id: 'no-error',
-          trigger: (screen) => {
-            screen.onBeforeShow({
-              email: 'someone@example.com',
-            });
-          },
-        },
-        {
-          // Has error
-          id: 'has-error',
-          trigger: (screen) => {
-            screen.onBeforeShow({
-              email: 'someone@example.com',
-              showError: true,
-            });
-          },
-        },
-      ],
-    },
-    {
       id: 'ad-password-change',
       kind: ScreenKind.OTHER,
       handledSteps: 'password',
diff --git a/chrome/browser/resources/chromeos/login/screens.ts b/chrome/browser/resources/chromeos/login/screens.ts
index ebd1b37..e47a4ca 100644
--- a/chrome/browser/resources/chromeos/login/screens.ts
+++ b/chrome/browser/resources/chromeos/login/screens.ts
@@ -64,7 +64,6 @@
 import './screens/osauth/pin_setup.js';
 // AUTHENTICATION SCREENS USED DURING THE LOGIN FLOW
 import './screens/osauth/cryptohome_recovery.js';
-import './screens/osauth/gaia_password_changed.js';
 // SCREENS USED DURING THE LOGIN FLOW
 import './screens/login/arc_vm_data_migration.js';
 import './screens/login/encryption_migration.js';
@@ -232,7 +231,6 @@
   },
   {tag: 'cryptohome-recovery-element', id: 'cryptohome-recovery'},
   {tag: 'encryption-migration-element', id: 'encryption-migration'},
-  {tag: 'gaia-password-changed-element', id: 'gaia-password-changed'},
   {
     tag: 'lacros-data-backward-migration-element',
     id: 'lacros-data-backward-migration',
diff --git a/chrome/browser/resources/chromeos/login/screens/common/categories_selection.html b/chrome/browser/resources/chromeos/login/screens/common/categories_selection.html
index 9a7c5ae..484fe8d3 100644
--- a/chrome/browser/resources/chromeos/login/screens/common/categories_selection.html
+++ b/chrome/browser/resources/chromeos/login/screens/common/categories_selection.html
@@ -11,8 +11,11 @@
         aria-label$="[[i18nDynamic(locale, 'categoriesLoading')]]">
     <iron-icon slot="icon" icon="oobe-32:sync-chrome"></iron-icon>
 </oobe-loading-dialog>
-<oobe-adaptive-dialog id="categoriesDialog" role="presentation"
-      for-step="overview">
+<oobe-adaptive-dialog id="categoriesDialog" role="dialog"
+      for-step="overview"
+      aria-label$="[[i18nDynamic(locale, 'categoriesScreenTitle')]]"
+      aria-description$="[[i18nDynamic(locale,
+          'categoriesScreenDescription')]]">
     <iron-icon slot="icon" icon="oobe-32:sync-chrome"></iron-icon>
     <h1 slot="title" id="categories-title" aria-live="polite">
       [[i18nDynamic(locale, 'categoriesScreenTitle')]]
diff --git a/chrome/browser/resources/chromeos/login/screens/common/personalized_recommend_apps.html b/chrome/browser/resources/chromeos/login/screens/common/personalized_recommend_apps.html
index 3695dc0..fb3cdd8 100644
--- a/chrome/browser/resources/chromeos/login/screens/common/personalized_recommend_apps.html
+++ b/chrome/browser/resources/chromeos/login/screens/common/personalized_recommend_apps.html
@@ -10,7 +10,11 @@
         aria-label$="[[i18nDynamic(locale, 'personalizedRecommendedLoading')]]">
     <iron-icon slot="icon" icon="oobe-32:perosonalized-apps"></iron-icon>
 </oobe-loading-dialog>
-<oobe-adaptive-dialog id="personalizedRecommendDialog" role="presentation"
+<oobe-adaptive-dialog id="personalizedRecommendDialog" role="dialog"
+      aria-label$="[[i18nDynamic(locale,
+          'personalizedRecommendedAppsScreenTitle')]]"
+      aria-description$="[[i18nDynamic(locale,
+          'personalizedRecommendedAppsScreenDescription')]]"
       for-step="overview">
     <iron-icon slot="icon" icon="oobe-32:perosonalized-apps"></iron-icon>
     <h1 slot="title" id="recommend-title" aria-live="polite">
diff --git a/chrome/browser/resources/chromeos/login/screens/common/quick_start.ts b/chrome/browser/resources/chromeos/login/screens/common/quick_start.ts
index d57bb88..40d0c83 100644
--- a/chrome/browser/resources/chromeos/login/screens/common/quick_start.ts
+++ b/chrome/browser/resources/chromeos/login/screens/common/quick_start.ts
@@ -227,13 +227,15 @@
   }
 
   // eslint-disable-next-line @typescript-eslint/naming-convention
-  setQRCode(qrCode: boolean[]): void {
+  setQRCode(qrCodeData: boolean[], qrCodeURL: string): void {
     this.getQuickStartBluetoothDialog().hideDialog();
     this.usePinInsteadOfQrForVerification = false;
     this.setUIStep(QuickStartUiState.VERIFICATION);
     flush();
 
-    this.qrCodeCanvas?.setData(qrCode);
+    this.qrCodeCanvas?.setData(qrCodeData);
+    this.shadowRoot?.querySelector('#qrCodeCanvas')
+        ?.setAttribute('qr-code-url', qrCodeURL);
     this.qrCodeAvailable = true;
   }
 
diff --git a/chrome/browser/resources/chromeos/login/screens/osauth/gaia_password_changed.html b/chrome/browser/resources/chromeos/login/screens/osauth/gaia_password_changed.html
deleted file mode 100644
index 42affb6..0000000
--- a/chrome/browser/resources/chromeos/login/screens/osauth/gaia_password_changed.html
+++ /dev/null
@@ -1,120 +0,0 @@
-<!--
-Copyright 2015 The Chromium Authors
-Use of this source code is governed by a BSD-style license that can be
-found in the LICENSE file.
--->
-<!--
-  Password changed UI for the Gaia flow.
-  Contains cards with a transition between them:
-    1. Old password input form
-    2. Warning about data loss
-    3. Spinner with notice "Please wait"
-
-  Example:
-    <gaia-password-changed-element id="gaia-password-changed" hidden>
-    </gaia-password-changed-element>
-
-  Methods:
-    'reset'      - reset element, sets in on the first screen and enables
-                   buttons.
--->
-<style include="oobe-dialog-host-styles cros-color-overrides">
-  #oldPasswordInput {
-    padding-top: 16px;
-  }
-
-  @media (orientation: portrait) {
-    #oldPasswordInput {
-      padding-top: 24px;
-      text-align: initial;
-      width: calc(var(--oobe-adaptive-dialog-width) - 48px
-          - 2 * var(--oobe-adaptive-dialog-content-padding));
-    }
-  }
-
-  :host-context(.jelly-enabled)
-    cr-input#oldPasswordInput {
-    --cr-input-background-color: var(--cros-sys-input_field_on_shaded);
-  }
-</style>
-
-<oobe-adaptive-dialog role="dialog" for-step="password" id="passwordStep"
-    single-column>
-  <iron-icon slot="icon" icon="oobe-32:lock"></iron-icon>
-  <h1 slot="title">
-    [[i18nDynamic(locale,'recoverLocalDataTitle')]]
-  </h1>
-  <div slot="subtitle">
-    [[i18nDynamic(locale, 'recoverLocalDataSubtitle')]]
-  </div>
-  <cr-input slot="subtitle" type="password" id="oldPasswordInput"
-      value="{{password}}" invalid="{{passwordInvalid}}"
-      required class="focus-on-show"
-      placeholder="[[i18nDynamic(locale, 'oldPasswordHint')]]"
-      error-message="[[i18nDynamic(locale, 'oldPasswordIncorrect')]]">
-  </cr-input>
-  <div slot="bottom-buttons">
-    <oobe-text-button id="forgotPasswordButton"
-        on-click="onForgotPasswordClicked"
-        text-key="forgotOldPasswordButton">
-    </oobe-text-button>
-    <oobe-next-button id="next" on-click="submit" inverse>
-    </oobe-next-button>
-  </div>
-</oobe-adaptive-dialog>
-
-<oobe-adaptive-dialog role="dialog" for-step="forgot" id="forgotPassword"
-    aria-label$="[[i18nDynamic(locale,'dataLossWarningTitle')]]">
-  <iron-icon slot="icon" icon="oobe-32:warning"></iron-icon>
-  <h1 slot="title">
-    [[i18nDynamic(locale,'dataLossWarningTitle')]]
-  </h1>
-  <div slot="subtitle"
-    inner-h-t-m-l="[[getDataLossWarningSubtitleMessage(locale, email)]]">
-  </div>
-  <div slot="content" class="flex layout vertical center
-      center-justified">
-    <iron-icon icon="oobe-illos:data-loss-illo"
-        class="illustration-jelly">
-    </iron-icon>
-  </div>
-  <div slot="back-navigation">
-    <oobe-back-button id="backButton" on-click="onBackButtonClicked">
-    </oobe-back-button>
-  </div>
-  <div slot="bottom-buttons">
-    <oobe-text-button id="cancelForgot" on-click="onCancel"
-        class="focus-on-show" text-key="cancel">
-    </oobe-text-button>
-    <oobe-text-button id="proceedAnyway" on-click="onProceedClicked"
-        text-key="continueAndDeleteDataButton">
-    </oobe-text-button>
-  </div>
-</oobe-adaptive-dialog>
-
-<oobe-adaptive-dialog id="dialog" role="dialog" for-step="setup-recovery">
-  <iron-icon slot="icon" icon="oobe-32:lock"></iron-icon>
-  <h1 slot="title">[[i18nDynamic(locale, 'recoveryOptInTitle')]]</h1>
-  <div slot="subtitle">
-    [[i18nDynamic(locale, 'recoveryOptInSubtitle')]]
-  </div>
-  <div slot="content" class="flex layout vertical center
-    center-justified">
-    <iron-icon icon="oobe-illos:smart-lock-illo"
-        class="illustration-jelly">
-    </iron-icon>
-  </div>
-  <div slot="bottom-buttons">
-    <oobe-text-button id="skipButton" on-click="onNoRecovery"
-        text-key="recoveryOptInNoButton">
-    </oobe-text-button>
-    <oobe-text-button id="enableButton" inverse on-click="onSetRecovery"
-        text-key="recoveryOptInEnableButton">
-    </oobe-text-button>
-  </div>
-</oobe-adaptive-dialog>
-
-<oobe-loading-dialog id="progress" role="dialog" for-step="progress"
-    title-key="gaiaLoading">
-  <iron-icon slot="icon" icon="oobe-32:googleg"></iron-icon>
-</oobe-loading-dialog>
diff --git a/chrome/browser/resources/chromeos/login/screens/osauth/gaia_password_changed.ts b/chrome/browser/resources/chromeos/login/screens/osauth/gaia_password_changed.ts
deleted file mode 100644
index 0c2fb36..0000000
--- a/chrome/browser/resources/chromeos/login/screens/osauth/gaia_password_changed.ts
+++ /dev/null
@@ -1,264 +0,0 @@
-// Copyright 2015 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-/**
- * @fileoverview Polymer element for GAIA password changed screen.
- */
-
-import '//resources/ash/common/cr_elements/cros_color_overrides.css.js';
-import '//resources/polymer/v3_0/iron-icon/iron-icon.js';
-import '../../components/oobe_icons.html.js';
-import '../../components/buttons/oobe_next_button.js';
-import '../../components/common_styles/oobe_common_styles.css.js';
-import '../../components/common_styles/oobe_dialog_host_styles.css.js';
-import '../../components/dialogs/oobe_adaptive_dialog.js';
-import '../../components/dialogs/oobe_loading_dialog.js';
-import '../../components/buttons/oobe_text_button.js';
-
-import {CrInputElement} from '//resources/ash/common/cr_elements/cr_input/cr_input.js';
-import {assert} from '//resources/js/assert.js';
-import {PolymerElementProperties} from '//resources/polymer/v3_0/polymer/interfaces.js';
-import {mixinBehaviors, PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-
-import {LoginScreenBehavior, LoginScreenBehaviorInterface} from '../../components/behaviors/login_screen_behavior.js';
-import {MultiStepBehavior, MultiStepBehaviorInterface} from '../../components/behaviors/multi_step_behavior.js';
-import {OobeI18nMixin, OobeI18nMixinInterface} from '../../components/mixins/oobe_i18n_mixin.js';
-import {OobeUiState} from '../../components/display_manager_types.js';
-import {addSubmitListener} from '../../login_ui_tools.js';
-
-import {getTemplate} from './gaia_password_changed.html.js';
-
-
-/**
- * UI mode for the dialog.
- */
-enum GaiaPasswordChangedUiState {
-  PASSWORD = 'password',
-  FORGOT = 'forgot',
-  RECOVERY = 'setup-recovery',
-  PROGRESS = 'progress',
-}
-
-const GaiaPasswordChangedBase = mixinBehaviors(
-                                    [
-                                      LoginScreenBehavior,
-                                      MultiStepBehavior,
-                                    ],
-                                    OobeI18nMixin(PolymerElement)) as {
-  new (): PolymerElement & OobeI18nMixinInterface &
-      LoginScreenBehaviorInterface & MultiStepBehaviorInterface,
-};
-
-
-/**
- * Data that is passed to the screen during onBeforeShow.
- */
-interface GaiaPasswordChangedScreenData {
-  email: string;
-  showError: boolean;
-}
-
-export class GaiaPasswordChanged extends GaiaPasswordChangedBase {
-  static get is() {
-    return 'gaia-password-changed-element' as const;
-  }
-
-  static get template(): HTMLTemplateElement {
-    return getTemplate();
-  }
-
-  static get properties(): PolymerElementProperties {
-    return {
-      email: {
-        type: String,
-        value: '',
-      },
-
-      password: {
-        type: String,
-        value: '',
-      },
-
-      passwordInvalid: {
-        type: Boolean,
-        value: false,
-      },
-
-      disabled: {
-        type: Boolean,
-        value: false,
-      },
-
-      passwordInput: Object,
-    };
-  }
-
-  private email: string;
-  private password: string;
-  private passwordInvalid: boolean;
-  private disabled: boolean;
-  private passwordInput: CrInputElement;
-
-
-  // eslint-disable-next-line @typescript-eslint/naming-convention
-  override defaultUIStep(): GaiaPasswordChangedUiState {
-    return GaiaPasswordChangedUiState.PASSWORD;
-  }
-
-  override get UI_STEPS() {
-    return GaiaPasswordChangedUiState;
-  }
-
-  // clang-format off
-  override get EXTERNAL_API() : string[] {
-    return [
-      'showWrongPasswordError',
-      'suggestRecovery',
-    ];
-  }
-  // clang-format on
-
-  override ready(): void {
-    super.ready();
-    this.initializeLoginScreen('GaiaPasswordChangedScreen');
-
-    const oldpasswordInput =
-        this.shadowRoot?.querySelector<CrInputElement>('#oldPasswordInput');
-    assert(oldpasswordInput instanceof CrInputElement);
-    this.passwordInput = oldpasswordInput;
-    addSubmitListener(this.passwordInput, this.submit.bind(this));
-  }
-
-  /** Initial UI State for screen */
-  // eslint-disable-next-line @typescript-eslint/naming-convention
-  override getOobeUIInitialState(): OobeUiState {
-    return OobeUiState.PASSWORD_CHANGED;
-  }
-
-  /**
-   * Invoked just before being shown. Contains all the data for the screen.
-   */
-  onBeforeShow(data: GaiaPasswordChangedScreenData): void {
-    this.reset();
-    this.email = data.email;
-    this.passwordInvalid = data.showError;
-  }
-
-  private reset(): void {
-    this.setUIStep(GaiaPasswordChangedUiState.PASSWORD);
-    this.clearPassword();
-    this.disabled = false;
-  }
-
-  /**
-   * Called when Screen fails to authenticate with
-   * provided password.
-   */
-  showWrongPasswordError(): void {
-    this.clearPassword();
-    this.disabled = false;
-    this.passwordInvalid = true;
-    this.setUIStep(GaiaPasswordChangedUiState.PASSWORD);
-  }
-
-  /**
-   * Called when password was successfully updated
-   * and it is possible to set up recovery for the user.
-   */
-  suggestRecovery(): void {
-    this.disabled = false;
-    this.setUIStep(GaiaPasswordChangedUiState.RECOVERY);
-  }
-
-  /**
-   * Returns the subtitle message for the data loss warning screen.
-   * @param locale The i18n locale.
-   * @param email The email address that the user is trying to recover.
-   * @return The translated subtitle message.
-   */
-  private getDataLossWarningSubtitleMessage(locale: string, email: string):
-      TrustedHTML {
-    return this.i18nAdvancedDynamic(
-        locale, 'dataLossWarningSubtitle', {substitutions: [email]});
-  }
-
-  private submit(): void {
-    if (this.disabled) {
-      return;
-    }
-    if (!this.passwordInput.validate()) {
-      return;
-    }
-    this.setUIStep(GaiaPasswordChangedUiState.PROGRESS);
-    this.disabled = true;
-    this.userActed(['migrate-user-data', this.passwordInput.value]);
-  }
-
-  private onForgotPasswordClicked(): void {
-    if (this.disabled) {
-      return;
-    }
-    this.setUIStep(GaiaPasswordChangedUiState.FORGOT);
-    this.clearPassword();
-  }
-
-  private onBackButtonClicked(): void {
-    this.setUIStep(GaiaPasswordChangedUiState.PASSWORD);
-  }
-
-  private onAnimationFinish(): void {
-    this.focus();
-  }
-
-  private clearPassword(): void {
-    this.password = '';
-    this.passwordInvalid = false;
-  }
-
-  private onProceedClicked(): void {
-    if (this.disabled) {
-      return;
-    }
-    this.setUIStep(GaiaPasswordChangedUiState.PROGRESS);
-    this.disabled = true;
-    this.clearPassword();
-    this.userActed('resync');
-  }
-
-  private onNoRecovery(): void {
-    if (this.disabled) {
-      return;
-    }
-    this.setUIStep(GaiaPasswordChangedUiState.PROGRESS);
-    this.disabled = true;
-    this.clearPassword();
-    this.userActed('no-recovery');
-  }
-
-  private onSetRecovery(): void {
-    if (this.disabled) {
-      return;
-    }
-    this.setUIStep(GaiaPasswordChangedUiState.PROGRESS);
-    this.disabled = true;
-    this.clearPassword();
-    this.userActed('setup-recovery');
-  }
-
-  private onCancel(): void {
-    if (this.disabled) {
-      return;
-    }
-    this.disabled = true;
-    this.userActed('cancel');
-  }
-}
-
-declare global {
-  interface HTMLElementTagNameMap {
-    [GaiaPasswordChanged.is]: GaiaPasswordChanged;
-  }
-}
-
-customElements.define(GaiaPasswordChanged.is, GaiaPasswordChanged);
diff --git a/chrome/browser/resources/chromeos/login/test_api/test_api.ts b/chrome/browser/resources/chromeos/login/test_api/test_api.ts
index f5ed2a3..5e10536 100644
--- a/chrome/browser/resources/chromeos/login/test_api/test_api.ts
+++ b/chrome/browser/resources/chromeos/login/test_api/test_api.ts
@@ -1225,14 +1225,6 @@
   }
 
   override shouldSkip(): boolean {
-    return loadTimeData.getBoolean('testapi_shouldSkipChoobe');
-  }
-
-  // TODO(b/327270907) To avoid breaking existing calls to `shouldSkip()`,
-  // `updatedShouldSkip()` is temporarily introduced. The code in
-  // `updatedShouldSkip()` should later be moved to `shouldSkip()` once the
-  // users of the API are migrated to the new logic.
-  updatedShouldSkip(): boolean {
     assert(
         this.isShouldSkipReceived(),
         '`shouldSkip()` should only be called after `requestShouldSkip()`' +
@@ -1240,6 +1232,12 @@
     return this.shouldBeSkipped;
   }
 
+  // TODO(b/327270907): Remove `updatedShouldSkip()` after the users of the test
+  // API migrate to using `shouldSkip()`
+  updatedShouldSkip(): boolean {
+    return this.shouldSkip();
+  }
+
   isReadyForTesting(): boolean {
     return this.isVisible();
   }
@@ -1353,14 +1351,6 @@
   }
 
   override shouldSkip(): boolean {
-    return loadTimeData.getBoolean('testapi_shouldSkipTouchpadScroll');
-  }
-
-  // TODO(b/327270907) To avoid breaking existing calls to `shouldSkip()`,
-  // `updatedShouldSkip()` is temporarily introduced. The code in
-  // `updatedShouldSkip()` should later be moved to `shouldSkip()` once the
-  // users of the API are migrated to the new logic.
-  updatedShouldSkip(): boolean {
     assert(
         this.isShouldSkipReceived(),
         '`shouldSkip()` should only be called after `requestShouldSkip()`' +
@@ -1368,6 +1358,12 @@
     return this.shouldBeSkipped;
   }
 
+  // TODO(b/327270907): Remove `updatedShouldSkip()` after the users of the test
+  // API migrate to using `shouldSkip()`
+  updatedShouldSkip(): boolean {
+    return this.shouldSkip();
+  }
+
   isReadyForTesting(): boolean {
     return this.isVisible();
   }
diff --git a/chrome/browser/resources/commerce/product_specifications/app.ts b/chrome/browser/resources/commerce/product_specifications/app.ts
index b82ed3b..c16edf3 100644
--- a/chrome/browser/resources/commerce/product_specifications/app.ts
+++ b/chrome/browser/resources/commerce/product_specifications/app.ts
@@ -203,8 +203,11 @@
     }
   }
 
-  private updateSetName_(_: CustomEvent<{name: string}>) {
-    // TODO(b/330345730): Plumb name update through mojom
+  private updateSetName_(e: CustomEvent<{name: string}>) {
+    if (this.id_) {
+      this.shoppingApi_.setNameForProductSpecificationsSet(
+          this.id_, e.detail.name);
+    }
   }
 
   private seeAllSets_() {
diff --git a/chrome/browser/resources/settings/BUILD.gn b/chrome/browser/resources/settings/BUILD.gn
index eac5202..9659cdc 100644
--- a/chrome/browser/resources/settings/BUILD.gn
+++ b/chrome/browser/resources/settings/BUILD.gn
@@ -106,7 +106,6 @@
     "people_page/page_content_page.ts",
     "people_page/people_page.ts",
     "people_page/signout_dialog.ts",
-    "people_page/sync_account_control.ts",
     "people_page/sync_controls.ts",
     "people_page/sync_encryption_options.ts",
     "people_page/sync_page.ts",
@@ -216,6 +215,7 @@
   if (!is_chromeos_ash) {
     web_component_files += [
       "people_page/manage_profile.ts",
+      "people_page/sync_account_control.ts",
       "relaunch_confirmation_dialog.ts",
       "languages_page/add_languages_dialog.ts",
       "languages_page/languages_page.ts",
diff --git a/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_dialog.html b/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_dialog.html
index 225c7a5..6c99888 100644
--- a/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_dialog.html
+++ b/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_dialog.html
@@ -325,12 +325,14 @@
         </paper-spinner-lite>
         <cr-button class="cancel-button" autofocus
             disabled="[[clearingInProgress_]]"
-            on-click="onCancelClick_">$i18n{cancel}</cr-button>
-        <cr-button id="clearBrowsingDataConfirm"
-            class="action-button" on-click="clearBrowsingData_"
+            on-click="onCancelClick_">
+          $i18n{cancel}
+        </cr-button>
+        <cr-button id="clearButton" class="action-button"
+            on-click="clearBrowsingData_"
             disabled="[[isClearButtonDisabled_(clearingInProgress_,
                                                clearButtonDisabled_)]]">
-            $i18n{clearData}
+          $i18n{clearData}
         </cr-button>
 
         <!-- The alert must be inside the dialog for it to be read while the
@@ -339,6 +341,7 @@
           [[clearingDataAlertString_]]
         </div>
       </div>
+<if expr="not is_chromeos">
       <template is="dom-if"
           if="[[shouldShowFooter_(isSignedIn_, syncStatus.signedInState)]]"
           restamp>
@@ -371,6 +374,7 @@
           </div>
         </div>
       </template>
+</if>
     </cr-dialog>
 
     <template is="dom-if" if="[[showHistoryDeletionDialog_]]" restamp>
diff --git a/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_dialog.ts b/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_dialog.ts
index 885aea4..02a0334 100644
--- a/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_dialog.ts
+++ b/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_dialog.ts
@@ -18,11 +18,16 @@
 import '../controls/settings_checkbox.js';
 import '../icons.html.js';
 import '../settings_shared.css.js';
+// <if expr="not is_chromeos">
+import '../people_page/sync_account_control.js';
+
+// </if>
 
 import type {SyncBrowserProxy, SyncStatus} from '/shared/settings/people_page/sync_browser_proxy.js';
 import {SignedInState, StatusAction, SyncBrowserProxyImpl} from '/shared/settings/people_page/sync_browser_proxy.js';
 import {PrefsMixin} from '/shared/settings/prefs/prefs_mixin.js';
 import {getInstance as getAnnouncerInstance} from 'chrome://resources/cr_elements/cr_a11y_announcer/cr_a11y_announcer.js';
+import type {CrButtonElement} from 'chrome://resources/cr_elements/cr_button/cr_button.js';
 import type {CrDialogElement} from 'chrome://resources/cr_elements/cr_dialog/cr_dialog.js';
 import type {CrTabsElement} from 'chrome://resources/cr_elements/cr_tabs/cr_tabs.js';
 import {I18nMixin} from 'chrome://resources/cr_elements/i18n_mixin.js';
@@ -64,6 +69,7 @@
     clearBrowsingDataConfirm: HTMLElement,
     cookiesCheckbox: SettingsCheckboxElement,
     cookiesCheckboxBasic: SettingsCheckboxElement,
+    clearButton: CrButtonElement,
     clearBrowsingDataDialog: CrDialogElement,
     pages: IronPagesElement,
     tabs: CrTabsElement,
@@ -280,6 +286,7 @@
         value: false,
       },
 
+      // <if expr="not is_chromeos">
       isSyncPaused_: {
         type: Boolean,
         value: false,
@@ -298,6 +305,7 @@
         computed:
             'computeHasOtherError_(syncStatus, isSyncPaused_, hasPassphraseError_)',
       },
+      // </if>
 
       selectedTabIndex_: Number,
 
@@ -351,9 +359,11 @@
   private isSyncConsented_: boolean;
   private isSyncingHistory_: boolean;
   private shouldShowCookieException_: boolean;
+  // <if expr="not is_chromeos">
   private isSyncPaused_: boolean;
   private hasPassphraseError_: boolean;
   private hasOtherSyncError_: boolean;
+  // </if>
   private selectedTabIndex_: number;
   private tabsNames_: string[];
   private googleSearchHistoryString_: TrustedHTML;
@@ -715,7 +725,7 @@
     }
   }
 
-  // <if expr="not chromeos_ash">
+  // <if expr="not is_chromeos">
   /** Called when the user clicks the link in the footer. */
   private onSyncDescriptionLinkClicked_(e: Event) {
     if ((e.target as HTMLElement).tagName === 'A') {
@@ -742,7 +752,6 @@
       }
     }
   }
-  // </if>
 
   private computeIsSyncPaused_(): boolean {
     return !!this.syncStatus!.hasError &&
@@ -759,6 +768,7 @@
     return this.syncStatus !== undefined && !!this.syncStatus!.hasError &&
         !this.isSyncPaused_ && !this.hasPassphraseError_;
   }
+  // </if>
 
   private computeGoogleSearchHistoryString_(isNonGoogleDse: boolean):
       TrustedHTML {
@@ -767,16 +777,15 @@
         this.i18nAdvanced('clearGoogleSearchHistoryGoogleDse');
   }
 
+  // <if expr="not is_chromeos">
   private shouldShowFooter_(): boolean {
     let showFooter = false;
-    // <if expr="not is_chromeos">
     if (this.unoDesktopEnabled_) {
       showFooter = this.isSignedIn_;
     } else {
       showFooter = !!this.syncStatus &&
           this.syncStatus.signedInState === SignedInState.SYNCING;
     }
-    // </if>
     return showFooter;
   }
 
@@ -796,6 +805,7 @@
     return !this.showSigninInfo_() && !!this.syncStatus &&
         !this.syncStatus.hasError;
   }
+  // </if>
 
   private onTimePeriodChanged_() {
     const dropdownMenu = this.getTimeRangeDropdownForCurrentPage_();
diff --git a/chrome/browser/resources/settings/people_page/people_page.html b/chrome/browser/resources/settings/people_page/people_page.html
index e04b6b6..249ce32 100644
--- a/chrome/browser/resources/settings/people_page/people_page.html
+++ b/chrome/browser/resources/settings/people_page/people_page.html
@@ -53,6 +53,7 @@
     <settings-animated-pages id="pages" section="people"
         focus-config="[[focusConfig_]]">
       <div route-path="default">
+<if expr="not chromeos_ash">
         <template is="dom-if" if="[[shouldShowSyncAccountControl_(
             syncStatus.syncSystemEnabled)]]">
           <settings-sync-account-control
@@ -66,6 +67,7 @@
                   "$i18n{peopleSignInPromptSecondaryWithNoAccount}">
           </settings-sync-account-control>
         </template>
+</if>
         <template is="dom-if" if="[[!shouldShowSyncAccountControl_(
             syncStatus.syncSystemEnabled, signinAllowed_)]]" restamp>
           <div id="profile-row" class="cr-row first two-line"
@@ -176,6 +178,7 @@
 </if>
     </settings-animated-pages>
 
+<if expr="not chromeos_ash">
     <template is="dom-if" if="[[showSignoutDialog_]]" restamp>
       <settings-signout-dialog sync-status="[[syncStatus]]"
           on-close="onDisconnectDialogClosed_">
@@ -187,6 +190,7 @@
           on-close="onImportDataDialogClosed_">
       </settings-import-data-dialog>
     </template>
+</if>
     <cr-toast duration="3000" id="toast">
       <span>$i18n{syncSettingsSavedToast}</span>
     </cr-toast>
diff --git a/chrome/browser/resources/settings/people_page/people_page.ts b/chrome/browser/resources/settings/people_page/people_page.ts
index 239005f..bf39fba 100644
--- a/chrome/browser/resources/settings/people_page/people_page.ts
+++ b/chrome/browser/resources/settings/people_page/people_page.ts
@@ -17,7 +17,9 @@
 import 'chrome://resources/polymer/v3_0/iron-flex-layout/iron-flex-layout-classes.js';
 import '../controls/settings_toggle_button.js';
 import './page_content_page.js';
+// <if expr="not chromeos_ash">
 import './sync_account_control.js';
+// </if>
 import '../icons.html.js';
 import '../settings_page/settings_animated_pages.js';
 import '../settings_page/settings_subpage.js';
@@ -43,7 +45,11 @@
 import {loadTimeData} from '../i18n_setup.js';
 import type {PageVisibility} from '../page_visibility.js';
 import {routes} from '../route.js';
-import {RouteObserverMixin, Router} from '../router.js';
+import {Router} from '../router.js';
+
+// <if expr="not chromeos_ash">
+import {RouteObserverMixin} from '../router.js';
+// </if>
 
 // <if expr="chromeos_ash">
 import {AccountManagerBrowserProxyImpl} from './account_manager_browser_proxy.js';
@@ -58,8 +64,14 @@
   };
 }
 
+// <if expr="not chromeos_ash">
 const SettingsPeoplePageElementBase =
     RouteObserverMixin(WebUiListenerMixin(BaseMixin(PolymerElement)));
+// </if>
+// <if expr="chromeos_ash">
+const SettingsPeoplePageElementBase =
+    WebUiListenerMixin(BaseMixin(PolymerElement));
+// </if>
 
 export class SettingsPeoplePageElement extends SettingsPeoplePageElementBase {
   static get is() {
@@ -160,9 +172,10 @@
         type: Boolean,
         value: false,
       },
-      // </if>
 
       showSignoutDialog_: Boolean,
+      // </if>
+
 
       focusConfig_: {
         type: Object,
@@ -199,9 +212,9 @@
   storedAccounts: StoredAccount[]|null;
   private shouldShowGoogleAccount_: boolean;
   private showImportDataDialog_: boolean;
+  private showSignoutDialog_: boolean;
   // </if>
 
-  private showSignoutDialog_: boolean;
   private focusConfig_: FocusConfig;
 
   private syncBrowserProxy_: SyncBrowserProxy =
@@ -246,11 +259,10 @@
     // </if>
   }
 
+  // <if expr="not chromeos_ash">
   override currentRouteChanged() {
-    // <if expr="not chromeos_ash">
     this.showImportDataDialog_ =
         Router.getInstance().getCurrentRoute() === routes.IMPORT_DATA;
-    // </if>
 
     if (Router.getInstance().getCurrentRoute() === routes.SIGN_OUT) {
       // If the sync status has not been fetched yet, optimistically display
@@ -263,6 +275,7 @@
       }
     }
   }
+  // </if>
 
   private getEditPersonAssocControl_(): Element {
     return this.signinAllowed_ ?
@@ -354,6 +367,7 @@
     // </if>
   }
 
+  // <if expr="not chromeos_ash">
   private onDisconnectDialogClosed_() {
     this.showSignoutDialog_ = false;
 
@@ -361,6 +375,7 @@
       Router.getInstance().navigateToPreviousRoute();
     }
   }
+  // </if>
 
   private onSyncClick_() {
     // Users can go to sync subpage regardless of sync status.
diff --git a/chrome/browser/resources/settings/people_page/sync_account_control.html b/chrome/browser/resources/settings/people_page/sync_account_control.html
index 1523394..f2ee389 100644
--- a/chrome/browser/resources/settings/people_page/sync_account_control.html
+++ b/chrome/browser/resources/settings/people_page/sync_account_control.html
@@ -114,8 +114,6 @@
         display: block;
       }
     </style>
-    <!-- TODO(jamescook): Show the promo on Chrome OS if the user is signed-in
-         but has sync disabled. -->
     <div id="banner" hidden="[[shouldHideBanner_(syncStatus.signedInState)]]"
         part="banner"></div>
     <div class="cr-row first"
@@ -170,8 +168,6 @@
                 shownAccount_.isPrimaryAccount)]]
           </div>
         </div>
-<!-- Chrome OS does not allow switching users for sync. -->
-<if expr="not chromeos_ash">
         <cr-icon-button class="icon-arrow-dropdown cr-button-gap"
             hidden="[[!shouldAllowAccountSwitch_(syncStatus.signedInState,
                 syncStatus.domain, syncStatus.signinPaused)]]"
@@ -182,7 +178,6 @@
             hidden="[[!shouldAllowAccountSwitch_(syncStatus.signedInState,
                 syncStatus.domain, syncStatus.signinPaused)]]">
         </div>
-</if>
         <cr-button id="sync-button" class="action-button cr-button-gap"
             hidden="[[shouldHideSyncButton_(syncStatus.signedInState,
                 syncStatus.signinPaused)]]"
@@ -228,8 +223,6 @@
         </div>
 
       </div>
-<!-- Chrome OS does not allow switching users for sync. -->
-<if expr="not chromeos_ash">
       <template is="dom-if"
           if="[[shouldAllowAccountSwitch_(syncStatus.signedInState,
               syncStatus.domain, syncStatus.signinPaused)]]" restamp>
@@ -255,5 +248,4 @@
           </button>
         </cr-action-menu>
       </template>
-</if>
     </template>
diff --git a/chrome/browser/resources/settings/people_page/sync_account_control.ts b/chrome/browser/resources/settings/people_page/sync_account_control.ts
index 023ad961..14cfaec2 100644
--- a/chrome/browser/resources/settings/people_page/sync_account_control.ts
+++ b/chrome/browser/resources/settings/people_page/sync_account_control.ts
@@ -338,15 +338,6 @@
   }
 
   private shouldShowTurnOffButton_(): boolean {
-    // <if expr="chromeos_ash">
-    if (this.syncStatus.domain) {
-      // Chrome OS cannot delete the user's profile like other platforms, so
-      // hide the turn off sync button for enterprise users who are not
-      // allowed to sign out.
-      return false;
-    }
-    // </if>
-
     return !this.hideButtons && !this.showSetupButtons_ && this.isSyncing_();
   }
 
@@ -392,11 +383,9 @@
     const router = Router.getInstance();
     const routes = router.getRoutes();
     switch (this.syncStatus.statusAction) {
-      // <if expr="not chromeos_ash">
       case StatusAction.REAUTHENTICATE:
         this.syncBrowserProxy_.startSignIn();
         break;
-      // </if>
       case StatusAction.UPGRADE_CLIENT:
         router.navigateTo(routes.ABOUT);
         break;
@@ -411,13 +400,7 @@
   }
 
   private onSigninClick_() {
-    // <if expr="not chromeos_ash">
     this.syncBrowserProxy_.startSignIn();
-    // </if>
-    // <if expr="chromeos_ash">
-    // Chrome OS is always signed-in, so just turn on sync.
-    this.syncBrowserProxy_.turnOnSync();
-    // </if>
     // Need to close here since one menu item also triggers this function.
     const actionMenu = this.shadowRoot!.querySelector('cr-action-menu');
     if (actionMenu) {
@@ -425,12 +408,10 @@
     }
   }
 
-  // <if expr="not chromeos_ash">
   private onSignoutClick_() {
     this.syncBrowserProxy_.signOut(false /* deleteProfile */);
     this.shadowRoot!.querySelector('cr-action-menu')!.close();
   }
-  // </if>
 
   private onSyncButtonClick_() {
     assert(this.shownAccount_);
diff --git a/chrome/browser/resources/settings/people_page/sync_page.html b/chrome/browser/resources/settings/people_page/sync_page.html
index aff2f05..5288207d 100644
--- a/chrome/browser/resources/settings/people_page/sync_page.html
+++ b/chrome/browser/resources/settings/people_page/sync_page.html
@@ -69,6 +69,7 @@
       }
     </style>
 
+<if expr="not chromeos_ash">
     <template is="dom-if" if="[[shouldShowSyncAccountControl_(
         syncStatus.syncSystemEnabled)]]">
       <settings-sync-account-control embedded-in-subpage
@@ -80,6 +81,7 @@
           on-sync-setup-done="onSyncSetupDone_">
       </settings-sync-account-control>
     </template>
+</if>
     <div class="cr-row first" hidden="[[!syncDisabledByAdmin_]]">
       <iron-icon id="disabled-by-admin-icon" icon="cr20:domain"></iron-icon>
       <div class="flex cr-padded-text">
diff --git a/chrome/browser/resources/settings/people_page/sync_page.ts b/chrome/browser/resources/settings/people_page/sync_page.ts
index 002c95ce..6722c2f 100644
--- a/chrome/browser/resources/settings/people_page/sync_page.ts
+++ b/chrome/browser/resources/settings/people_page/sync_page.ts
@@ -14,13 +14,16 @@
 import '//resources/polymer/v3_0/iron-collapse/iron-collapse.js';
 import '//resources/polymer/v3_0/iron-icon/iron-icon.js';
 import '//resources/polymer/v3_0/iron-flex-layout/iron-flex-layout-classes.js';
-import './sync_account_control.js';
+// <if expr="not chromeos_ash">
+import '//resources/cr_elements/cr_toast/cr_toast.js';
+// </if>
+
 import './sync_encryption_options.js';
 import '../privacy_page/personalization_options.js';
 import '../settings_shared.css.js';
 import '../settings_vars.css.js';
 // <if expr="not chromeos_ash">
-import '//resources/cr_elements/cr_toast/cr_toast.js';
+import './sync_account_control.js';
 
 // </if>
 
@@ -678,16 +681,13 @@
     }
   }
 
+  // <if expr="not chromeos_ash">
   private shouldShowSyncAccountControl_(): boolean {
-    // <if expr="chromeos_ash">
-    return false;
-    // </if>
-    // <if expr="not chromeos_ash">
     return this.syncStatus !== undefined &&
         !!this.syncStatus.syncSystemEnabled &&
         loadTimeData.getBoolean('signinAllowed');
-    // </if>
   }
+  // </if>
 
   private computeShowExistingPassphraseBelowAccount_(): boolean {
     return this.syncStatus !== undefined &&
diff --git a/chrome/browser/resources/settings/performance_page/performance_page.html b/chrome/browser/resources/settings/performance_page/performance_page.html
index 64bd59c..c4e71f4 100644
--- a/chrome/browser/resources/settings/performance_page/performance_page.html
+++ b/chrome/browser/resources/settings/performance_page/performance_page.html
@@ -25,7 +25,7 @@
           group-aria-label="$i18n{memorySaverModeRadioGroupAriaLabel}">
         <controlled-radio-button id="conservativeButton"
             label="$i18n{memorySaverModeConservativeLabel}"
-            name="[[memorySaverModeAggressivenessEnum_.CONSERVATIVE]]"
+            name$="[[memorySaverModeAggressivenessEnum_.CONSERVATIVE]]"
             pref="[[
                 prefs.performance_tuning.high_efficiency_mode.aggressiveness]]">
           <div class="cr-secondary-text">
@@ -34,7 +34,7 @@
         </controlled-radio-button>
         <controlled-radio-button id="mediumButton"
             label="$i18n{memorySaverModeMediumLabel}"
-            name="[[memorySaverModeAggressivenessEnum_.MEDIUM]]"
+            name$="[[memorySaverModeAggressivenessEnum_.MEDIUM]]"
             pref="[[
                 prefs.performance_tuning.high_efficiency_mode.aggressiveness]]">
           <div class="cr-secondary-text">
@@ -43,7 +43,7 @@
         </controlled-radio-button>
         <controlled-radio-button id="aggressiveButton"
             label="$i18n{memorySaverModeAggressiveLabel}"
-            name="[[memorySaverModeAggressivenessEnum_.AGGRESSIVE]]"
+            name$="[[memorySaverModeAggressivenessEnum_.AGGRESSIVE]]"
             pref="[[
                 prefs.performance_tuning.high_efficiency_mode.aggressiveness]]">
           <div class="cr-secondary-text">
diff --git a/chrome/browser/resources/settings/privacy_page/cookies_page.html b/chrome/browser/resources/settings/privacy_page/cookies_page.html
index 3277fc7..f1c515d 100644
--- a/chrome/browser/resources/settings/privacy_page/cookies_page.html
+++ b/chrome/browser/resources/settings/privacy_page/cookies_page.html
@@ -9,7 +9,8 @@
         padding: 0 var(--cr-section-padding);
       }
 
-      #exceptionHeader3pcd {
+      #exceptionHeader3pcd,
+      #exceptionHeaderTrackingProtection {
         padding: 0 var(--cr-section-padding);
         margin-bottom: -32px;
       }
@@ -267,17 +268,30 @@
         on-click="onSiteDataClick_" label="$i18n{cookiePageAllSitesLink}"
         role-description="$i18n{subpageArrowRoleDescription}">
     </cr-link-row>
-    <div id="exceptionHeader3pcd">
-      <h2>$i18n{trackingProtectionSitesAllowedCookiesTitle}</h2>
-    </div>
-    <site-list id="allowExceptionsList"
-        category="[[cookiesContentSettingType_]]"
-        category-subtype="[[contentSetting_.ALLOW]]"
-        category-header="$i18n{trackingProtectionSitesAllowedCookiesDescription}"
-        read-only-list="[[exceptionListsReadOnly_]]"
-        search-filter="[[searchTerm]]"
-        cookies-exception-type="third-party">
-    </site-list>
+    <template is="dom-if" if="[[!enableTrackingProtectionRolloutUx_]]">
+      <div id="exceptionHeader3pcd">
+        <h2>$i18n{trackingProtectionSitesAllowedCookiesTitle}</h2>
+      </div>
+      <site-list id="allow3pcExceptionsList"
+          category="[[cookiesContentSettingType_]]"
+          category-subtype="[[contentSetting_.ALLOW]]"
+          category-header="$i18n{trackingProtectionSitesAllowedCookiesDescription}"
+          read-only-list="[[exceptionListsReadOnly_]]"
+          search-filter="[[searchTerm]]"
+          cookies-exception-type="third-party">
+      </site-list>
+    </template>
+    <template is="dom-if" if="[[enableTrackingProtectionRolloutUx_]]">
+      <div id="exceptionHeaderTrackingProtection">
+        <h2>$i18n{trackingProtectionSitesAllowedCookiesTitle}</h2>
+      </div>
+      <site-list id="trackingProtectionExceptionsList"
+          category="[[trackingProtectionContentSettingType_]]"
+          category-subtype="[[contentSetting_.ALLOW]]"
+          category-header="$i18n{trackingProtectionSitesAllowedCookiesDescription}"
+          search-filter="[[searchTerm]]">
+      </site-list>
+    </template>
     <cr-toast id="toast">
       <div id="toastText">$i18n{privacySandboxCookiesDialog}</div>
       <cr-button on-click="onPrivacySandboxClick_">
diff --git a/chrome/browser/resources/settings/privacy_page/cookies_page.ts b/chrome/browser/resources/settings/privacy_page/cookies_page.ts
index 185ef60b..a37ac5eb 100644
--- a/chrome/browser/resources/settings/privacy_page/cookies_page.ts
+++ b/chrome/browser/resources/settings/privacy_page/cookies_page.ts
@@ -94,6 +94,11 @@
         value: ContentSettingsTypes.COOKIES,
       },
 
+      trackingProtectionContentSettingType_: {
+        type: String,
+        value: ContentSettingsTypes.TRACKING_PROTECTION,
+      },
+
       exceptionListsReadOnly_: {
         type: Boolean,
         value: false,
@@ -116,6 +121,12 @@
         value: () => loadTimeData.getBoolean('firstPartySetsUIEnabled'),
       },
 
+      enableTrackingProtectionRolloutUx_: {
+        type: Boolean,
+        value: () =>
+            loadTimeData.getBoolean('enableTrackingProtectionRolloutUx'),
+      },
+
       is3pcdRedesignEnabled_: {
         type: Boolean,
         value: () =>
@@ -149,10 +160,12 @@
 
   searchTerm: string;
   private cookiesContentSettingType_: ContentSettingsTypes;
+  private trackingProtectionContentSettingType_: ContentSettingsTypes;
   private exceptionListsReadOnly_: boolean;
   private blockAllPref_: chrome.settingsPrivate.PrefObject;
   focusConfig: FocusConfig;
   private enableFirstPartySetsUI_: boolean;
+  private enableTrackingProtectionRolloutUx_: boolean;
   private is3pcdRedesignEnabled_: boolean;
   private isIpProtectionAvailable_: boolean;
   private isFingerprintingProtectionAvailable_: boolean;
diff --git a/chrome/browser/resources/settings/router.ts b/chrome/browser/resources/settings/router.ts
index 259e80e8..385c0d1 100644
--- a/chrome/browser/resources/settings/router.ts
+++ b/chrome/browser/resources/settings/router.ts
@@ -32,7 +32,6 @@
   // <if expr="_google_chrome">
   GET_MOST_CHROME: Route;
   // </if>
-  IMPORT_DATA: Route;
   INCOMPATIBLE_APPLICATIONS: Route;
   LANGUAGES: Route;
   MANAGE_PROFILE: Route;
@@ -59,7 +58,6 @@
   SECURITY: Route;
   SECURITY_KEYS: Route;
   SECURITY_KEYS_PHONES: Route;
-  SIGN_OUT: Route;
   SITE_SETTINGS: Route;
   SITE_SETTINGS_ADS: Route;
   SITE_SETTINGS_ALL: Route;
@@ -114,6 +112,11 @@
   SYSTEM: Route;
   TRACKING_PROTECTION: Route;
   TRIGGERED_RESET_DIALOG: Route;
+
+  // <if expr="not chromeos_ash">
+  IMPORT_DATA: Route;
+  SIGN_OUT: Route;
+  // </if>
 }
 
 /** Class for navigable routes. */
diff --git a/chrome/browser/resources/settings/settings.ts b/chrome/browser/resources/settings/settings.ts
index 31b3656..6a4aecc 100644
--- a/chrome/browser/resources/settings/settings.ts
+++ b/chrome/browser/resources/settings/settings.ts
@@ -73,7 +73,9 @@
 export {AccountManagerBrowserProxy, AccountManagerBrowserProxyImpl} from './people_page/account_manager_browser_proxy.js';
 // </if>
 export {SettingsPeoplePageElement} from './people_page/people_page.js';
+// <if expr="not chromeos_ash">
 export {MAX_SIGNIN_PROMO_IMPRESSION, SettingsSyncAccountControlElement} from './people_page/sync_account_control.js';
+// </if>
 export {BATTERY_SAVER_MODE_PREF, SettingsBatteryPageElement} from './performance_page/battery_page.js';
 export {PerformanceBrowserProxy, PerformanceBrowserProxyImpl} from './performance_page/performance_browser_proxy.js';
 export {BatterySaverModeState, MemorySaverModeAggressiveness, MemorySaverModeExceptionListAction, MemorySaverModeState, PerformanceMetricsProxy, PerformanceMetricsProxyImpl} from './performance_page/performance_metrics_proxy.js';
diff --git a/chrome/browser/resources/side_panel/read_anything/app.ts b/chrome/browser/resources/side_panel/read_anything/app.ts
index 189aa7a..46deb69b 100644
--- a/chrome/browser/resources/side_panel/read_anything/app.ts
+++ b/chrome/browser/resources/side_panel/read_anything/app.ts
@@ -795,7 +795,7 @@
   }
 
   updateSelection() {
-    const selection = this.getSelection()!;
+    const selection: Selection = this.getSelection()!;
     selection.removeAllRanges();
 
     const range = new Range();
@@ -847,8 +847,18 @@
       }
     }
 
-    range.setStart(startNode, startOffset);
-    range.setEnd(endNode, endOffset);
+    // Gmail will try to select text when collapsing the node. At the same time,
+    // the node contents are then shortened because of the collapse which causes
+    // the range to go out of bounds. When this happens we should reset the
+    // selection.
+    try {
+      range.setStart(startNode, startOffset);
+      range.setEnd(endNode, endOffset);
+    } catch (err) {
+      selection.removeAllRanges();
+      return;
+    }
+
     selection.addRange(range);
 
     // Scroll the start node into view. ScrollIntoView is available on the
@@ -1378,65 +1388,15 @@
     }
   }
 
-  // TODO: Should this be merged with highlightAndPlayMessage?
   highlightAndPlayInterruptedMessage(): boolean {
-    // getCurrentText gets the AX Node IDs of text that should be spoken and
-    // highlighted.
-    const axNodeIds: number[] = chrome.readingMode.getCurrentText();
-
-    // If there aren't any valid ax node ids returned by getCurrentText,
-    // speech should stop.
-    if (axNodeIds.length === 0) {
-      return false;
-    }
-
-    const utteranceText = this.extractTextOf(axNodeIds);
-    // Return if the utterance is empty or null.
-    if (!utteranceText) {
-      // TODO(b/332694565): This fallback should never be needed, but it is.
-      // Investigate root cause of Read Aloud / Reading Mode mismatch.
-      chrome.readingMode.movePositionToNextGranularity();
-      return this.highlightAndPlayInterruptedMessage();
-    }
-
-    // The TTS engine may not like attempts to speak whitespace, so move to the
-    // next utterance.
-    if (utteranceText.trim().length === 0) {
-      chrome.readingMode.movePositionToNextGranularity();
-      return this.highlightAndPlayInterruptedMessage();
-    }
-
-    if (this.wordBoundaryState.mode === WordBoundaryMode.BOUNDARY_DETECTED) {
-      const substringIndex = this.wordBoundaryState.previouslySpokenIndex +
-          this.wordBoundaryState.speechUtteranceStartIndex;
-      this.wordBoundaryState.previouslySpokenIndex = 0;
-      this.wordBoundaryState.speechUtteranceStartIndex = substringIndex;
-      const utteranceTextForWordBoundary =
-          utteranceText.substring(substringIndex);
-      // Don't use the word boundary if it's going to cause a TTS engine issue.
-      if (utteranceTextForWordBoundary.trim().length === 0) {
-        this.playText(utteranceText);
-      } else {
-        this.playText(utteranceText.substring(substringIndex));
-      }
-    } else {
-      this.playText(utteranceText);
-    }
-    if (this.wordBoundaryState.mode ===
-            WordBoundaryMode.BOUNDARIES_NOT_SUPPORTED ||
-        !this.shouldUseWordHighlighting()) {
-      this.highlightNodes(axNodeIds);
-    } else {
-      this.highlightNodesForWordBoundary();
-    }
-    return true;
+    return this.highlightAndPlayMessage(/* isInterrupted = */ true);
   }
 
   // Play text of these axNodeIds. When finished, read and highlight to read the
   // following text.
   // TODO (crbug.com/1474951): Investigate using AXRange.GetText to get text
   // between start node / end nodes and their offsets.
-  highlightAndPlayMessage(): boolean {
+  highlightAndPlayMessage(isInterrupted: boolean = false): boolean {
     // getCurrentText gets the AX Node IDs of text that should be spoken and
     // highlighted.
     const axNodeIds: number[] = chrome.readingMode.getCurrentText();
@@ -1456,17 +1416,37 @@
       // TODO(b/332694565): This fallback should never be needed, but it is.
       // Investigate root cause of Read Aloud / Reading Mode mismatch.
       chrome.readingMode.movePositionToNextGranularity();
-      return this.highlightAndPlayMessage();
+      return this.highlightAndPlayMessage(isInterrupted);
     }
 
     // The TTS engine may not like attempts to speak whitespace, so move to the
     // next utterance.
     if (utteranceText.trim().length === 0) {
       chrome.readingMode.movePositionToNextGranularity();
-      return this.highlightAndPlayMessage();
+      return this.highlightAndPlayMessage(isInterrupted);
     }
 
-    this.playText(utteranceText);
+    // If we're resuming a previously interrupted message, use word
+    // boundaries (if available) to resume at the beginning of the current
+    // word.
+    if (isInterrupted &&
+        this.wordBoundaryState.mode === WordBoundaryMode.BOUNDARY_DETECTED) {
+      const substringIndex = this.wordBoundaryState.previouslySpokenIndex +
+          this.wordBoundaryState.speechUtteranceStartIndex;
+      this.wordBoundaryState.previouslySpokenIndex = 0;
+      this.wordBoundaryState.speechUtteranceStartIndex = substringIndex;
+      const utteranceTextForWordBoundary =
+          utteranceText.substring(substringIndex);
+      // Don't use the word boundary if it's going to cause a TTS engine issue.
+      if (utteranceTextForWordBoundary.trim().length === 0) {
+        this.playText(utteranceText);
+      } else {
+        this.playText(utteranceText.substring(substringIndex));
+      }
+    } else {
+      this.playText(utteranceText);
+    }
+
     if (this.wordBoundaryState.mode ===
             WordBoundaryMode.BOUNDARIES_NOT_SUPPORTED ||
         !this.shouldUseWordHighlighting()) {
@@ -2050,8 +2030,8 @@
       return;
     }
 
-    const selectedVoice = this.getVoices()
-                              .filter(voice => voice.name === storedVoiceName);
+    const selectedVoice =
+        this.getVoices().filter(voice => voice.name === storedVoiceName);
     this.selectedVoice = selectedVoice && (selectedVoice.length > 0) ?
         selectedVoice[0] :
         this.defaultVoice();
diff --git a/chrome/browser/resources/webui_gallery/BUILD.gn b/chrome/browser/resources/webui_gallery/BUILD.gn
index cc3a31b..d963c51 100644
--- a/chrome/browser/resources/webui_gallery/BUILD.gn
+++ b/chrome/browser/resources/webui_gallery/BUILD.gn
@@ -23,11 +23,15 @@
     "demos/cr_dialog/cr_dialog_demo.css",
     "demos/cr_icons/cr_icons_demo.css",
     "demos/cr_input/cr_input_demo.css",
+    "demos/cr_slider/cr_slider_demo.css",
+    "demos/cr_tabs/cr_tabs_demo.css",
+    "demos/cr_toast/cr_toast_demo.css",
     "demos/demo.css",
     "demos/demo_lit.css",
     "demos/md_select/md_select_demo.css",
     "demos/nav_menu/nav_menu_demo.css",
     "demos/progress_indicators/progress_indicator_demo.css",
+    "demos/side_panel/sp_components_demo.css",
   ]
 
   # Files holding a Polymer or native custom element definition AND have an
@@ -77,7 +81,7 @@
   ]
 
   ts_path_mappings =
-      [ "chrome://webui-gallery/shared/*|" +
+      [ "//webui-gallery/shared/*|" +
         rebase_path(
             "$root_gen_dir/chrome/browser/resources/side_panel/shared/tsc/*",
             target_gen_dir) ]
diff --git a/chrome/browser/resources/webui_gallery/demos/cr_slider/cr_slider_demo.css b/chrome/browser/resources/webui_gallery/demos/cr_slider/cr_slider_demo.css
new file mode 100644
index 0000000..4829535
--- /dev/null
+++ b/chrome/browser/resources/webui_gallery/demos/cr_slider/cr_slider_demo.css
@@ -0,0 +1,14 @@
+/* Copyright 2024 The Chromium Authors
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file. */
+
+/* #css_wrapper_metadata_start
+ * #type=style-lit
+ * #import=../demo_lit.css.js
+ * #scheme=relative
+ * #include=demo-lit
+ * #css_wrapper_metadata_end */
+
+cr-slider {
+  width: 200px;
+}
diff --git a/chrome/browser/resources/webui_gallery/demos/cr_slider/cr_slider_demo.html b/chrome/browser/resources/webui_gallery/demos/cr_slider/cr_slider_demo.html
index c3c761c..d0290b5 100644
--- a/chrome/browser/resources/webui_gallery/demos/cr_slider/cr_slider_demo.html
+++ b/chrome/browser/resources/webui_gallery/demos/cr_slider/cr_slider_demo.html
@@ -1,29 +1,26 @@
-<style include="demo">
-  cr-slider {
-    width: 200px;
-  }
-</style>
-
 <h1>cr-slider</h1>
 
 <h2>Indiscrete</h2>
 <div class="demos">
-  <cr-slider id="basicSlider" min="0" max="20" value="[[basicValue_]]"
-      on-cr-slider-value-changed="onBasicValueChanged_">
+  <cr-slider id="basicSlider" min="0" max="20" .value="${this.basicValue_}"
+      @cr-slider-value-changed="${this.onBasicValueChanged_}">
   </cr-slider>
-  <div>Value of slider: [[basicValue_]]</div>
+  <div>Value of slider: ${this.basicValue_}</div>
 </div>
 
 <h2>5 ticks, increments of 5</h2>
 <div class="demos">
-  <cr-slider id="tickedSlider" ticks="[[ticks_]]"
-      marker-count="[[getMarkerCount_(showMarkers_)]]"
-      value="[[tickedValue_]]"
-      on-cr-slider-value-changed="onTickedValueChanged_">
+  <cr-slider id="tickedSlider" .ticks="${this.ticks_}"
+      marker-count="${this.getMarkerCount_()}"
+      .value="${this.tickedValue_}"
+      @cr-slider-value-changed="${this.onTickedValueChanged_}">
   </cr-slider>
-  <div>Value of slider, the index of selected tick: [[tickedValue_]]</div>
-  <div>Value of selected tick: [[getTickValue_(tickedValue_)]]</div>
-  <cr-checkbox checked="{{showMarkers_}}">Show markers</cr-checkbox>
+  <div>Value of slider, the index of selected tick: ${this.tickedValue_}</div>
+  <div>Value of selected tick: ${this.getTickValue_()}</div>
+  <cr-checkbox ?checked="${this.showMarkers_}"
+      @checked-changed="${this.onShowMarkersChanged_}">
+    Show markers
+  </cr-checkbox>
 </div>
 
 <h2>Disabled</h2>
diff --git a/chrome/browser/resources/webui_gallery/demos/cr_slider/cr_slider_demo.ts b/chrome/browser/resources/webui_gallery/demos/cr_slider/cr_slider_demo.ts
index 0a9be3c..464bc36 100644
--- a/chrome/browser/resources/webui_gallery/demos/cr_slider/cr_slider_demo.ts
+++ b/chrome/browser/resources/webui_gallery/demos/cr_slider/cr_slider_demo.ts
@@ -4,19 +4,12 @@
 
 import '//resources/cr_elements/cr_checkbox/cr_checkbox.js';
 import '//resources/cr_elements/cr_slider/cr_slider.js';
-import '../demo.css.js';
 
 import type {CrSliderElement, SliderTick} from '//resources/cr_elements/cr_slider/cr_slider.js';
-import {PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {CrLitElement} from '//resources/lit/v3_0/lit.rollup.js';
 
-import {getTemplate} from './cr_slider_demo.html.js';
-
-interface CrSliderDemoElement {
-  $: {
-    basicSlider: CrSliderElement,
-    tickedSlider: CrSliderElement,
-  };
-}
+import {getCss} from './cr_slider_demo.css.js';
+import {getHtml} from './cr_slider_demo.html.js';
 
 function createTicks(
     min: number, increment: number, steps: number): SliderTick[] {
@@ -31,31 +24,42 @@
   return ticks;
 }
 
-class CrSliderDemoElement extends PolymerElement {
+export interface CrSliderDemoElement {
+  $: {
+    basicSlider: CrSliderElement,
+    tickedSlider: CrSliderElement,
+  };
+}
+
+export class CrSliderDemoElement extends CrLitElement {
   static get is() {
     return 'cr-slider-demo';
   }
 
-  static get template() {
-    return getTemplate();
+  static override get styles() {
+    return getCss();
   }
 
-  static get properties() {
+  override render() {
+    return getHtml.bind(this)();
+  }
+
+  static override get properties() {
     return {
-      basicValue_: Number,
-      disabledTicks_: Array,
-      showMarkers_: Boolean,
-      tickedValue_: Number,
-      ticks_: Array,
+      basicValue_: {type: Number},
+      disabledTicks_: {type: Array},
+      showMarkers_: {type: Boolean},
+      tickedValue_: {type: Number},
+      ticks_: {type: Array},
     };
   }
 
-  private basicValue_: number = 5;
-  private showMarkers_: boolean = false;
-  private tickedValue_: number = 0;
-  private ticks_: SliderTick[] = createTicks(0, 5, 5);
+  protected basicValue_: number = 5;
+  protected showMarkers_: boolean = false;
+  protected tickedValue_: number = 0;
+  protected ticks_: SliderTick[] = createTicks(0, 5, 5);
 
-  private getMarkerCount_(): number {
+  protected getMarkerCount_(): number {
     if (!this.showMarkers_) {
       return 0;
     }
@@ -63,17 +67,21 @@
     return this.ticks_.length;
   }
 
-  private getTickValue_(): number {
+  protected getTickValue_(): number {
     return this.ticks_[this.tickedValue_]!.value;
   }
 
-  private onBasicValueChanged_() {
+  protected onBasicValueChanged_() {
     this.basicValue_ = this.$.basicSlider.value;
   }
 
-  private onTickedValueChanged_() {
+  protected onTickedValueChanged_() {
     this.tickedValue_ = this.$.tickedSlider.value;
   }
+
+  protected onShowMarkersChanged_(e: CustomEvent<{value: boolean}>) {
+    this.showMarkers_ = e.detail.value;
+  }
 }
 
 export const tagName = CrSliderDemoElement.is;
diff --git a/chrome/browser/resources/webui_gallery/demos/cr_tabs/cr_tabs_demo.css b/chrome/browser/resources/webui_gallery/demos/cr_tabs/cr_tabs_demo.css
new file mode 100644
index 0000000..834eeab
--- /dev/null
+++ b/chrome/browser/resources/webui_gallery/demos/cr_tabs/cr_tabs_demo.css
@@ -0,0 +1,22 @@
+/* Copyright 2024 The Chromium Authors
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file. */
+
+/* #css_wrapper_metadata_start
+ * #type=style-lit
+ * #import=../demo_lit.css.js
+ * #scheme=relative
+ * #include=demo-lit
+ * #css_wrapper_metadata_end */
+
+cr-tabs,
+cr-page-selector {
+  width: 100%;
+}
+
+.tab-contents {
+  align-items: center;
+  display: flex;
+  height: 150px;
+  justify-content: center;
+}
diff --git a/chrome/browser/resources/webui_gallery/demos/cr_tabs/cr_tabs_demo.html b/chrome/browser/resources/webui_gallery/demos/cr_tabs/cr_tabs_demo.html
index d4e10550..e0513d7 100644
--- a/chrome/browser/resources/webui_gallery/demos/cr_tabs/cr_tabs_demo.html
+++ b/chrome/browser/resources/webui_gallery/demos/cr_tabs/cr_tabs_demo.html
@@ -1,39 +1,25 @@
-<style include="demo">
-  cr-tabs,
-  cr-page-selector {
-    width: 100%;
-  }
-
-  .tab-contents {
-    align-items: center;
-    display: flex;
-    height: 150px;
-    justify-content: center;
-  }
-</style>
-
 <h1>cr-tabs</h1>
 <div class="demos">
-  <cr-tabs id="tabs" tab-names="[[tabNames_]]" selected="{{selectedTabIndex_}}">
+  <cr-tabs id="tabs" .tabNames="${this.tabNames_}"
+      .selected="${this.selectedTabIndex_}"
+      @selected-changed="${this.onSelectedTabIndexChanged_}">
   </cr-tabs>
-  <cr-page-selector selected="[[selectedTabIndex_]]">
-    <template is="dom-repeat" items="[[tabNames_]]" as="tabName">
-      <div class="tab-contents">
-        [[tabName]] contents
-      </div>
-    </template>
+  <cr-page-selector .selected="${this.selectedTabIndex_}">
+    ${this.tabNames_.map(tabName => html`
+      <div class="tab-contents">${tabName} contents</div>
+    `)}
   </cr-page-selector>
 </div>
 
 <div class="demos">
   <div class="row">
-    <cr-button on-click="onAddClick_">Add</cr-button>
-    <cr-button on-click="onAddAt1Click_">Add at 1</cr-button>
-    <cr-button on-click="onSelectAt1Click_">Select at 1</cr-button>
+    <cr-button @click="${this.onAddClick_}">Add</cr-button>
+    <cr-button @click="${this.onAddAt1Click_}">Add at 1</cr-button>
+    <cr-button @click="${this.onSelectAt1Click_}">Select at 1</cr-button>
   </div>
 </div>
 
 <div>
-  Tab Count: [[tabNames_.length]],
-  Selected Tab: [[selectedTabIndex_]]
+  Tab Count: ${this.tabNames_.length},
+  Selected Tab: ${this.selectedTabIndex_}
 </div>
diff --git a/chrome/browser/resources/webui_gallery/demos/cr_tabs/cr_tabs_demo.ts b/chrome/browser/resources/webui_gallery/demos/cr_tabs/cr_tabs_demo.ts
index 267b443..f98688a6 100644
--- a/chrome/browser/resources/webui_gallery/demos/cr_tabs/cr_tabs_demo.ts
+++ b/chrome/browser/resources/webui_gallery/demos/cr_tabs/cr_tabs_demo.ts
@@ -5,51 +5,59 @@
 import '//resources/cr_elements/cr_button/cr_button.js';
 import '//resources/cr_elements/cr_page_selector/cr_page_selector.js';
 import '//resources/cr_elements/cr_tabs/cr_tabs.js';
-import '../demo.css.js';
 
 import type {CrTabsElement} from '//resources/cr_elements/cr_tabs/cr_tabs.js';
-import {PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {CrLitElement} from '//resources/lit/v3_0/lit.rollup.js';
 
-import {getTemplate} from './cr_tabs_demo.html.js';
+import {getCss} from './cr_tabs_demo.css.js';
+import {getHtml} from './cr_tabs_demo.html.js';
 
-interface CrTabsDemoElement {
+export interface CrTabsDemoElement {
   $: {
     tabs: CrTabsElement,
   };
 }
 
-class CrTabsDemoElement extends PolymerElement {
+export class CrTabsDemoElement extends CrLitElement {
   static get is() {
     return 'cr-tabs-demo';
   }
 
-  static get template() {
-    return getTemplate();
+  static override get styles() {
+    return getCss();
   }
 
-  static get properties() {
+  override render() {
+    return getHtml.bind(this)();
+  }
+
+  static override get properties() {
     return {
-      selectedTabIndex_: Number,
-      tabNames_: Array,
+      selectedTabIndex_: {type: Number},
+      tabNames_: {type: Array},
     };
   }
 
-  private selectedTabIndex_ = 0;
-  private tabNames_: string[] = ['Tab 1', 'Tab 2', 'Tab 3'];
+  protected selectedTabIndex_: number = 0;
+  protected tabNames_: string[] = ['Tab 1', 'Tab 2', 'Tab 3'];
 
-  private onAddClick_() {
-    this.push('tabNames_', 'Added');
-    this.$.tabs.requestUpdate();
+  protected onAddClick_() {
+    this.tabNames_.push('Added');
+    this.tabNames_ = this.tabNames_.slice();
   }
 
-  private onAddAt1Click_() {
-    this.splice('tabNames_', 1, 0, 'Added at 1');
-    this.$.tabs.requestUpdate();
+  protected onAddAt1Click_() {
+    this.tabNames_.splice(1, 0, 'Added at 1');
+    this.tabNames_ = this.tabNames_.slice();
   }
 
-  private onSelectAt1Click_() {
+  protected onSelectAt1Click_() {
     this.selectedTabIndex_ = 1;
   }
+
+  protected onSelectedTabIndexChanged_(e: CustomEvent<{value: number}>) {
+    this.selectedTabIndex_ = e.detail.value;
+  }
 }
 
 export const tagName = CrTabsDemoElement.is;
diff --git a/chrome/browser/resources/webui_gallery/demos/cr_toast/cr_toast_demo.css b/chrome/browser/resources/webui_gallery/demos/cr_toast/cr_toast_demo.css
new file mode 100644
index 0000000..6a3e3a1
--- /dev/null
+++ b/chrome/browser/resources/webui_gallery/demos/cr_toast/cr_toast_demo.css
@@ -0,0 +1,18 @@
+/* Copyright 2024 The Chromium Authors
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file. */
+
+/* #css_wrapper_metadata_start
+ * #type=style-lit
+ * #import=../demo_lit.css.js
+ * #scheme=relative
+ * #include=demo-lit
+ * #css_wrapper_metadata_end */
+
+cr-input {
+  --cr-input-error-display: none;
+}
+
+#toastContent {
+  flex: 1;
+}
diff --git a/chrome/browser/resources/webui_gallery/demos/cr_toast/cr_toast_demo.html b/chrome/browser/resources/webui_gallery/demos/cr_toast/cr_toast_demo.html
index 2a90d1f..6d79ab0 100644
--- a/chrome/browser/resources/webui_gallery/demos/cr_toast/cr_toast_demo.html
+++ b/chrome/browser/resources/webui_gallery/demos/cr_toast/cr_toast_demo.html
@@ -1,23 +1,16 @@
-<style include="demo">
-  cr-input {
-    --cr-input-error-display: none;
-  }
-
-  #toastContent {
-    flex: 1;
-  }
-</style>
-
 <h1>cr-toast</h1>
 <div class="demos">
-  <cr-input type="text" label="Toast message" value="{{message_}}"></cr-input>
-  <cr-checkbox checked="{{showDismissButton_}}">
+  <cr-input type="text" label="Toast message" .value="${this.message_}"
+      @value-changed="${this.onMessageChanged_}"></cr-input>
+  <cr-checkbox ?checked="${this.showDismissButton_}"
+      @checked-changed="${this.onShowDismissButtonChanged_}">
     Show dismiss button
   </cr-checkbox>
-  <cr-input type="number" label="Duration (ms)" value="{{duration_}}">
+  <cr-input type="number" label="Duration (ms)" .value="${this.duration_}"
+      @value-changed="${this.onDurationChanged_}">
   </cr-input>
 
-  <cr-button on-click="onShowToastClick_">Show toast</cr-button>
+  <cr-button @click="${this.onShowToastClick_}">Show toast</cr-button>
 </div>
 
 <h1>cr-toast-manager</h1>
@@ -27,16 +20,17 @@
     toast messages that can be truncated if strings are too long.
   </div>
 
-  <cr-button on-click="onShowToastManagerClick_">
+  <cr-button @click="${this.onShowToastManagerClick_}">
     Show toast from toast manager
   </cr-button>
 </div>
 
-<cr-toast id="toast" duration="[[duration_]]">
-  <div id="toastContent">[[message_]]</div>
-  <cr-button hidden$="[[!showDismissButton_]]" on-click="onHideToastClick_">
+<cr-toast id="toast" duration="${this.duration_}">
+  <div id="toastContent">${this.message_}</div>
+  <cr-button ?hidden="${!this.showDismissButton_}"
+      @click="${this.onHideToastClick_}">
     Dismiss
   </cr-button>
 </cr-toast>
 
-<cr-toast-manager duration="[[duration_]]"></cr-toast-manager>
+<cr-toast-manager .duration="${this.duration_}"></cr-toast-manager>
diff --git a/chrome/browser/resources/webui_gallery/demos/cr_toast/cr_toast_demo.ts b/chrome/browser/resources/webui_gallery/demos/cr_toast/cr_toast_demo.ts
index e2e1a62..7089199 100644
--- a/chrome/browser/resources/webui_gallery/demos/cr_toast/cr_toast_demo.ts
+++ b/chrome/browser/resources/webui_gallery/demos/cr_toast/cr_toast_demo.ts
@@ -7,46 +7,50 @@
 import '//resources/cr_elements/cr_input/cr_input.js';
 import '//resources/cr_elements/cr_toast/cr_toast.js';
 import '//resources/cr_elements/cr_toast/cr_toast_manager.js';
-import '../demo.css.js';
 
 import type {CrToastElement} from '//resources/cr_elements/cr_toast/cr_toast.js';
 import {getToastManager} from '//resources/cr_elements/cr_toast/cr_toast_manager.js';
-import {PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {CrLitElement} from '//resources/lit/v3_0/lit.rollup.js';
 
-import {getTemplate} from './cr_toast_demo.html.js';
+import {getCss} from './cr_toast_demo.css.js';
+import {getHtml} from './cr_toast_demo.html.js';
 
-interface CrToastDemoElement {
+export interface CrToastDemoElement {
   $: {
     toast: CrToastElement,
   };
 }
 
-class CrToastDemoElement extends PolymerElement {
+export class CrToastDemoElement extends CrLitElement {
   static get is() {
     return 'cr-toast-demo';
   }
 
-  static get template() {
-    return getTemplate();
+  static override get styles() {
+    return getCss();
   }
 
-  static get properties() {
+  override render() {
+    return getHtml.bind(this)();
+  }
+
+  static override get properties() {
     return {
-      message_: String,
-      duration_: Number,
-      showDismissButton_: Boolean,
+      message_: {type: String},
+      duration_: {type: Number},
+      showDismissButton_: {type: Boolean},
     };
   }
 
-  private duration_: number = 0;
-  private message_: string = 'Hello, world';
-  private showDismissButton_: boolean = false;
+  protected duration_: number = 0;
+  protected message_: string = 'Hello, world';
+  protected showDismissButton_: boolean = false;
 
-  private onHideToastClick_() {
+  protected onHideToastClick_() {
     this.$.toast.hide();
   }
 
-  private onShowToastManagerClick_() {
+  protected onShowToastManagerClick_() {
     getToastManager().showForStringPieces([
       {value: '\'', collapsible: false},
       {
@@ -59,9 +63,21 @@
     ]);
   }
 
-  private onShowToastClick_() {
+  protected onShowToastClick_() {
     this.$.toast.show();
   }
+
+  protected onMessageChanged_(e: CustomEvent<{value: string}>) {
+    this.message_ = e.detail.value;
+  }
+
+  protected onShowDismissButtonChanged_(e: CustomEvent<{value: boolean}>) {
+    this.showDismissButton_ = e.detail.value;
+  }
+
+  protected onDurationChanged_(e: CustomEvent<{value: number}>) {
+    this.duration_ = e.detail.value;
+  }
 }
 
 export const tagName = CrToastDemoElement.is;
diff --git a/chrome/browser/resources/webui_gallery/demos/side_panel/sp_components_demo.css b/chrome/browser/resources/webui_gallery/demos/side_panel/sp_components_demo.css
new file mode 100644
index 0000000..de5f6f95
--- /dev/null
+++ b/chrome/browser/resources/webui_gallery/demos/side_panel/sp_components_demo.css
@@ -0,0 +1,56 @@
+/* Copyright 2024 The Chromium Authors
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file. */
+
+/* #css_wrapper_metadata_start
+ * #type=style-lit
+ * #import=../demo_lit.css.js
+ * #import=//resources/cr_elements/md_select_lit.css.js
+ * #import=//webui-gallery/shared/sp_shared_style_lit.css.js
+ * #import=//webui-gallery/shared/sp_shared_vars.css.js
+ * #scheme=relative
+ * #include=md-select-lit sp-shared-style-lit demo-lit
+ * #css_wrapper_metadata_end */
+
+#urlCountSlider {
+  width: 200px;
+}
+
+.side-panel-demo {
+  background: var(--color-side-panel-content-background);
+  border: solid 1px var(--cr-separator-color);
+  box-sizing: border-box;
+  display: flex;
+  flex-direction: column;
+  padding: var(--sp-body-padding) 0;
+  height: 500px;
+  width: 304px;
+}
+
+.side-panel-demo:has(sp-footer) {
+  padding-block-end: 0;
+}
+
+sp-heading {
+  padding: 0;
+}
+
+#scroller {
+  display: flex;
+  flex-direction: column;
+  height: 200px;
+  min-height: 0;
+}
+
+.card-content {
+  align-items: center;
+  display: flex;
+  height: 300px;
+  justify-content: center;
+  width: 100%;
+}
+
+#emptyStateDemo {
+  align-items: center;
+  gap: 0;
+}
diff --git a/chrome/browser/resources/webui_gallery/demos/side_panel/sp_components_demo.html b/chrome/browser/resources/webui_gallery/demos/side_panel/sp_components_demo.html
index a31357e..3f7d587 100644
--- a/chrome/browser/resources/webui_gallery/demos/side_panel/sp_components_demo.html
+++ b/chrome/browser/resources/webui_gallery/demos/side_panel/sp_components_demo.html
@@ -1,48 +1,3 @@
-<style include="md-select sp-shared-style demo">
-  #urlCountSlider {
-    width: 200px;
-  }
-
-  .side-panel-demo {
-    background: var(--color-side-panel-content-background);
-    border: solid 1px var(--cr-separator-color);
-    box-sizing: border-box;
-    display: flex;
-    flex-direction: column;
-    padding: var(--sp-body-padding) 0;
-    height: 500px;
-    width: 304px;
-  }
-
-  .side-panel-demo:has(sp-footer) {
-    padding-block-end: 0;
-  }
-
-  sp-heading {
-    padding: 0;
-  }
-
-  #scroller {
-    display: flex;
-    flex-direction: column;
-    height: 200px;
-    min-height: 0;
-  }
-
-  .card-content {
-    align-items: center;
-    display: flex;
-    height: 300px;
-    justify-content: center;
-    width: 100%;
-  }
-
-  #emptyStateDemo {
-    align-items: center;
-    gap: 0;
-  }
-</style>
-
 <h1>Side panel shared components</h1>
 <div class="demos">
   Shared UI elements that are only accessible to WebUIs in
@@ -52,24 +7,28 @@
 <div class="demos">
   <div class="row center">
     <label id="urlCountLabel">Number of url items</label>
-    <cr-slider id="urlCountSlider" min="1" max="30" value="[[urlCount_]]"
-        on-cr-slider-value-changed="onUrlCountChanged_"
+    <cr-slider id="urlCountSlider" min="1" max="30" .value="${this.urlCount_}"
+        @cr-slider-value-changed="${this.onUrlCountChanged_}"
         aria-labelledby="urlCountLabel">
     </cr-slider>
   </div>
-  <cr-checkbox checked="{{hideBackButton_}}">
+  <cr-checkbox ?checked="${this.hideBackButton_}"
+      @checked-changed="${this.onHideBackButtonChanged_}">
     Hide back button in heading
   </cr-checkbox>
-  <cr-checkbox checked="{{showBadges_}}">Show item badges</cr-checkbox>
+  <cr-checkbox ?checked="${this.showBadges_}"
+      @checked-changed="${this.onShowBadgesChanged_}">
+    Show item badges
+  </cr-checkbox>
   <div class="row center">
     <label id="itemSizeLabel">Item size</label>
     <select id="itemSizeSelect" class="md-select"
         aria-labelledby="itemSizeLabel"
-        value="[[itemSize_]]"
-        on-change="onItemSizeChanged_">
-      <template is="dom-repeat" items="[[itemSizeOptions_]]">
-        <option value="[[item]]">[[item]]</option>
-      </template>
+        .value="${this.itemSize_}"
+        @change="${this.onItemSizeChanged_}">
+      ${this.itemSizeOptions_.map(item => html`
+        <option .value="${item}">${item}</option>
+      `)}
     </select>
   </div>
 </div>
@@ -77,23 +36,24 @@
 <h2>Scrollable container with cards</h2>
 <div id="scroller" class="side-panel-demo sp-scroller">
   <div class="sp-card">
-    <sp-heading hide-back-button="[[hideBackButton_]]">
+    <sp-heading ?hide-back-button="${this.hideBackButton_}"
+        back-button-aria-label="$i18n{backButton}">
       <h3 slot="heading">Heading</h3>
     </sp-heading>
 
-    <template is="dom-repeat" items="[[urls_]]">
-      <cr-url-list-item title="[[item.title]]"
-          description="[[item.url]]"
-          url="[[item.url]]"
-          size="[[itemSize_]]">
-        <template is="dom-if" if="[[showBadges_]]" restamp>
+    ${this.urls_.map(item => html`
+      <cr-url-list-item title="${item.title}"
+          description="${item.url}"
+          .url="${item.url}"
+          .size="${this.itemSize_}">
+        ${this.showBadges_ ? html`
           <sp-list-item-badge slot="badges">
             <cr-icon icon="cr:info-outline"></cr-icon>
             <span>2 Notes</span>
           </sp-list-item-badge>
-        </template>
+        ` : ''}
       </cr-url-list-item>
-    </template>
+    `)}
   </div>
   <hr class="sp-cards-separator">
   <div class="sp-card">
diff --git a/chrome/browser/resources/webui_gallery/demos/side_panel/sp_components_demo.ts b/chrome/browser/resources/webui_gallery/demos/side_panel/sp_components_demo.ts
index b839753..70148fa 100644
--- a/chrome/browser/resources/webui_gallery/demos/side_panel/sp_components_demo.ts
+++ b/chrome/browser/resources/webui_gallery/demos/side_panel/sp_components_demo.ts
@@ -9,79 +9,77 @@
 import '//resources/cr_elements/cr_icon_button/cr_icon_button.js';
 import '//resources/cr_elements/cr_slider/cr_slider.js';
 import '//resources/cr_elements/cr_url_list_item/cr_url_list_item.js';
-import '//resources/cr_elements/md_select.css.js';
 import '//webui-gallery/shared/sp_empty_state.js';
 import '//webui-gallery/shared/sp_footer.js';
 import '//webui-gallery/shared/sp_heading.js';
 import '//webui-gallery/shared/sp_icons.html.js';
 import '//webui-gallery/shared/sp_list_item_badge.js';
-import '//webui-gallery/shared/sp_shared_style.css.js';
-import '//webui-gallery/shared/sp_shared_vars.css.js';
-import '../demo.css.js';
 
 import type {CrSliderElement} from '//resources/cr_elements/cr_slider/cr_slider.js';
 import {CrUrlListItemSize} from '//resources/cr_elements/cr_url_list_item/cr_url_list_item.js';
-import {PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {CrLitElement} from '//resources/lit/v3_0/lit.rollup.js';
+import type {PropertyValues} from '//resources/lit/v3_0/lit.rollup.js';
 
-import {getTemplate} from './sp_components_demo.html.js';
+import {getCss} from './sp_components_demo.css.js';
+import {getHtml} from './sp_components_demo.html.js';
 
 interface UrlItem {
   title: string;
   url: string;
 }
 
-interface SpComponentsDemo {
+export interface SpComponentsDemoElement {
   $: {
     itemSizeSelect: HTMLSelectElement,
     urlCountSlider: CrSliderElement,
   };
 }
 
-class SpComponentsDemo extends PolymerElement {
+export class SpComponentsDemoElement extends CrLitElement {
   static get is() {
     return 'sp-components-demo';
   }
 
-  static get template() {
-    return getTemplate();
+  static override get styles() {
+    return getCss();
   }
 
-  static get properties() {
+  override render() {
+    return getHtml.bind(this)();
+  }
+
+  static override get properties() {
     return {
-      hideBackButton_: {
-        type: Boolean,
-        value: true,
-      },
-      itemSize_: {
-        type: String,
-        value: CrUrlListItemSize.COMPACT,
-      },
-      itemSizeOptions_: {
-        type: Array,
-        value: [
-          CrUrlListItemSize.COMPACT,
-          CrUrlListItemSize.MEDIUM,
-          CrUrlListItemSize.LARGE,
-        ],
-      },
-      showBadges_: Boolean,
-      urlCount_: {
-        type: Number,
-        value: 15,
-      },
-      urls_: {
-        type: Array,
-        computed: 'computeUrls_(urlCount_)',
-      },
+      hideBackButton_: {type: Boolean},
+      itemSize_: {type: String},
+      itemSizeOptions_: {type: Array},
+      showBadges_: {type: Boolean},
+      urlCount_: {type: Number},
+      urls_: {type: Array},
     };
   }
 
-  private hideBackButton_: boolean;
-  private itemSize_: CrUrlListItemSize;
-  private itemSizeOptions_: CrUrlListItemSize[];
-  private showBadges_: boolean;
-  private urlCount_: number;
-  private urls_: UrlItem[];
+  protected hideBackButton_: boolean = true;
+  protected itemSize_: CrUrlListItemSize = CrUrlListItemSize.COMPACT;
+  protected itemSizeOptions_: CrUrlListItemSize[] = [
+    CrUrlListItemSize.COMPACT,
+    CrUrlListItemSize.MEDIUM,
+    CrUrlListItemSize.LARGE,
+  ];
+  protected showBadges_: boolean = false;
+  protected urlCount_: number = 15;
+  protected urls_: UrlItem[];
+
+  override willUpdate(changedProperties: PropertyValues<this>) {
+    super.willUpdate(changedProperties);
+
+    const changedPrivateProperties =
+        changedProperties as Map<PropertyKey, unknown>;
+
+    if (changedPrivateProperties.has('urlCount_')) {
+      this.urls_ = this.computeUrls_();
+    }
+  }
 
   private computeUrls_(): UrlItem[] {
     const urls: UrlItem[] = [];
@@ -94,15 +92,23 @@
     return urls;
   }
 
-  private onItemSizeChanged_() {
+  protected onItemSizeChanged_() {
     this.itemSize_ = this.$.itemSizeSelect.value as CrUrlListItemSize;
   }
 
-  private onUrlCountChanged_() {
+  protected onUrlCountChanged_() {
     this.urlCount_ = this.$.urlCountSlider.value;
   }
+
+  protected onHideBackButtonChanged_(e: CustomEvent<{value: boolean}>) {
+    this.hideBackButton_ = e.detail.value;
+  }
+
+  protected onShowBadgesChanged_(e: CustomEvent<{value: boolean}>) {
+    this.showBadges_ = e.detail.value;
+  }
 }
 
-export const tagName = SpComponentsDemo.is;
+export const tagName = SpComponentsDemoElement.is;
 
-customElements.define(SpComponentsDemo.is, SpComponentsDemo);
+customElements.define(SpComponentsDemoElement.is, SpComponentsDemoElement);
diff --git a/chrome/browser/safe_browsing/OWNERS b/chrome/browser/safe_browsing/OWNERS
index 3f8c72f..6398957 100644
--- a/chrome/browser/safe_browsing/OWNERS
+++ b/chrome/browser/safe_browsing/OWNERS
@@ -5,3 +5,6 @@
 per-file chrome_safe_browsing_blocking_page_factory.*=skrakowi@chromium.org
 per-file safe_browsing_blocking_page_test.*=skrakowi@chromium.org
 per-file threat_details_unittest.cc=skrakowi@chromium.org
+
+# For extensions telemetry changes.
+per-file BUILD.gn=anunoy@chromium.org
diff --git a/chrome/browser/safe_browsing/cloud_content_scanning/multipart_uploader.cc b/chrome/browser/safe_browsing/cloud_content_scanning/multipart_uploader.cc
index 8ace6d3..ad915986 100644
--- a/chrome/browser/safe_browsing/cloud_content_scanning/multipart_uploader.cc
+++ b/chrome/browser/safe_browsing/cloud_content_scanning/multipart_uploader.cc
@@ -164,11 +164,10 @@
   request->headers.SetHeader("X-Goog-Upload-Header-Content-Length",
                              base::NumberToString(data_size));
 
-  if (access_token_.empty()) {
-    request->credentials_mode = network::mojom::CredentialsMode::kOmit;
-  } else {
+  if (!access_token_.empty()) {
     SetAccessTokenAndClearCookieInResourceRequest(request, access_token_);
   }
+  request->credentials_mode = network::mojom::CredentialsMode::kOmit;
 }
 
 void MultipartUploadRequest::MarkScanAsCompleteForTesting() {
diff --git a/chrome/browser/safe_browsing/cloud_content_scanning/resumable_uploader.cc b/chrome/browser/safe_browsing/cloud_content_scanning/resumable_uploader.cc
index 86bc2f71..47f5d46 100644
--- a/chrome/browser/safe_browsing/cloud_content_scanning/resumable_uploader.cc
+++ b/chrome/browser/safe_browsing/cloud_content_scanning/resumable_uploader.cc
@@ -108,11 +108,10 @@
   request->headers.SetHeader(kUploadHeaderContentTypeHeader,
                              kUploadContentType);
 
-  if (access_token_.empty()) {
-    request->credentials_mode = network::mojom::CredentialsMode::kOmit;
-  } else {
+  if (!access_token_.empty()) {
     SetAccessTokenAndClearCookieInResourceRequest(request, access_token_);
   }
+  request->credentials_mode = network::mojom::CredentialsMode::kOmit;
 }
 
 void ResumableUploadRequest::Start() {
diff --git a/chrome/browser/sharing/sharing_device_registration.cc b/chrome/browser/sharing/sharing_device_registration.cc
index ad7b468..1ff4e29 100644
--- a/chrome/browser/sharing/sharing_device_registration.cc
+++ b/chrome/browser/sharing/sharing_device_registration.cc
@@ -11,6 +11,7 @@
 #include "base/base64url.h"
 #include "base/feature_list.h"
 #include "base/functional/bind.h"
+#include "base/metrics/histogram_functions.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
 #include "chrome/browser/sharing/buildflags.h"
@@ -164,6 +165,8 @@
     return;
   }
 
+  base::UmaHistogramBoolean("Sharing.LocalSharingTargetInfoSupportsSync",
+                            !!sharing_target_info);
   std::set<SharingSpecificFields::EnabledFeatures> enabled_features =
       GetEnabledFeatures(/*supports_vapid=*/authorized_entity.has_value());
   syncer::DeviceInfo::SharingInfo sharing_info(
diff --git a/chrome/browser/sharing/sharing_fcm_sender.cc b/chrome/browser/sharing/sharing_fcm_sender.cc
index 86bcb9a..d90ffd1 100644
--- a/chrome/browser/sharing/sharing_fcm_sender.cc
+++ b/chrome/browser/sharing/sharing_fcm_sender.cc
@@ -73,6 +73,9 @@
     return;
   }
 
+  base::UmaHistogramBoolean(
+      "Sharing.SendMessageWithSyncAckFcmConfiguration",
+      !message.fcm_channel_configuration().sender_id_fcm_token().empty());
   SendMessageToFcmTarget(*fcm_configuration, time_to_live, std::move(message),
                          std::move(callback));
 }
diff --git a/chrome/browser/shortcuts/BUILD.gn b/chrome/browser/shortcuts/BUILD.gn
index b0a2a71..237a695 100644
--- a/chrome/browser/shortcuts/BUILD.gn
+++ b/chrome/browser/shortcuts/BUILD.gn
@@ -22,10 +22,8 @@
 
     sources += [
       "create_shortcut_for_current_web_contents_task.cc",
-      "document_icon_fetcher.cc",
-      "document_icon_fetcher.h",
-      "fetch_icons_from_document_task.cc",
-      "fetch_icons_from_document_task.h",
+      "document_icon_fetcher_task.cc",
+      "document_icon_fetcher_task.h",
       "icon_badging.cc",
       "icon_badging.h",
       "shortcut_creator.cc",
diff --git a/chrome/browser/shortcuts/create_shortcut_for_current_web_contents_task.cc b/chrome/browser/shortcuts/create_shortcut_for_current_web_contents_task.cc
index f38e3ee..835082c 100644
--- a/chrome/browser/shortcuts/create_shortcut_for_current_web_contents_task.cc
+++ b/chrome/browser/shortcuts/create_shortcut_for_current_web_contents_task.cc
@@ -10,7 +10,7 @@
 #include "base/task/bind_post_task.h"
 #include "chrome/browser/platform_util.h"  // nogncheck (crbug.com/335727004)
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/shortcuts/document_icon_fetcher.h"
+#include "chrome/browser/shortcuts/document_icon_fetcher_task.h"
 #include "chrome/browser/shortcuts/icon_badging.h"
 #include "chrome/browser/shortcuts/shortcut_creator.h"
 #include "chrome/common/chrome_features.h"
@@ -101,15 +101,18 @@
   callback_ = std::move(callback);
   Observe(&web_contents);
 
-  DocumentIconFetcher::FetchIcons(
+  icon_fetcher_task_ = std::make_unique<DocumentIconFetcherTask>(
       web_contents, base::BindOnce(&CreateShortcutForCurrentWebContentsTask::
                                        OnIconsFetchedStartBadgingAndShowDialog,
                                    weak_ptr_factory_.GetWeakPtr()));
+  icon_fetcher_task_->StartIconFetching();
 }
 
 void CreateShortcutForCurrentWebContentsTask::
     OnIconsFetchedStartBadgingAndShowDialog(
         FetchIconsFromDocumentResult result) {
+  CHECK(icon_fetcher_task_);
+  icon_fetcher_task_.reset();
   if (!result.has_value()) {
     OnMetadataFetchCompleteSelfDestruct(
         base::unexpected(ShortcutCreationTaskResult::kIconFetchingFailed));
diff --git a/chrome/browser/shortcuts/create_shortcut_for_current_web_contents_task.h b/chrome/browser/shortcuts/create_shortcut_for_current_web_contents_task.h
index 7f964c2..ac0d6a16 100644
--- a/chrome/browser/shortcuts/create_shortcut_for_current_web_contents_task.h
+++ b/chrome/browser/shortcuts/create_shortcut_for_current_web_contents_task.h
@@ -5,9 +5,11 @@
 #ifndef CHROME_BROWSER_SHORTCUTS_CREATE_SHORTCUT_FOR_CURRENT_WEB_CONTENTS_TASK_H_
 #define CHROME_BROWSER_SHORTCUTS_CREATE_SHORTCUT_FOR_CURRENT_WEB_CONTENTS_TASK_H_
 
+#include <memory>
+
 #include "base/functional/callback.h"
 #include "base/memory/weak_ptr.h"
-#include "chrome/browser/shortcuts/fetch_icons_from_document_task.h"
+#include "chrome/browser/shortcuts/document_icon_fetcher_task.h"
 #include "chrome/browser/shortcuts/shortcut_creator.h"
 #include "content/public/browser/document_user_data.h"
 #include "content/public/browser/web_contents_observer.h"
@@ -94,6 +96,7 @@
 
   ShortcutsDialogCallback dialog_callback_;
   base::OnceCallback<void(ShortcutCreationTaskResult task_result)> callback_;
+  std::unique_ptr<DocumentIconFetcherTask> icon_fetcher_task_;
   base::WeakPtrFactory<CreateShortcutForCurrentWebContentsTask>
       weak_ptr_factory_{this};
 };
diff --git a/chrome/browser/shortcuts/document_icon_fetcher.cc b/chrome/browser/shortcuts/document_icon_fetcher.cc
deleted file mode 100644
index b75fdc8..0000000
--- a/chrome/browser/shortcuts/document_icon_fetcher.cc
+++ /dev/null
@@ -1,90 +0,0 @@
-// Copyright 2024 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/shortcuts/document_icon_fetcher.h"
-
-#include <iterator>
-#include <memory>
-
-#include "base/containers/flat_map.h"
-#include "base/functional/bind.h"
-#include "base/functional/callback.h"
-#include "base/functional/callback_forward.h"
-#include "base/location.h"
-#include "base/memory/weak_ptr.h"
-#include "base/task/sequenced_task_runner.h"
-#include "base/types/expected.h"
-#include "base/types/pass_key.h"
-#include "chrome/browser/shortcuts/fetch_icons_from_document_task.h"
-#include "chrome/browser/shortcuts/shortcut_icon_generator.h"
-#include "content/public/browser/document_user_data.h"
-#include "content/public/browser/web_contents.h"
-#include "third_party/skia/include/core/SkBitmap.h"
-
-namespace shortcuts {
-
-// static
-void DocumentIconFetcher::FetchIcons(content::WebContents& web_contents,
-                                     FetchIconsFromDocumentCallback callback) {
-  DocumentIconFetcher* fetch_manager =
-      DocumentIconFetcher::GetOrCreateForCurrentDocument(
-          web_contents.GetPrimaryMainFrame());
-  fetch_manager->RunTask(GenerateIconLetterFromName(web_contents.GetTitle()),
-                         std::move(callback));
-}
-
-DocumentIconFetcher::~DocumentIconFetcher() {
-  in_destruction_ = true;
-  // All of the tasks callbacks call `DocumentIconFetcher::OnTaskComplete` below
-  // directly. Save them & call them synchronously after the tasks are
-  // destroyed to prevent map modification during iteration.
-  std::vector<FetchIconsFromDocumentCallback> pending_callbacks;
-  for (const auto& [id, task] : fetch_tasks_) {
-    pending_callbacks.push_back(task->TakeCallback());
-  }
-  fetch_tasks_.clear();
-  for (FetchIconsFromDocumentCallback& callback : pending_callbacks) {
-    CHECK(callback);
-    // This calls `DocumentIconFetcher::OnTaskComplete` below.
-    std::move(callback).Run(
-        base::unexpected(FetchIconsForDocumentError::kDocumentDestroyed));
-  }
-}
-
-DOCUMENT_USER_DATA_KEY_IMPL(DocumentIconFetcher);
-
-DocumentIconFetcher::DocumentIconFetcher(content::RenderFrameHost* rfh)
-    : content::DocumentUserData<DocumentIconFetcher>(rfh) {}
-
-void DocumentIconFetcher::RunTask(char32_t fallback_letter,
-                                  FetchIconsFromDocumentCallback callback) {
-  std::unique_ptr<FetchIconsFromDocumentTask> task =
-      std::make_unique<FetchIconsFromDocumentTask>(
-          base::PassKey<DocumentIconFetcher>(), render_frame_host());
-  int task_id = next_task_id_;
-  next_task_id_++;
-  const auto& [iter, _] = fetch_tasks_.emplace(task_id, std::move(task));
-  // Note: the callback may be called synchronously.
-  iter->second->Start(base::BindOnce(&DocumentIconFetcher::OnTaskComplete,
-                                     weak_factory_.GetWeakPtr(), task_id,
-                                     fallback_letter, std::move(callback)));
-}
-
-void DocumentIconFetcher::OnTaskComplete(
-    int id,
-    char32_t fallback_letter,
-    FetchIconsFromDocumentCallback original_callback,
-    FetchIconsFromDocumentResult result) {
-  int num_erased = fetch_tasks_.erase(id);
-  CHECK(num_erased > 0 || in_destruction_);
-
-  if (result.has_value() && result->empty()) {
-    result->push_back(GenerateBitmap(128, fallback_letter));
-  }
-
-  base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
-      FROM_HERE, base::BindOnce(std::move(original_callback), result));
-}
-
-}  // namespace shortcuts
diff --git a/chrome/browser/shortcuts/document_icon_fetcher.h b/chrome/browser/shortcuts/document_icon_fetcher.h
deleted file mode 100644
index 543fe8f..0000000
--- a/chrome/browser/shortcuts/document_icon_fetcher.h
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright 2024 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_SHORTCUTS_DOCUMENT_ICON_FETCHER_H_
-#define CHROME_BROWSER_SHORTCUTS_DOCUMENT_ICON_FETCHER_H_
-
-#include <memory>
-
-#include "base/containers/flat_map.h"
-#include "base/functional/callback_forward.h"
-#include "base/memory/weak_ptr.h"
-#include "chrome/browser/shortcuts/fetch_icons_from_document_task.h"
-#include "content/public/browser/document_user_data.h"
-
-namespace content {
-class WebContents;
-}
-
-namespace shortcuts {
-
-// This object is responsible for fetching all available icons from a given
-// document.
-class DocumentIconFetcher
-    : public content::DocumentUserData<DocumentIconFetcher> {
- public:
-  // Fetches all icons for the top level primary frame of the given web
-  // contents. `callback` will always be called (even on document destruction),
-  // and always called asynchronously. If the callback is not called with an
-  // error, it is guaranteed to include at least one icon (i.e. it is not
-  // possible for fetching to succeed but not return any icons. If no icons
-  // were found, a fallback icon is generated).
-  static void FetchIcons(content::WebContents& web_contents,
-                         FetchIconsFromDocumentCallback callback);
-
-  ~DocumentIconFetcher() override;
-
- private:
-  friend DocumentUserData;
-  DOCUMENT_USER_DATA_KEY_DECL();
-
-  explicit DocumentIconFetcher(content::RenderFrameHost* rfh);
-
-  void RunTask(char32_t fallback_letter,
-               FetchIconsFromDocumentCallback callback);
-
-  void OnTaskComplete(int id,
-                      char32_t fallback_letter,
-                      FetchIconsFromDocumentCallback original_callback,
-                      FetchIconsFromDocumentResult result);
-
-  bool in_destruction_ = false;
-  int next_task_id_ = 0;
-  base::flat_map<int, std::unique_ptr<FetchIconsFromDocumentTask>> fetch_tasks_;
-
-  base::WeakPtrFactory<DocumentIconFetcher> weak_factory_{this};
-};
-
-}  // namespace shortcuts
-
-#endif  // CHROME_BROWSER_SHORTCUTS_DOCUMENT_ICON_FETCHER_H_
diff --git a/chrome/browser/shortcuts/document_icon_fetcher_browsertest.cc b/chrome/browser/shortcuts/document_icon_fetcher_browsertest.cc
index 2080e45..a570be0 100644
--- a/chrome/browser/shortcuts/document_icon_fetcher_browsertest.cc
+++ b/chrome/browser/shortcuts/document_icon_fetcher_browsertest.cc
@@ -2,8 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/shortcuts/document_icon_fetcher.h"
-
 #include <memory>
 
 #include "base/base_paths.h"
@@ -12,11 +10,10 @@
 #include "base/test/gmock_expected_support.h"
 #include "base/test/test_future.h"
 #include "base/types/expected.h"
-#include "chrome/browser/shortcuts/fetch_icons_from_document_task.h"
+#include "chrome/browser/shortcuts/document_icon_fetcher_task.h"
 #include "chrome/browser/shortcuts/image_test_utils.h"
 #include "chrome/browser/shortcuts/shortcut_icon_generator.h"
 #include "chrome/browser/ui/browser.h"
-#include "chrome/browser/ui/browser_commands.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "content/public/test/browser_test.h"
@@ -45,6 +42,16 @@
     return default_favicon_server_.GetURL("/index.html");
   }
 
+  FetchIconsFromDocumentResult RunIconFetchingTaskForCurrentWebContents() {
+    base::test::TestFuture<FetchIconsFromDocumentResult> future;
+    DocumentIconFetcherTask icon_fetcher_task(
+        *browser()->tab_strip_model()->GetActiveWebContents(),
+        future.GetCallback());
+    icon_fetcher_task.StartIconFetching();
+    EXPECT_TRUE(future.Wait());
+    return future.Get();
+  }
+
  private:
   net::EmbeddedTestServer default_favicon_server_{
       net::EmbeddedTestServer::TYPE_HTTPS};
@@ -54,14 +61,11 @@
   ASSERT_TRUE(ui_test_utils::NavigateToURL(
       browser(), embedded_https_test_server().GetURL(kPageNoIcons)));
 
-  base::test::TestFuture<FetchIconsFromDocumentResult> future;
-  DocumentIconFetcher::FetchIcons(
-      *browser()->tab_strip_model()->GetActiveWebContents(),
-      future.GetCallback());
-  ASSERT_TRUE(future.Wait());
-  EXPECT_TRUE(future.Get().has_value());
+  FetchIconsFromDocumentResult result =
+      RunIconFetchingTaskForCurrentWebContents();
+  ASSERT_TRUE(result.has_value());
   EXPECT_THAT(
-      future.Get().value(),
+      result.value(),
       testing::ElementsAre(gfx::test::EqualsBitmap(GenerateBitmap(128, U'P'))));
 }
 
@@ -69,14 +73,10 @@
   ASSERT_TRUE(ui_test_utils::NavigateToURL(
       browser(), embedded_https_test_server().GetURL(kPageWithIcons)));
 
-  base::test::TestFuture<FetchIconsFromDocumentResult> future;
-
-  DocumentIconFetcher::FetchIcons(
-      *browser()->tab_strip_model()->GetActiveWebContents(),
-      future.GetCallback());
-  ASSERT_TRUE(future.Wait());
-  ASSERT_TRUE(future.Get().has_value());
-  std::vector<SkBitmap> images = future.Get().value();
+  FetchIconsFromDocumentResult result =
+      RunIconFetchingTaskForCurrentWebContents();
+  ASSERT_TRUE(result.has_value());
+  std::vector<SkBitmap> images = result.value();
 
   SkBitmap expected_green;
   ASSERT_OK_AND_ASSIGN(expected_green,
@@ -95,13 +95,10 @@
   ASSERT_TRUE(
       ui_test_utils::NavigateToURL(browser(), GetPageWithDefaultFavicon()));
 
-  base::test::TestFuture<FetchIconsFromDocumentResult> future;
-  DocumentIconFetcher::FetchIcons(
-      *browser()->tab_strip_model()->GetActiveWebContents(),
-      future.GetCallback());
-  ASSERT_TRUE(future.Wait());
-  EXPECT_TRUE(future.Get().has_value());
-  std::vector<SkBitmap> images = future.Get().value();
+  FetchIconsFromDocumentResult result =
+      RunIconFetchingTaskForCurrentWebContents();
+  ASSERT_TRUE(result.has_value());
+  std::vector<SkBitmap> images = result.value();
   SkBitmap expected_16;
   ASSERT_OK_AND_ASSIGN(
       expected_16, LoadImageFromTestFile(base::FilePath(FILE_PATH_LITERAL(
@@ -115,19 +112,20 @@
                           gfx::test::EqualsBitmap(expected_32)));
 }
 
-IN_PROC_BROWSER_TEST_F(DocumentIconFetcherTest, WebContentsClosed) {
+IN_PROC_BROWSER_TEST_F(DocumentIconFetcherTest,
+                       TaskDestroyedDoesNotDropCallback) {
   base::test::TestFuture<FetchIconsFromDocumentResult> future;
-  chrome::NewTab(browser());
   ASSERT_TRUE(ui_test_utils::NavigateToURL(
       browser(), embedded_https_test_server().GetURL(kPageWithIcons)));
-  DocumentIconFetcher::FetchIcons(
+  auto icon_fetcher_task = std::make_unique<DocumentIconFetcherTask>(
       *browser()->tab_strip_model()->GetActiveWebContents(),
       future.GetCallback());
-  chrome::CloseTab(browser());
+
+  // Task destruction should not drop the callback.
+  icon_fetcher_task.reset();
   ASSERT_TRUE(future.Wait());
-  EXPECT_THAT(
-      future.Get(),
-      base::test::ErrorIs(FetchIconsForDocumentError::kDocumentDestroyed));
+  EXPECT_THAT(future.Get(),
+              base::test::ErrorIs(FetchIconsForDocumentError::kTaskDestroyed));
 }
 
 }  // namespace
diff --git a/chrome/browser/shortcuts/document_icon_fetcher_task.cc b/chrome/browser/shortcuts/document_icon_fetcher_task.cc
new file mode 100644
index 0000000..2296225
--- /dev/null
+++ b/chrome/browser/shortcuts/document_icon_fetcher_task.cc
@@ -0,0 +1,137 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/shortcuts/document_icon_fetcher_task.h"
+
+#include <iterator>
+#include <memory>
+#include <vector>
+
+#include "base/containers/flat_map.h"
+#include "base/containers/flat_set.h"
+#include "base/functional/bind.h"
+#include "base/functional/callback.h"
+#include "base/functional/callback_forward.h"
+#include "base/location.h"
+#include "base/memory/weak_ptr.h"
+#include "base/task/sequenced_task_runner.h"
+#include "base/types/expected.h"
+#include "chrome/browser/shortcuts/shortcut_icon_generator.h"
+#include "components/webapps/common/web_page_metadata.mojom.h"
+#include "components/webapps/common/web_page_metadata_agent.mojom.h"
+#include "content/public/browser/web_contents.h"
+#include "mojo/public/cpp/bindings/associated_remote.h"
+#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
+#include "third_party/blink/public/mojom/favicon/favicon_url.mojom.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "ui/gfx/geometry/size.h"
+
+namespace shortcuts {
+
+DocumentIconFetcherTask::DocumentIconFetcherTask(
+    content::WebContents& web_contents,
+    FetchIconsFromDocumentCallback callback)
+    : web_contents_(web_contents.GetWeakPtr()),
+      fallback_letter_(GenerateIconLetterFromName(web_contents.GetTitle())),
+      final_callback_(std::move(callback)) {}
+
+DocumentIconFetcherTask::~DocumentIconFetcherTask() {
+  // If the final_callback_ has not been run yet, prevent that from hanging by
+  // returning an error message. Although this function calls
+  // `OnIconFetchingCompleteSelfDestruct()`, for this use-case, self-destruction
+  // will not occur since this is being triggered as part of destruction itself.
+  if (!final_callback_.is_null()) {
+    OnIconFetchingCompleteSelfDestruct(
+        base::unexpected(FetchIconsForDocumentError::kTaskDestroyed));
+  }
+}
+
+void DocumentIconFetcherTask::StartIconFetching() {
+  mojo::AssociatedRemote<webapps::mojom::WebPageMetadataAgent> metadata_agent;
+  web_contents_->GetPrimaryMainFrame()
+      ->GetRemoteAssociatedInterfaces()
+      ->GetInterface(&metadata_agent);
+
+  // Set the error handler so that we can run abort this task if the WebContents
+  // or the RenderFrameHost are destroyed and the connection to
+  // ChromeRenderFrame is lost.
+  metadata_agent.set_disconnect_handler(
+      base::BindOnce(&DocumentIconFetcherTask::OnMetadataFetchError,
+                     weak_factory_.GetWeakPtr()));
+
+  // Bind the InterfacePtr into the callback so that it's kept alive
+  // until there's either a connection error or a response.
+  auto* web_page_metadata_proxy = metadata_agent.get();
+  web_page_metadata_proxy->GetWebPageMetadata(
+      base::BindOnce(&DocumentIconFetcherTask::OnWebPageMetadataObtained,
+                     weak_factory_.GetWeakPtr(), std::move(metadata_agent)));
+}
+
+void DocumentIconFetcherTask::OnMetadataFetchError() {
+  OnIconFetchingCompleteSelfDestruct(
+      base::unexpected(FetchIconsForDocumentError::kMetadataFetchFailed));
+}
+
+void DocumentIconFetcherTask::OnWebPageMetadataObtained(
+    mojo::AssociatedRemote<webapps::mojom::WebPageMetadataAgent> metadata_agent,
+    webapps::mojom::WebPageMetadataPtr web_page_metadata) {
+  metadata_fetch_complete_ = true;
+  std::vector<GURL> icons;
+  for (const auto& icon_info : web_page_metadata->icons) {
+    icons.push_back(icon_info->url);
+  }
+  for (const auto& favicon_url : web_contents_->GetFaviconURLs()) {
+    icons.push_back(favicon_url->icon_url);
+  }
+
+  // Eliminate duplicates.
+  base::flat_set<GURL> icon_set(std::move(icons));
+  num_pending_image_requests_ = icon_set.size();
+
+  for (const GURL& url : icon_set) {
+    web_contents_->DownloadImage(
+        url, /*is_favicon=*/true,
+        /*preferred_size=*/gfx::Size(),
+        /*max_bitmap_size=*/0, /*bypass_cache=*/false,
+        base::BindOnce(&DocumentIconFetcherTask::DidDownloadFavicon,
+                       weak_factory_.GetWeakPtr()));
+  }
+  MaybeCompleteImageDownloadAndSelfDestruct();
+}
+
+void DocumentIconFetcherTask::DidDownloadFavicon(
+    int id,
+    int http_status_code,
+    const GURL& image_url,
+    const std::vector<SkBitmap>& bitmaps,
+    const std::vector<gfx::Size>& sizes) {
+  icons_.reserve(icons_.size() + bitmaps.size());
+  for (const SkBitmap& bitmap : bitmaps) {
+    if (bitmap.drawsNothing()) {
+      continue;
+    }
+    icons_.push_back(bitmap);
+  }
+  --num_pending_image_requests_;
+  MaybeCompleteImageDownloadAndSelfDestruct();
+}
+
+void DocumentIconFetcherTask::MaybeCompleteImageDownloadAndSelfDestruct() {
+  if (!metadata_fetch_complete_ || num_pending_image_requests_ > 0) {
+    return;
+  }
+  OnIconFetchingCompleteSelfDestruct(base::ok(std::move(icons_)));
+}
+
+void DocumentIconFetcherTask::OnIconFetchingCompleteSelfDestruct(
+    FetchIconsFromDocumentResult result) {
+  if (result.has_value() && result->empty()) {
+    result->push_back(GenerateBitmap(128, fallback_letter_));
+  }
+
+  base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
+      FROM_HERE, base::BindOnce(std::move(final_callback_), result));
+}
+
+}  // namespace shortcuts
diff --git a/chrome/browser/shortcuts/document_icon_fetcher_task.h b/chrome/browser/shortcuts/document_icon_fetcher_task.h
new file mode 100644
index 0000000..741ad6ad
--- /dev/null
+++ b/chrome/browser/shortcuts/document_icon_fetcher_task.h
@@ -0,0 +1,87 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_SHORTCUTS_DOCUMENT_ICON_FETCHER_TASK_H_
+#define CHROME_BROWSER_SHORTCUTS_DOCUMENT_ICON_FETCHER_TASK_H_
+
+#include <memory>
+#include <vector>
+
+#include "base/containers/flat_map.h"
+#include "base/functional/callback_forward.h"
+#include "base/memory/weak_ptr.h"
+#include "base/types/expected.h"
+#include "components/webapps/common/web_page_metadata.mojom-forward.h"
+#include "components/webapps/common/web_page_metadata_agent.mojom-forward.h"
+#include "content/public/browser/document_user_data.h"
+#include "mojo/public/cpp/bindings/associated_remote.h"
+
+namespace content {
+class WebContents;
+}  // namespace content
+
+class GURL;
+class SkBitmap;
+
+namespace gfx {
+class Size;
+}
+
+namespace shortcuts {
+
+enum class FetchIconsForDocumentError { kTaskDestroyed, kMetadataFetchFailed };
+
+using FetchIconsFromDocumentResult =
+    base::expected<std::vector<SkBitmap>, FetchIconsForDocumentError>;
+
+using FetchIconsFromDocumentCallback =
+    base::OnceCallback<void(FetchIconsFromDocumentResult)>;
+
+// This object is responsible for fetching all available icons from a given
+// document.
+class DocumentIconFetcherTask {
+ public:
+  // Creates a task that fetches all icons for the top level primary frame of
+  // the given web contents. `callback` will always be called (even on document
+  // destruction), and always called asynchronously. If the callback is not
+  // called with an error, it is guaranteed to include at least one icon (i.e.
+  // it is not possible for fetching to succeed but not return any icons. If no
+  // icons were found, a fallback icon is generated).
+  DocumentIconFetcherTask(content::WebContents& web_contents,
+                          FetchIconsFromDocumentCallback callback);
+  ~DocumentIconFetcherTask();
+
+  void StartIconFetching();
+
+ private:
+  void OnMetadataFetchError();
+
+  void OnWebPageMetadataObtained(
+      mojo::AssociatedRemote<webapps::mojom::WebPageMetadataAgent>
+          metadata_agent,
+      webapps::mojom::WebPageMetadataPtr web_page_metadata);
+
+  void DidDownloadFavicon(int id,
+                          int http_status_code,
+                          const GURL& image_url,
+                          const std::vector<SkBitmap>& bitmaps,
+                          const std::vector<gfx::Size>& sizes);
+
+  void MaybeCompleteImageDownloadAndSelfDestruct();
+
+  void OnIconFetchingCompleteSelfDestruct(FetchIconsFromDocumentResult result);
+
+  base::WeakPtr<content::WebContents> web_contents_;
+  char32_t fallback_letter_;
+  FetchIconsFromDocumentCallback final_callback_;
+  bool metadata_fetch_complete_ = false;
+  int num_pending_image_requests_ = 0;
+  std::vector<SkBitmap> icons_;
+
+  base::WeakPtrFactory<DocumentIconFetcherTask> weak_factory_{this};
+};
+
+}  // namespace shortcuts
+
+#endif  // CHROME_BROWSER_SHORTCUTS_DOCUMENT_ICON_FETCHER_TASK_H_
diff --git a/chrome/browser/shortcuts/fetch_icons_from_document_task.cc b/chrome/browser/shortcuts/fetch_icons_from_document_task.cc
deleted file mode 100644
index 653a96e..0000000
--- a/chrome/browser/shortcuts/fetch_icons_from_document_task.cc
+++ /dev/null
@@ -1,124 +0,0 @@
-// Copyright 2024 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/shortcuts/fetch_icons_from_document_task.h"
-
-#include "base/functional/bind.h"
-#include "base/functional/callback.h"
-#include "base/location.h"
-#include "base/memory/raw_ref.h"
-#include "base/types/expected.h"
-#include "components/webapps/common/web_page_metadata.mojom.h"
-#include "components/webapps/common/web_page_metadata_agent.mojom.h"
-#include "content/public/browser/render_frame_host.h"
-#include "content/public/browser/web_contents.h"
-#include "mojo/public/cpp/bindings/associated_remote.h"
-#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
-#include "third_party/blink/public/mojom/favicon/favicon_url.mojom.h"
-#include "third_party/skia/include/core/SkBitmap.h"
-#include "ui/gfx/geometry/size.h"
-
-namespace shortcuts {
-
-FetchIconsFromDocumentTask::FetchIconsFromDocumentTask(
-    base::PassKey<DocumentIconFetcher>,
-    content::RenderFrameHost& rfh)
-    : frame_host_(rfh) {
-  CHECK(frame_host_->IsInPrimaryMainFrame());
-}
-
-FetchIconsFromDocumentTask::~FetchIconsFromDocumentTask() = default;
-
-void FetchIconsFromDocumentTask::Start(
-    FetchIconsFromDocumentCallback callback) {
-  callback_ = std::move(callback);
-  mojo::AssociatedRemote<webapps::mojom::WebPageMetadataAgent> metadata_agent;
-  frame_host_->GetRemoteAssociatedInterfaces()->GetInterface(&metadata_agent);
-
-  // Set the error handler so that we can run abort this task if the WebContents
-  // or the RenderFrameHost are destroyed and the connection to
-  // ChromeRenderFrame is lost.
-  metadata_agent.set_disconnect_handler(
-      base::BindOnce(&FetchIconsFromDocumentTask::OnMetadataFetchError,
-                     weak_factory_.GetWeakPtr()));
-
-  // Bind the InterfacePtr into the callback so that it's kept alive
-  // until there's either a connection error or a response.
-  auto* web_page_metadata_proxy = metadata_agent.get();
-  web_page_metadata_proxy->GetWebPageMetadata(
-      base::BindOnce(&FetchIconsFromDocumentTask::OnWebPageMetadataObtained,
-                     weak_factory_.GetWeakPtr(), std::move(metadata_agent)));
-}
-
-FetchIconsFromDocumentCallback FetchIconsFromDocumentTask::TakeCallback() {
-  return std::move(callback_);
-}
-
-void FetchIconsFromDocumentTask::OnWebPageMetadataObtained(
-    mojo::AssociatedRemote<webapps::mojom::WebPageMetadataAgent> metadata_agent,
-    webapps::mojom::WebPageMetadataPtr web_page_metadata) {
-  metadata_fetch_complete_ = true;
-  std::vector<GURL> icons;
-  for (const auto& icon_info : web_page_metadata->icons) {
-    icons.push_back(icon_info->url);
-  }
-  for (const auto& favicon_url : frame_host_->FaviconURLs()) {
-    icons.push_back(favicon_url->icon_url);
-  }
-
-  // Eliminate duplicates.
-  base::flat_set<GURL> icon_set(std::move(icons));
-  num_pending_image_requests_ = icon_set.size();
-
-  content::WebContents* web_contents =
-      content::WebContents::FromRenderFrameHost(&frame_host_.get());
-  for (const GURL& url : icon_set) {
-    web_contents->DownloadImageInFrame(
-        frame_host_->GetGlobalId(), url, /*is_favicon=*/true,
-        /*preferred_size=*/gfx::Size(),
-        /*max_bitmap_size=*/0, /*bypass_cache=*/false,
-        base::BindOnce(&FetchIconsFromDocumentTask::DidDownloadFavicon,
-                       weak_factory_.GetWeakPtr()));
-  }
-  MaybeCompleteImageDownloadAndSelfDestruct();
-}
-
-void FetchIconsFromDocumentTask::DidDownloadFavicon(
-    int id,
-    int http_status_code,
-    const GURL& image_url,
-    const std::vector<SkBitmap>& bitmaps,
-    const std::vector<gfx::Size>& sizes) {
-  icons_.reserve(icons_.size() + bitmaps.size());
-  for (const SkBitmap& bitmap : bitmaps) {
-    if (bitmap.drawsNothing()) {
-      continue;
-    }
-    icons_.push_back(bitmap);
-  }
-  --num_pending_image_requests_;
-  MaybeCompleteImageDownloadAndSelfDestruct();
-}
-
-void FetchIconsFromDocumentTask::MaybeCompleteImageDownloadAndSelfDestruct() {
-  if (!metadata_fetch_complete_ || num_pending_image_requests_ > 0) {
-    return;
-  }
-  OnCompleteSelfDestruct(base::ok(std::move(icons_)));
-}
-
-void FetchIconsFromDocumentTask::OnCompleteSelfDestruct(Result result,
-                                                        base::Location here) {
-  if (!callback_) {
-    return;
-  }
-  std::move(callback_).Run(std::move(result));
-}
-
-void FetchIconsFromDocumentTask::OnMetadataFetchError() {
-  OnCompleteSelfDestruct(
-      base::unexpected(FetchIconsForDocumentError::kMetadataFetchFailed));
-}
-
-}  // namespace shortcuts
diff --git a/chrome/browser/shortcuts/fetch_icons_from_document_task.h b/chrome/browser/shortcuts/fetch_icons_from_document_task.h
deleted file mode 100644
index 7ef5b9a..0000000
--- a/chrome/browser/shortcuts/fetch_icons_from_document_task.h
+++ /dev/null
@@ -1,101 +0,0 @@
-// Copyright 2024 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_SHORTCUTS_FETCH_ICONS_FROM_DOCUMENT_TASK_H_
-#define CHROME_BROWSER_SHORTCUTS_FETCH_ICONS_FROM_DOCUMENT_TASK_H_
-
-#include <vector>
-
-#include "base/functional/callback_forward.h"
-#include "base/memory/raw_ref.h"
-#include "base/memory/weak_ptr.h"
-#include "base/types/expected.h"
-#include "base/types/pass_key.h"
-#include "base/values.h"
-#include "components/webapps/common/web_page_metadata.mojom-forward.h"
-#include "components/webapps/common/web_page_metadata_agent.mojom-forward.h"
-#include "mojo/public/cpp/bindings/associated_remote.h"
-
-class SkBitmap;
-class GURL;
-
-namespace base {
-class Location;
-}
-
-namespace content {
-class RenderFrameHost;
-}
-
-namespace gfx {
-class Size;
-}
-
-namespace shortcuts {
-class DocumentIconFetcher;
-
-enum class FetchIconsForDocumentError {
-  kDocumentDestroyed,
-  kMetadataFetchFailed
-};
-using FetchIconsFromDocumentResult =
-    base::expected<std::vector<SkBitmap>, FetchIconsForDocumentError>;
-using FetchIconsFromDocumentCallback =
-    base::OnceCallback<void(FetchIconsFromDocumentResult)>;
-
-// Fetch all icons for the current RenderFrameHost document.
-// Invariants:
-// - The `frame_host_` provided to this class must be alive for the lifetime of
-//   this class.
-// - The `callback` may be called synchronously.
-// - The `callback` will be NOT be called on destruction. To handle that, the
-// user
-//   of this class must use the `TakeCallback()` method to extract the callback.
-class FetchIconsFromDocumentTask {
- public:
-  using Result = FetchIconsFromDocumentResult;
-
-  // `rfh` must be the primary main frame.
-  FetchIconsFromDocumentTask(base::PassKey<DocumentIconFetcher>,
-                             content::RenderFrameHost& rfh);
-  ~FetchIconsFromDocumentTask();
-
-  // The `callback` may be called synchronously.
-  void Start(FetchIconsFromDocumentCallback callback);
-
-  FetchIconsFromDocumentCallback TakeCallback();
-
- private:
-  void OnWebPageMetadataObtained(
-      mojo::AssociatedRemote<webapps::mojom::WebPageMetadataAgent>
-          metadata_agent,
-      webapps::mojom::WebPageMetadataPtr web_page_metadata);
-
-  void DownloadIcon(const GURL& url);
-
-  void DidDownloadFavicon(int id,
-                          int http_status_code,
-                          const GURL& image_url,
-                          const std::vector<SkBitmap>& bitmaps,
-                          const std::vector<gfx::Size>& sizes);
-
-  void MaybeCompleteImageDownloadAndSelfDestruct();
-
-  void OnCompleteSelfDestruct(Result result, base::Location here = FROM_HERE);
-
-  void OnMetadataFetchError();
-
-  base::raw_ref<content::RenderFrameHost> frame_host_;
-  FetchIconsFromDocumentCallback callback_;
-
-  bool metadata_fetch_complete_ = false;
-  int num_pending_image_requests_ = 0;
-  std::vector<SkBitmap> icons_;
-
-  base::WeakPtrFactory<FetchIconsFromDocumentTask> weak_factory_{this};
-};
-
-}  // namespace shortcuts
-
-#endif  // CHROME_BROWSER_SHORTCUTS_FETCH_ICONS_FROM_DOCUMENT_TASK_H_
diff --git a/chrome/browser/signin/dice_response_handler.cc b/chrome/browser/signin/dice_response_handler.cc
index 0fcb1cf..ea53cd6 100644
--- a/chrome/browser/signin/dice_response_handler.cc
+++ b/chrome/browser/signin/dice_response_handler.cc
@@ -551,27 +551,11 @@
   bool is_new_account =
       !identity_manager_->HasAccountWithRefreshToken(account_id);
 
-  // If this is a reauth, do not update the access point.
-  signin_metrics::AccessPoint access_point =
-      is_new_account ? token_fetcher->delegate()->GetAccessPoint()
-                     : signin_metrics::AccessPoint::ACCESS_POINT_UNKNOWN;
-  // Specifically set the token operation source in case the error was updated
-  // through a sign in from a password sign in promo, as this will indicate
-  // whether to move the password to account storage or not.
-  // TODO(crbug.com/339157240): Change the way this is implemented to not use
-  // SourceForRefreshTokenOperation as an indicator of the reauthentication
-  // source.
-  signin_metrics::SourceForRefreshTokenOperation token_operation_source =
-      token_fetcher->delegate()->GetAccessPoint() ==
-              signin_metrics::AccessPoint::ACCESS_POINT_PASSWORD_BUBBLE
-          ? signin_metrics::SourceForRefreshTokenOperation::
-                kDiceResponseHandler_PasswordPromoSignin
-          : signin_metrics::SourceForRefreshTokenOperation::
-                kDiceResponseHandler_Signin;
-
   identity_manager_->GetAccountsMutator()->AddOrUpdateAccount(
-      gaia_id, email, refresh_token, is_under_advanced_protection, access_point,
-      token_operation_source
+      gaia_id, email, refresh_token, is_under_advanced_protection,
+      token_fetcher->delegate()->GetAccessPoint(),
+      signin_metrics::SourceForRefreshTokenOperation::
+          kDiceResponseHandler_Signin
 #if BUILDFLAG(ENABLE_BOUND_SESSION_CREDENTIALS)
       ,
       wrapped_binding_key
diff --git a/chrome/browser/signin/signin_manager.cc b/chrome/browser/signin/signin_manager.cc
index 15748349..f92b84f 100644
--- a/chrome/browser/signin/signin_manager.cc
+++ b/chrome/browser/signin/signin_manager.cc
@@ -133,9 +133,9 @@
             signin::ConsentLevel::kSignin) != account) {
       DCHECK(
           !identity_manager_->HasPrimaryAccount(signin::ConsentLevel::kSync));
-      // The access point is the same as the access point that added the
-      // account. If it is unknown, report `ACCESS_POINT_DESKTOP_SIGNIN_MANAGER`
-      // instead.
+      // The access point is the point from where the last authentication
+      // happened, either through adding the account or a reauth. If it is
+      // unknown, report `ACCESS_POINT_DESKTOP_SIGNIN_MANAGER` instead.
       signin_metrics::AccessPoint access_point =
           identity_manager_->FindExtendedAccountInfo(account).access_point;
       if (access_point == signin_metrics::AccessPoint::ACCESS_POINT_UNKNOWN) {
diff --git a/chrome/browser/storage/shared_storage_browsertest.cc b/chrome/browser/storage/shared_storage_browsertest.cc
index 0183f27..a67939a 100644
--- a/chrome/browser/storage/shared_storage_browsertest.cc
+++ b/chrome/browser/storage/shared_storage_browsertest.cc
@@ -191,17 +191,17 @@
 }
 
 std::string GetSharedStorageDisabledErrorMessage() {
-  return base::StrCat({"a JavaScript error: \"Error: ",
+  return base::StrCat({"a JavaScript error: \"OperationError: ",
                        content::GetSharedStorageDisabledMessage()});
 }
 
 std::string GetSharedStorageSelectURLDisabledErrorMessage() {
-  return base::StrCat({"a JavaScript error: \"Error: ",
+  return base::StrCat({"a JavaScript error: \"OperationError: ",
                        content::GetSharedStorageSelectURLDisabledMessage()});
 }
 
 std::string GetSharedStorageAddModuleDisabledErrorMessage() {
-  return base::StrCat({"a JavaScript error: \"Error: ",
+  return base::StrCat({"a JavaScript error: \"OperationError: ",
                        content::GetSharedStorageAddModuleDisabledMessage()});
 }
 
@@ -738,7 +738,7 @@
   }
 
   std::string ExpectedSharedStorageDisabledMessage() {
-    return "Error: " + content::GetSharedStorageDisabledMessage();
+    return "OperationError: " + content::GetSharedStorageDisabledMessage();
   }
 
  protected:
@@ -2229,11 +2229,11 @@
       content::JsReplace("sharedStorage.worklet.addModule($1)", invalid_url));
 
   EXPECT_EQ(
-      base::StrCat(
-          {"a JavaScript error: \"Error: The module script url is invalid.\n",
-           "    at __const_std::string&_script__:1:24):\n",
-           "        {sharedStorage.worklet.addModule(\"", invalid_url, "\")\n",
-           "                               ^^^^^\n"}),
+      base::StrCat({"a JavaScript error: \"DataError: The module script url is "
+                    "invalid.\n",
+                    "    at __const_std::string&_script__:1:24):\n",
+                    "        {sharedStorage.worklet.addModule(\"", invalid_url,
+                    "\")\n", "                               ^^^^^\n"}),
       result.error);
 
   WaitForHistograms({kErrorTypeHistogram});
@@ -2253,7 +2253,7 @@
       content::JsReplace("sharedStorage.worklet.addModule($1)", script_url));
 
   EXPECT_EQ(
-      base::StrCat({"a JavaScript error: \"Error: Only same origin module ",
+      base::StrCat({"a JavaScript error: \"DataError: Only same origin module ",
                     "script is allowed.",
                     "\n    at __const_std::string&_script__:1:24):\n        ",
                     "{sharedStorage.worklet.addModule(\"",
@@ -2278,7 +2278,7 @@
       content::JsReplace("sharedStorage.worklet.addModule($1)", script_url));
 
   EXPECT_EQ(
-      base::StrCat({"a JavaScript error: \"Error: Failed to load ",
+      base::StrCat({"a JavaScript error: \"OperationError: Failed to load ",
                     script_url.spec(), " HTTP status = 404 Not Found.\"\n"}),
       result.error);
 
@@ -2299,8 +2299,9 @@
       content::JsReplace("sharedStorage.worklet.addModule($1)", script_url));
 
   EXPECT_EQ(
-      base::StrCat({"a JavaScript error: \"Error: Unexpected redirect on ",
-                    script_url.spec(), ".\"\n"}),
+      base::StrCat(
+          {"a JavaScript error: \"OperationError: Unexpected redirect on ",
+           script_url.spec(), ".\"\n"}),
       result.error);
 
   WaitForHistograms({kErrorTypeHistogram});
diff --git a/chrome/browser/sync/prefs/chrome_syncable_prefs_database.cc b/chrome/browser/sync/prefs/chrome_syncable_prefs_database.cc
index c261212..255c53b5 100644
--- a/chrome/browser/sync/prefs/chrome_syncable_prefs_database.cc
+++ b/chrome/browser/sync/prefs/chrome_syncable_prefs_database.cc
@@ -338,6 +338,7 @@
   kKeyboardDefaultSplitModifierSettings = 100279,
   kDisplayAmbientLightSensorLastEnabled = 100280,
   kAccessibilityMainNodeAnnotationsEnabled = 100281,
+  kSyncableVersionedWallpaperInfo = 100282,
   // See components/sync_preferences/README.md about adding new entries here.
   // vvvvv IMPORTANT! vvvvv
   // Note to the reviewer: IT IS YOUR RESPONSIBILITY to ensure that new syncable
@@ -1137,6 +1138,10 @@
      {syncable_prefs_ids::kDisplayAmbientLightSensorLastEnabled,
       syncer::OS_PREFERENCES, sync_preferences::PrefSensitivity::kNone,
       sync_preferences::MergeBehavior::kNone}},
+    {ash::prefs::kSyncableVersionedWallpaperInfo,
+     {syncable_prefs_ids::kSyncableVersionedWallpaperInfo,
+      syncer::OS_PREFERENCES, sync_preferences::PrefSensitivity::kNone,
+      sync_preferences::MergeBehavior::kNone}},
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
     {performance_manager::user_tuning::prefs::kTabDiscardingExceptions,
      {syncable_prefs_ids::kTabDiscardingExceptions, syncer::PREFERENCES,
diff --git a/chrome/browser/touch_to_fill/autofill/android/touch_to_fill_delegate_android_impl.cc b/chrome/browser/touch_to_fill/autofill/android/touch_to_fill_delegate_android_impl.cc
index b90a335..d2e0c1d 100644
--- a/chrome/browser/touch_to_fill/autofill/android/touch_to_fill_delegate_android_impl.cc
+++ b/chrome/browser/touch_to_fill/autofill/android/touch_to_fill_delegate_android_impl.cc
@@ -247,15 +247,7 @@
 // TODO(crbug.com/40233391): Create a central point for TTF hiding decision.
 void TouchToFillDelegateAndroidImpl::HideTouchToFill() {
   if (IsShowingTouchToFill()) {
-    // TODO(crbug.com/40257323): This is to prevent calling virtual functions in
-    // destructors in the following call chain:
-    //       ~ContentAutofillDriver()
-    //   --> ~BrowserAutofillManager()
-    //   --> ~TouchToFillDelegateAndroidImpl()
-    //   --> HideTouchToFill()
-    //   --> AutofillManager::safe_client()
-    //   --> ContentAutofillDriver::IsPrerendering()
-    manager_->unsafe_client(/*pass_key=*/{}).HideTouchToFillCreditCard();
+    manager_->client().HideTouchToFillCreditCard();
   }
 }
 
diff --git a/chrome/browser/tpcd/metadata/manager_browsertest.cc b/chrome/browser/tpcd/metadata/manager_browsertest.cc
index e6fe97c..a46d002 100644
--- a/chrome/browser/tpcd/metadata/manager_browsertest.cc
+++ b/chrome/browser/tpcd/metadata/manager_browsertest.cc
@@ -326,26 +326,19 @@
 
   Metadata metadata;
 
-  const auto* dtrp_eligible_source = Parser::kSource1pDt;
-  EXPECT_TRUE(
-      Parser::IsDtrpEligible(Parser::ToRuleSource(dtrp_eligible_source)));
-
   const uint32_t dtrp_guarantees_grace_period_forced_on = 0;
   tpcd::metadata::helpers::AddEntryToMetadata(
       metadata, primary_pattern_spec_1, secondary_pattern_spec,
-      dtrp_eligible_source, dtrp_guarantees_grace_period_forced_on);
+      Parser::kSource1pDt, dtrp_guarantees_grace_period_forced_on);
 
   const uint32_t dtrp_guarantees_grace_period_forced_off = 100;
   tpcd::metadata::helpers::AddEntryToMetadata(
       metadata, primary_pattern_spec_2, secondary_pattern_spec,
-      dtrp_eligible_source, dtrp_guarantees_grace_period_forced_off);
+      Parser::kSource1pDt, dtrp_guarantees_grace_period_forced_off);
 
-  const auto* dtrp_ineligible_source = Parser::kSourceCriticalSector;
-  EXPECT_FALSE(
-      Parser::IsDtrpEligible(Parser::ToRuleSource(dtrp_ineligible_source)));
   tpcd::metadata::helpers::AddEntryToMetadata(metadata, primary_pattern_spec_3,
                                               secondary_pattern_spec,
-                                              dtrp_ineligible_source);
+                                              Parser::kSourceCriticalSector);
 
   EXPECT_EQ(GetCookieSettings()->GetTpcdMetadataGrants().size(), 0u);
   MockComponentInstallation(metadata);
@@ -432,26 +425,19 @@
 
   Metadata metadata;
 
-  const auto* dtrp_eligible_source = Parser::kSource1pDt;
-  EXPECT_TRUE(
-      Parser::IsDtrpEligible(Parser::ToRuleSource(dtrp_eligible_source)));
-
   const uint32_t dtrp_guarantees_grace_period_forced_on = 0;
   tpcd::metadata::helpers::AddEntryToMetadata(
       metadata, primary_pattern_spec_1, secondary_pattern_spec,
-      dtrp_eligible_source, dtrp_guarantees_grace_period_forced_on);
+      Parser::kSource1pDt, dtrp_guarantees_grace_period_forced_on);
 
   const uint32_t dtrp_guarantees_grace_period_forced_off = 100;
   tpcd::metadata::helpers::AddEntryToMetadata(
       metadata, primary_pattern_spec_2, secondary_pattern_spec,
-      dtrp_eligible_source, dtrp_guarantees_grace_period_forced_off);
+      Parser::kSource1pDt, dtrp_guarantees_grace_period_forced_off);
 
-  const auto* dtrp_ineligible_source = Parser::kSourceCriticalSector;
-  EXPECT_FALSE(
-      Parser::IsDtrpEligible(Parser::ToRuleSource(dtrp_ineligible_source)));
   tpcd::metadata::helpers::AddEntryToMetadata(metadata, primary_pattern_spec_3,
                                               secondary_pattern_spec,
-                                              dtrp_ineligible_source);
+                                              Parser::kSourceCriticalSector);
 
   EXPECT_EQ(GetCookieSettings()->GetTpcdMetadataGrants().size(), 0u);
   MockComponentInstallation(metadata);
diff --git a/chrome/browser/trusted_vault/trusted_vault_service_factory.cc b/chrome/browser/trusted_vault/trusted_vault_service_factory.cc
index f34443d..5a24f1d 100644
--- a/chrome/browser/trusted_vault/trusted_vault_service_factory.cc
+++ b/chrome/browser/trusted_vault/trusted_vault_service_factory.cc
@@ -33,6 +33,7 @@
 #endif
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
+#include "base/check_is_test.h"
 #include "chrome/browser/browser_process.h"
 #include "chromeos/ash/components/browser_context_helper/browser_context_helper.h"
 #include "components/trusted_vault/recovery_key_provider_ash.h"
@@ -103,23 +104,30 @@
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 std::unique_ptr<trusted_vault::TrustedVaultClient>
 CreatePasskeyTrustedVaultClient(Profile* profile) {
-  AccountId account_id = ash::BrowserContextHelper::Get()
-                             ->GetUserByBrowserContext(profile)
-                             ->GetAccountId();
-  std::string device_id =
-      user_manager::KnownUser(g_browser_process->local_state())
-          .GetDeviceId(account_id);
-
+  std::unique_ptr<trusted_vault::RecoveryKeyProviderAsh> recovery_key_provider;
+  const user_manager::User* user =
+      ash::BrowserContextHelper::Get()->GetUserByBrowserContext(profile);
+  // `user` may be nullptr in tests.
+  if (user) {
+    AccountId account_id = user->GetAccountId();
+    std::string device_id =
+        user_manager::KnownUser(g_browser_process->local_state())
+            .GetDeviceId(account_id);
+    recovery_key_provider = std::make_unique<
+        trusted_vault::RecoveryKeyProviderAsh>(
+        /*user_data_auth_client_task_runner=*/content::GetUIThreadTaskRunner(
+            {}),
+        std::move(account_id), std::move(device_id));
+  } else {
+    CHECK_IS_TEST();
+  }
   return std::make_unique<trusted_vault::StandaloneTrustedVaultClient>(
       trusted_vault::SecurityDomainId::kPasskeys,
       /*base_dir=*/profile->GetPath(),
       IdentityManagerFactory::GetForProfile(profile),
       profile->GetDefaultStoragePartition()
           ->GetURLLoaderFactoryForBrowserProcess(),
-      std::make_unique<trusted_vault::RecoveryKeyProviderAsh>(
-          /*user_data_auth_client_task_runner=*/content::GetUIThreadTaskRunner(
-              {}),
-          std::move(account_id), std::move(device_id)));
+      std::move(recovery_key_provider));
 }
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index c0b1f254..5260671 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -3007,6 +3007,8 @@
       "settings_window_manager_observer_chromeos.h",
       "views/apps/app_dialog/app_block_dialog_view.cc",
       "views/apps/app_dialog/app_block_dialog_view.h",
+      "views/apps/app_dialog/app_local_block_dialog_view.cc",
+      "views/apps/app_dialog/app_local_block_dialog_view.h",
       "views/apps/app_dialog/app_pause_dialog_view.cc",
       "views/apps/app_dialog/app_pause_dialog_view.h",
       "views/apps/app_info_dialog/arc_app_info_links_panel.cc",
@@ -3304,8 +3306,6 @@
       "webui/ash/login/fingerprint_setup_screen_handler.h",
       "webui/ash/login/gaia_info_screen_handler.cc",
       "webui/ash/login/gaia_info_screen_handler.h",
-      "webui/ash/login/gaia_password_changed_screen_handler.cc",
-      "webui/ash/login/gaia_password_changed_screen_handler.h",
       "webui/ash/login/gaia_screen_handler.cc",
       "webui/ash/login/gaia_screen_handler.h",
       "webui/ash/login/gesture_navigation_screen_handler.cc",
diff --git a/chrome/browser/ui/android/fakepdf/java/src/org/chromium/chrome/browser/fakepdf/PdfDocumentRequest.java b/chrome/browser/ui/android/fakepdf/java/src/org/chromium/chrome/browser/fakepdf/PdfDocumentRequest.java
index 29c8c392..4f8718f 100644
--- a/chrome/browser/ui/android/fakepdf/java/src/org/chromium/chrome/browser/fakepdf/PdfDocumentRequest.java
+++ b/chrome/browser/ui/android/fakepdf/java/src/org/chromium/chrome/browser/fakepdf/PdfDocumentRequest.java
@@ -5,6 +5,7 @@
 package org.chromium.chrome.browser.fakepdf;
 
 import android.net.Uri;
+import android.os.ParcelFileDescriptor;
 
 import androidx.annotation.NonNull;
 import androidx.core.util.Preconditions;
@@ -15,11 +16,13 @@
 public class PdfDocumentRequest {
     private final Uri mUri;
     private final File mFile;
+    private final ParcelFileDescriptor mPfd;
 
     public PdfDocumentRequest(@NonNull Builder builder) {
         Preconditions.checkNotNull(builder);
         this.mUri = builder.mUri;
         this.mFile = builder.mFile;
+        this.mPfd = builder.mPfd;
     }
 
     public Uri getUri() {
@@ -30,9 +33,14 @@
         return this.mFile;
     }
 
+    public ParcelFileDescriptor getParcelFileDescriptor() {
+        return this.mPfd;
+    }
+
     public static class Builder {
         private Uri mUri;
         private File mFile;
+        private ParcelFileDescriptor mPfd;
         private PdfViewSettings mPdfViewSettings;
 
         public Builder() {}
@@ -50,6 +58,12 @@
         }
 
         @NonNull
+        public Builder setPfd(ParcelFileDescriptor pfd) {
+            this.mPfd = pfd;
+            return this;
+        }
+
+        @NonNull
         public Builder setPdfViewSettings(PdfViewSettings settings) {
             this.mPdfViewSettings = settings;
             return this;
diff --git a/chrome/browser/ui/android/google_bottom_bar/BUILD.gn b/chrome/browser/ui/android/google_bottom_bar/BUILD.gn
index ebd2dbf0..c11e0de8 100644
--- a/chrome/browser/ui/android/google_bottom_bar/BUILD.gn
+++ b/chrome/browser/ui/android/google_bottom_bar/BUILD.gn
@@ -38,6 +38,7 @@
 android_resources("java_resources") {
   sources = [
     "java/res/drawable/bookmark.xml",
+    "java/res/drawable/bottom_bar_page_insights_icon.xml",
     "java/res/layout/google_bottom_bar_even.xml",
     "java/res/layout/google_bottom_bar_spotlight.xml",
     "java/res/values/colors.xml",
diff --git a/chrome/browser/ui/android/google_bottom_bar/java/res/drawable/bottom_bar_page_insights_icon.xml b/chrome/browser/ui/android/google_bottom_bar/java/res/drawable/bottom_bar_page_insights_icon.xml
new file mode 100644
index 0000000..207b2e1
--- /dev/null
+++ b/chrome/browser/ui/android/google_bottom_bar/java/res/drawable/bottom_bar_page_insights_icon.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+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.
+-->
+<!--This is just a temporary copy of chrome/browser/ui/android/page_insights/java/res/drawable/page_insights_icon.xml -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M12.8909 10.99H11.2559V17.065H12.8909V10.99Z"
+      android:fillColor="#4285F4"/>
+  <path
+      android:pathData="M12.0009 9.2299C12.2609 9.2299 12.4759 9.1449 12.6559 8.9749C12.8309 8.8049 12.9209 8.5899 12.9209 8.3399C12.9209 8.0699 12.8309 7.8449 12.6559 7.6649C12.4809 7.4849 12.2609 7.3999 12.0009 7.3999C11.7359 7.3999 11.5159 7.4899 11.3459 7.6649C11.1709 7.8449 11.0859 8.0649 11.0859 8.3349C11.0859 8.5899 11.1759 8.8049 11.3509 8.9749C11.5209 9.1399 11.7409 9.2299 12.0009 9.2299Z"
+      android:fillColor="#4285F4"/>
+  <path
+      android:pathData="M18.3491 10.6749C18.3491 9.22992 17.8441 8.00492 16.8391 6.99992C15.8341 5.99492 14.6041 5.48992 13.1641 5.48992C14.6091 5.48992 15.8341 4.98492 16.8391 3.97992C17.8441 2.97492 18.3491 1.74492 18.3491 0.294922C18.3491 1.74492 18.8541 2.96992 19.8591 3.97992C20.8641 4.98492 22.0941 5.48992 23.5441 5.48992C22.0941 5.48992 20.8691 5.99492 19.8591 6.99992C18.8541 8.00492 18.3491 9.22992 18.3491 10.6749Z"
+      android:fillColor="#4285F4"/>
+  <path
+      android:pathData="M6.0107 5.99007C7.6607 4.35007 9.6557 3.53007 11.9957 3.53007C12.7557 3.53007 13.4807 3.61507 14.1657 3.78507L15.5007 2.43007C14.4007 2.02507 13.2407 1.82007 12.0107 1.82007C10.6007 1.82007 9.2807 2.08507 8.0507 2.62007C6.8157 3.15507 5.7357 3.88007 4.8157 4.79507C4.1957 5.41007 3.6657 6.11007 3.2207 6.87007L4.6707 7.71507C5.0307 7.10507 5.4707 6.52507 6.0107 5.99007Z"
+      android:fillColor="#EA4335"/>
+  <path
+      android:pathData="M21.3703 15.945C21.9053 14.71 22.1703 13.39 22.1703 11.985C22.1703 10.755 21.9653 9.58999 21.5603 8.48999L20.2103 9.83999C20.3753 10.515 20.4603 11.23 20.4603 11.98C20.4603 13.575 20.0753 15.005 19.3203 16.275L20.7703 17.12C20.9853 16.745 21.1953 16.355 21.3703 15.945Z"
+      android:fillColor="#4285F4"/>
+  <path
+      android:pathData="M17.9955 17.9799C16.3505 19.63 14.3555 20.455 12.0105 20.455C9.66055 20.455 7.66055 19.63 6.01055 17.9799C5.45555 17.4249 5.00555 16.83 4.64055 16.2L3.18555 17.03C3.63555 17.82 4.17555 18.5349 4.81555 19.1749C5.74055 20.0999 6.81555 20.825 8.05055 21.36C9.28555 21.89 10.6005 22.16 12.0105 22.16C13.4155 22.16 14.7355 21.8899 15.9755 21.3549C17.2105 20.82 18.2855 20.09 19.2055 19.17C19.8155 18.555 20.3355 17.865 20.7755 17.115L19.3255 16.27C18.9605 16.88 18.5255 17.4499 17.9955 17.9799Z"
+      android:fillColor="#34A853"/>
+  <path
+      android:pathData="M3.53422 11.9951C3.53422 10.4051 3.91922 8.98012 4.66422 7.71512L3.21422 6.87012C2.99922 7.24012 2.79922 7.61512 2.62422 8.02012C2.09422 9.25512 1.82422 10.5751 1.82422 11.9801C1.82422 13.3851 2.08922 14.7101 2.62422 15.9401C2.78922 16.3201 2.97922 16.6801 3.17922 17.0301L4.63422 16.2001C3.90922 14.9501 3.53422 13.5501 3.53422 11.9951Z"
+      android:fillColor="#FBBC04"/>
+</vector>
diff --git a/chrome/browser/ui/android/google_bottom_bar/java/res/layout/google_bottom_bar_even.xml b/chrome/browser/ui/android/google_bottom_bar/java/res/layout/google_bottom_bar_even.xml
index 8217be5e..663d134 100644
--- a/chrome/browser/ui/android/google_bottom_bar/java/res/layout/google_bottom_bar_even.xml
+++ b/chrome/browser/ui/android/google_bottom_bar/java/res/layout/google_bottom_bar_even.xml
@@ -17,7 +17,7 @@
     android:orientation="horizontal">
   <ImageButton
       android:id="@+id/google_bottom_bar_save_button"
-      android:layout_width="@dimen/google_bottom_bar_button_size"
+      android:layout_width="0dp"
       android:layout_weight="1"
       android:layout_height="@dimen/google_bottom_bar_button_size"
       android:layout_marginTop="@dimen/google_bottom_bar_button_margin_vertical"
@@ -26,11 +26,14 @@
       android:layout_gravity="center_vertical"
       android:importantForAccessibility="yes"
       android:contentDescription="@null"
-      android:src="@drawable/bookmark"
-      android:background="@color/google_bottom_bar_background_color" />
+      android:padding="@dimen/google_bottom_bar_button_padding"
+      android:clickable="true"
+      android:focusable="true"
+      android:background="?attr/selectableItemBackground"
+      android:src="@drawable/bookmark" />
   <ImageButton
       android:id="@+id/google_bottom_bar_page_insights_button"
-      android:layout_width="@dimen/google_bottom_bar_button_size"
+      android:layout_width="0dp"
       android:layout_weight="1"
       android:layout_height="@dimen/google_bottom_bar_button_size"
       android:layout_marginTop="@dimen/google_bottom_bar_button_margin_vertical"
@@ -40,11 +43,14 @@
       android:importantForAccessibility="yes"
       android:contentDescription="@null"
       android:visibility="visible"
-      android:src="@drawable/page_insights_icon"
-      android:background="@color/google_bottom_bar_background_color" />
+      android:padding="@dimen/google_bottom_bar_button_padding"
+      android:clickable="true"
+      android:focusable="true"
+      android:background="?attr/selectableItemBackground"
+      android:src="@drawable/bottom_bar_page_insights_icon" />
   <ImageButton
       android:id="@+id/google_bottom_bar_share_button"
-      android:layout_width="@dimen/google_bottom_bar_button_size"
+      android:layout_width="0dp"
       android:layout_weight="1"
       android:layout_height="@dimen/google_bottom_bar_button_size"
       android:layout_marginTop="@dimen/google_bottom_bar_button_margin_vertical"
@@ -54,6 +60,9 @@
       android:importantForAccessibility="yes"
       android:contentDescription="@null"
       android:visibility="visible"
-      android:src="@drawable/ic_share_white_24dp"
-      android:background="@color/google_bottom_bar_background_color" />
+      android:padding="@dimen/google_bottom_bar_button_padding"
+      android:clickable="true"
+      android:focusable="true"
+      android:background="?attr/selectableItemBackground"
+      android:src="@drawable/ic_share_white_24dp" />
 </LinearLayout>
diff --git a/chrome/browser/ui/android/google_bottom_bar/java/res/layout/google_bottom_bar_spotlight.xml b/chrome/browser/ui/android/google_bottom_bar/java/res/layout/google_bottom_bar_spotlight.xml
index ca2d8e5e..564a9594 100644
--- a/chrome/browser/ui/android/google_bottom_bar/java/res/layout/google_bottom_bar_spotlight.xml
+++ b/chrome/browser/ui/android/google_bottom_bar/java/res/layout/google_bottom_bar_spotlight.xml
@@ -26,8 +26,11 @@
       android:importantForAccessibility="yes"
       android:contentDescription="@null"
       android:visibility="visible"
-      android:src="@drawable/page_insights_icon"
-      android:background="@color/google_bottom_bar_background_color" />
+      android:padding="@dimen/google_bottom_bar_button_padding"
+      android:clickable="true"
+      android:focusable="true"
+      android:background="?attr/selectableItemBackground"
+      android:src="@drawable/bottom_bar_page_insights_icon" />
   <LinearLayout
       android:id="@+id/google_bottom_bar_non_spotlit_buttons_container"
       android:layout_width="0dp"
@@ -47,8 +50,11 @@
         android:layout_marginStart="@dimen/google_bottom_bar_button_padding"
         android:importantForAccessibility="yes"
         android:contentDescription="@null"
-        android:src="@drawable/bookmark"
-        android:background="@color/google_bottom_bar_background_color" />
+        android:padding="@dimen/google_bottom_bar_button_padding"
+        android:clickable="true"
+        android:focusable="true"
+        android:background="?attr/selectableItemBackground"
+        android:src="@drawable/bookmark" />
     <ImageButton
         android:id="@+id/google_bottom_bar_share_button"
         android:layout_width="@dimen/google_bottom_bar_button_size"
@@ -59,7 +65,10 @@
         android:importantForAccessibility="yes"
         android:contentDescription="@null"
         android:visibility="visible"
-        android:src="@drawable/ic_share_white_24dp"
-        android:background="@color/google_bottom_bar_background_color" />
+        android:padding="@dimen/google_bottom_bar_button_padding"
+        android:clickable="true"
+        android:focusable="true"
+        android:background="?attr/selectableItemBackground"
+        android:src="@drawable/ic_share_white_24dp" />
   </LinearLayout>
 </LinearLayout>
diff --git a/chrome/browser/ui/android/google_bottom_bar/java/res/values/dimens.xml b/chrome/browser/ui/android/google_bottom_bar/java/res/values/dimens.xml
index ad2c73d..f3a1862e6 100644
--- a/chrome/browser/ui/android/google_bottom_bar/java/res/values/dimens.xml
+++ b/chrome/browser/ui/android/google_bottom_bar/java/res/values/dimens.xml
@@ -11,6 +11,5 @@
   <dimen name="google_bottom_bar_expanded_button_padding_horizontal">10dp</dimen>
   <dimen name="google_bottom_bar_button_padding">12dp</dimen>
   <dimen name="google_bottom_bar_button_size">48dp</dimen>
-  <dimen name="google_bottom_bar_button_image_size">24dp</dimen>
   <dimen name="google_bottom_bar_button_margin_vertical">8dp</dimen>
 </resources>
\ No newline at end of file
diff --git a/chrome/browser/ui/android/google_bottom_bar/java/src/org/chromium/chrome/browser/ui/google_bottom_bar/BottomBarConfigCreator.java b/chrome/browser/ui/android/google_bottom_bar/java/src/org/chromium/chrome/browser/ui/google_bottom_bar/BottomBarConfigCreator.java
index 604e970..efbdd70 100644
--- a/chrome/browser/ui/android/google_bottom_bar/java/src/org/chromium/chrome/browser/ui/google_bottom_bar/BottomBarConfigCreator.java
+++ b/chrome/browser/ui/android/google_bottom_bar/java/src/org/chromium/chrome/browser/ui/google_bottom_bar/BottomBarConfigCreator.java
@@ -4,9 +4,6 @@
 package org.chromium.chrome.browser.ui.google_bottom_bar;
 
 import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 
 import androidx.annotation.IntDef;
@@ -189,7 +186,7 @@
                         id,
                         UiUtils.getTintedDrawable(
                                 mContext,
-                                R.drawable.page_insights_icon,
+                                R.drawable.bottom_bar_page_insights_icon,
                                 R.color.default_icon_color_baseline),
                         mContext.getString(
                                 R.string.google_bottom_bar_page_insights_button_description),
@@ -263,25 +260,13 @@
         return code > 0 && code <= ButtonId.MAX_BUTTON_ID;
     }
 
-    private static Drawable getScaledAndTintedIcon(
-            Context context, Drawable drawable, int tintColorId) {
-        if (drawable instanceof BitmapDrawable) {
-            Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();
-            Resources resource = context.getResources();
-            int dimen = resource.getDimensionPixelSize(R.dimen.google_bottom_bar_button_image_size);
-            BitmapDrawable bitmapDrawable =
-                    new BitmapDrawable(
-                            context.getResources(),
-                            Bitmap.createScaledBitmap(bitmap, dimen, dimen, true));
-            bitmapDrawable.setTint(context.getColor(tintColorId));
-            return bitmapDrawable;
-        }
+    private static Drawable getTintedIcon(Context context, Drawable drawable, int tintColorId) {
         drawable.setTint(context.getColor(tintColorId));
         return drawable;
     }
 
     private static ButtonConfig getButtonConfigFromCustomButtonParams(
-            Context context, @ButtonId int buttonId, CustomButtonParams params) {
+            Context context, int buttonId, CustomButtonParams params) {
         return new ButtonConfig(
                 buttonId,
                 getIconDrawable(context, buttonId, params),
@@ -294,8 +279,10 @@
         // Always use pageInsights icon provided by Chrome
         return isPageInsightsButton(buttonId)
                 ? UiUtils.getTintedDrawable(
-                        context, R.drawable.page_insights_icon, R.color.default_icon_color_baseline)
-                : getScaledAndTintedIcon(
+                        context,
+                        R.drawable.bottom_bar_page_insights_icon,
+                        R.color.default_icon_color_baseline)
+                : getTintedIcon(
                         context, params.getIcon(context), R.color.default_icon_color_baseline);
     }
 
diff --git a/chrome/browser/ui/android/google_bottom_bar/java/src/org/chromium/chrome/browser/ui/google_bottom_bar/BottomBarConfigCreatorTest.java b/chrome/browser/ui/android/google_bottom_bar/java/src/org/chromium/chrome/browser/ui/google_bottom_bar/BottomBarConfigCreatorTest.java
index 8cf815b1..4b5e5fd 100644
--- a/chrome/browser/ui/android/google_bottom_bar/java/src/org/chromium/chrome/browser/ui/google_bottom_bar/BottomBarConfigCreatorTest.java
+++ b/chrome/browser/ui/android/google_bottom_bar/java/src/org/chromium/chrome/browser/ui/google_bottom_bar/BottomBarConfigCreatorTest.java
@@ -175,7 +175,7 @@
                 drawableToBitmap(
                         UiUtils.getTintedDrawable(
                                 mContext,
-                                R.drawable.page_insights_icon,
+                                R.drawable.bottom_bar_page_insights_icon,
                                 R.color.default_icon_color_baseline));
         Bitmap actualBitmap = drawableToBitmap(buttonConfig.getButtonList().get(0).getIcon());
         // the button has the expected custom button params set
diff --git a/chrome/browser/ui/android/management/java/res/layout/enterprise_management.xml b/chrome/browser/ui/android/management/java/res/layout/enterprise_management.xml
index 12439d2..0e73760 100644
--- a/chrome/browser/ui/android/management/java/res/layout/enterprise_management.xml
+++ b/chrome/browser/ui/android/management/java/res/layout/enterprise_management.xml
@@ -37,7 +37,6 @@
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_marginTop="@dimen/cm_description_margin_top"
-            android:text="@string/management_browser_notice"
             android:textAppearance="@style/TextAppearance.TextMedium.Secondary" />
 
         <TextView
diff --git a/chrome/browser/ui/android/management/java/src/org/chromium/chrome/browser/management/ManagementMediator.java b/chrome/browser/ui/android/management/java/src/org/chromium/chrome/browser/management/ManagementMediator.java
index a98cb71c..7e833c80 100644
--- a/chrome/browser/ui/android/management/java/src/org/chromium/chrome/browser/management/ManagementMediator.java
+++ b/chrome/browser/ui/android/management/java/src/org/chromium/chrome/browser/management/ManagementMediator.java
@@ -36,6 +36,9 @@
                         .with(
                                 ManagementProperties.BROWSER_IS_MANAGED,
                                 ManagedBrowserUtils.isBrowserManaged(profile))
+                        .with(
+                                ManagementProperties.PROFILE_IS_MANAGED,
+                                ManagedBrowserUtils.isProfileManaged(profile))
                         .with(ManagementProperties.TITLE, ManagedBrowserUtils.getTitle(profile))
                         .with(ManagementProperties.LEARN_MORE_TEXT, getLearnMoreClickableText())
                         .with(
diff --git a/chrome/browser/ui/android/management/java/src/org/chromium/chrome/browser/management/ManagementProperties.java b/chrome/browser/ui/android/management/java/src/org/chromium/chrome/browser/management/ManagementProperties.java
index 64fc2a0..1852332 100644
--- a/chrome/browser/ui/android/management/java/src/org/chromium/chrome/browser/management/ManagementProperties.java
+++ b/chrome/browser/ui/android/management/java/src/org/chromium/chrome/browser/management/ManagementProperties.java
@@ -17,6 +17,9 @@
     public static final PropertyModel.WritableBooleanPropertyKey BROWSER_IS_MANAGED =
             new PropertyModel.WritableBooleanPropertyKey();
 
+    public static final PropertyModel.WritableBooleanPropertyKey PROFILE_IS_MANAGED =
+            new PropertyModel.WritableBooleanPropertyKey();
+
     public static final PropertyModel.WritableObjectPropertyKey<SpannableString> LEARN_MORE_TEXT =
             new PropertyModel.WritableObjectPropertyKey<>();
 
@@ -32,6 +35,7 @@
     public static final PropertyKey[] ALL_KEYS = {
         TITLE,
         BROWSER_IS_MANAGED,
+        PROFILE_IS_MANAGED,
         LEARN_MORE_TEXT,
         REPORTING_IS_ENABLED,
         LEGACY_TECH_REPORTING_IS_ENABLED,
diff --git a/chrome/browser/ui/android/management/java/src/org/chromium/chrome/browser/management/ManagementView.java b/chrome/browser/ui/android/management/java/src/org/chromium/chrome/browser/management/ManagementView.java
index 59b85708..d95414de 100644
--- a/chrome/browser/ui/android/management/java/src/org/chromium/chrome/browser/management/ManagementView.java
+++ b/chrome/browser/ui/android/management/java/src/org/chromium/chrome/browser/management/ManagementView.java
@@ -20,11 +20,12 @@
 import org.chromium.components.browser_ui.widget.displaystyle.ViewResizer;
 
 /**
- * The View that renders the ManagementPage (chrome://management).
- * Consists of an medium size image icon over title and descriptive text.
+ * The View that renders the ManagementPage (chrome://management). Consists of an medium size image
+ * icon over title and descriptive text.
  */
 public class ManagementView extends ScrollView {
-    private boolean mIsManaged;
+    private boolean mIsBrowserManaged;
+    private boolean mIsProfileManaged;
     private boolean mIsReportingEnabled;
     private boolean mIsLegacyTechReportingEnabled;
 
@@ -61,7 +62,8 @@
         mReportLegacyTech = (TextView) findViewById(R.id.report_legacy_tech);
 
         // Set default management status
-        mIsManaged = false;
+        mIsBrowserManaged = false;
+        mIsProfileManaged = false;
         mIsReportingEnabled = false;
         mIsLegacyTechReportingEnabled = false;
 
@@ -84,17 +86,25 @@
         configureWideDisplayStyle();
     }
 
-    /** Sets whether account is managed. Then updates view accordingly. */
-    public void setManaged(boolean isManaged) {
-        if (mIsManaged != isManaged) {
-            mIsManaged = isManaged;
+    /** Sets whether browser is managed. Then updates view accordingly. */
+    public void setBrowserManaged(boolean isManaged) {
+        if (mIsBrowserManaged != isManaged) {
+            mIsBrowserManaged = isManaged;
             adjustView();
         }
     }
 
-    /** Gets whether account is managed. */
+    /** Sets whether profile is managed. Then updates view accordingly. */
+    public void setProfileManaged(boolean isManaged) {
+        if (mIsProfileManaged != isManaged) {
+            mIsProfileManaged = isManaged;
+            adjustView();
+        }
+    }
+
+    /** Gets whether browser or profile is managed. */
     public boolean isManaged() {
-        return mIsManaged;
+        return mIsBrowserManaged || mIsProfileManaged;
     }
 
     /** Sets whether status reporting is enabled. Then updates view accordingly. */
@@ -137,10 +147,23 @@
         mTitle.setText(title);
     }
 
+    public void setDescriptionText(String description) {
+        mDescription.setText(description);
+    }
+
     /** Adjusts Title, Description, and Learn More link based on management status. */
     private void adjustView() {
-        mDescription.setVisibility(mIsManaged ? VISIBLE : GONE);
-        mLearnMore.setVisibility(mIsManaged ? VISIBLE : GONE);
+        mDescription.setVisibility(isManaged() ? VISIBLE : GONE);
+        if (isManaged()) {
+            mDescription.setText(
+                    getContext()
+                            .getResources()
+                            .getString(
+                                    mIsBrowserManaged
+                                            ? R.string.management_browser_notice
+                                            : R.string.management_profile_notice));
+        }
+        mLearnMore.setVisibility(isManaged() ? VISIBLE : GONE);
 
         mBrowserReporting.setVisibility(
                 mIsReportingEnabled || mIsLegacyTechReportingEnabled ? VISIBLE : GONE);
diff --git a/chrome/browser/ui/android/management/java/src/org/chromium/chrome/browser/management/ManagementViewBinder.java b/chrome/browser/ui/android/management/java/src/org/chromium/chrome/browser/management/ManagementViewBinder.java
index 6c51e64..f10332c 100644
--- a/chrome/browser/ui/android/management/java/src/org/chromium/chrome/browser/management/ManagementViewBinder.java
+++ b/chrome/browser/ui/android/management/java/src/org/chromium/chrome/browser/management/ManagementViewBinder.java
@@ -11,13 +11,16 @@
 class ManagementViewBinder {
     /**
      * Listens to changes in MVC model.
+     *
      * @param model MVC property model to write changes to.
      * @param view Inflated view for the ManagementPage.
      * @param propertyKey Specific model attribute that changed on this event.
      */
     public static void bind(PropertyModel model, ManagementView view, PropertyKey propertyKey) {
         if (propertyKey == ManagementProperties.BROWSER_IS_MANAGED) {
-            view.setManaged(model.get(ManagementProperties.BROWSER_IS_MANAGED));
+            view.setBrowserManaged(model.get(ManagementProperties.BROWSER_IS_MANAGED));
+        } else if (propertyKey == ManagementProperties.PROFILE_IS_MANAGED) {
+            view.setProfileManaged(model.get(ManagementProperties.PROFILE_IS_MANAGED));
         } else if (propertyKey == ManagementProperties.TITLE) {
             view.setTitleText(model.get(ManagementProperties.TITLE));
         } else if (propertyKey == ManagementProperties.LEARN_MORE_TEXT) {
diff --git a/chrome/browser/ui/android/management/java/src/org/chromium/chrome/browser/management/ManagementViewTest.java b/chrome/browser/ui/android/management/java/src/org/chromium/chrome/browser/management/ManagementViewTest.java
index c57cf10d..16ffc96 100644
--- a/chrome/browser/ui/android/management/java/src/org/chromium/chrome/browser/management/ManagementViewTest.java
+++ b/chrome/browser/ui/android/management/java/src/org/chromium/chrome/browser/management/ManagementViewTest.java
@@ -102,6 +102,7 @@
     public void testManaged() {
         doReturn(TITLE).when(mMockManagedBrowserUtilNatives).getTitle(mMockProfile);
         doReturn(true).when(mMockManagedBrowserUtilNatives).isBrowserManaged(mMockProfile);
+        doReturn(true).when(mMockManagedBrowserUtilNatives).isProfileManaged(mMockProfile);
 
         createDialog();
 
@@ -111,6 +112,39 @@
         Assert.assertEquals(View.VISIBLE, view.mTitle.getVisibility());
 
         Assert.assertEquals(View.VISIBLE, view.mDescription.getVisibility());
+        Assert.assertEquals(
+                mActivity.getString(R.string.management_browser_notice),
+                view.mDescription.getText());
+        Assert.assertEquals(View.VISIBLE, view.mLearnMore.getVisibility());
+    }
+
+    @Test
+    public void testOnlyBrowserManaged() {
+        doReturn(true).when(mMockManagedBrowserUtilNatives).isBrowserManaged(mMockProfile);
+        doReturn(false).when(mMockManagedBrowserUtilNatives).isProfileManaged(mMockProfile);
+
+        createDialog();
+
+        ManagementView view = (ManagementView) mCoordinator.getView();
+        Assert.assertEquals(View.VISIBLE, view.mDescription.getVisibility());
+        Assert.assertEquals(
+                mActivity.getString(R.string.management_browser_notice),
+                view.mDescription.getText());
+        Assert.assertEquals(View.VISIBLE, view.mLearnMore.getVisibility());
+    }
+
+    @Test
+    public void testOnlyProfileManaged() {
+        doReturn(false).when(mMockManagedBrowserUtilNatives).isBrowserManaged(mMockProfile);
+        doReturn(true).when(mMockManagedBrowserUtilNatives).isProfileManaged(mMockProfile);
+
+        createDialog();
+
+        ManagementView view = (ManagementView) mCoordinator.getView();
+        Assert.assertEquals(View.VISIBLE, view.mDescription.getVisibility());
+        Assert.assertEquals(
+                mActivity.getString(R.string.management_profile_notice),
+                view.mDescription.getText());
         Assert.assertEquals(View.VISIBLE, view.mLearnMore.getVisibility());
     }
 
@@ -118,6 +152,7 @@
     public void testCloudReporting() {
         doReturn(TITLE).when(mMockManagedBrowserUtilNatives).getTitle(mMockProfile);
         doReturn(true).when(mMockManagedBrowserUtilNatives).isBrowserManaged(mMockProfile);
+        doReturn(true).when(mMockManagedBrowserUtilNatives).isProfileManaged(mMockProfile);
         doReturn(true).when(mMockManagedBrowserUtilNatives).isReportingEnabled();
         doReturn(false)
                 .when(mMockPrefService)
@@ -138,6 +173,7 @@
     public void testLegacyReporting() {
         doReturn(TITLE).when(mMockManagedBrowserUtilNatives).getTitle(mMockProfile);
         doReturn(true).when(mMockManagedBrowserUtilNatives).isBrowserManaged(mMockProfile);
+        doReturn(true).when(mMockManagedBrowserUtilNatives).isProfileManaged(mMockProfile);
         doReturn(false).when(mMockManagedBrowserUtilNatives).isReportingEnabled();
         doReturn(true)
                 .when(mMockPrefService)
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteCoordinator.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteCoordinator.java
index 09b874f..3c705bd 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteCoordinator.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteCoordinator.java
@@ -56,7 +56,6 @@
 import org.chromium.components.omnibox.suggestions.OmniboxSuggestionUiType;
 import org.chromium.ui.AsyncViewProvider;
 import org.chromium.ui.AsyncViewStub;
-import org.chromium.ui.UiUtils;
 import org.chromium.ui.ViewProvider;
 import org.chromium.ui.base.WindowAndroid;
 import org.chromium.ui.modaldialog.ModalDialogManager;
@@ -85,8 +84,8 @@
 
     /** An observer watching for changes to the visual state of the omnibox suggestions. */
     public interface OmniboxSuggestionsVisualStateObserver {
-        /** Called when the visibility of the omnibox suggestions changes. */
-        void onOmniboxSuggestionsVisibilityChanged(boolean visible);
+        /** Called when the Omnibox session state changes. */
+        void onOmniboxSessionStateChange(boolean isActive);
 
         /** Called when the background color of the omnibox suggestions changes. */
         void onOmniboxSuggestionsBackgroundColorChanged(@ColorInt int color);
@@ -114,13 +113,14 @@
         mModalDialogManagerSupplier = modalDialogManagerSupplier;
         Context context = parent.getContext();
 
-        PropertyModel listModel = new PropertyModel(SuggestionListProperties.ALL_KEYS);
         ModelList listItems = new ModelList();
-
-        listModel.set(SuggestionListProperties.EMBEDDER, dropdownEmbedder);
-        listModel.set(SuggestionListProperties.VISIBLE, false);
-        listModel.set(SuggestionListProperties.DRAW_OVER_ANCHOR, false);
-        listModel.set(SuggestionListProperties.SUGGESTION_MODELS, listItems);
+        PropertyModel listModel =
+                new PropertyModel.Builder(SuggestionListProperties.ALL_KEYS)
+                        .with(SuggestionListProperties.EMBEDDER, dropdownEmbedder)
+                        .with(SuggestionListProperties.OMNIBOX_SESSION_ACTIVE, false)
+                        .with(SuggestionListProperties.DRAW_OVER_ANCHOR, false)
+                        .with(SuggestionListProperties.SUGGESTION_MODELS, listItems)
+                        .build();
 
         mMediator =
                 new AutocompleteMediator(
@@ -163,7 +163,7 @@
                 });
         LazyConstructionPropertyMcp.create(
                 listModel,
-                SuggestionListProperties.VISIBLE,
+                SuggestionListProperties.OMNIBOX_SESSION_ACTIVE,
                 viewProvider,
                 SuggestionListViewBinder::bind);
 
@@ -230,16 +230,6 @@
                 dropdown.forcePhoneStyleOmnibox(forcePhoneStyleOmnibox);
                 dropdown.setAdapter(mAdapter);
                 mRecycledViewPool.ifPresent(p -> dropdown.setRecycledViewPool(p));
-
-                if (!OmniboxFeatures.sAsyncViewInflation.isEnabled()) {
-                    // NOTE: Old style Suggestions dropdown visibility management relies on adding
-                    // and removing the view from the view hierarchy. The view inflated from XML is
-                    // automatically added to the hierarchy, which changes the precondition assumed
-                    // by the old logic. The lines below ensure the initial condition is what the
-                    // logic expects it to be.
-                    UiUtils.removeViewFromParent(dropdown);
-                }
-
                 mHolder = new SuggestionListViewHolder(container, dropdown);
                 for (int i = 0; i < mCallbacks.size(); i++) {
                     mCallbacks.get(i).onResult(mHolder);
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java
index 9416b23..d49cae2 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java
@@ -229,7 +229,7 @@
                         windowAndroid,
                         mListPropertyModel,
                         embedder::getVerticalTranslationForAnimation,
-                        () -> updateOmniboxSuggestionsVisibility(true),
+                        () -> propagateOmniboxSessionStateChange(true),
                         addedVerticalOffset);
     }
 
@@ -454,7 +454,7 @@
             mNewOmniboxEditSessionTimestamp = -1;
             // Prevent any upcoming omnibox suggestions from showing once a URL is loaded (and as
             // a consequence the omnibox is unfocused).
-            hideSuggestions();
+            clearSuggestions();
         }
     }
 
@@ -463,7 +463,7 @@
      *     org.chromium.chrome.browser.omnibox.UrlFocusChangeListener#onUrlAnimationFinished(boolean)
      */
     void onUrlAnimationFinished(boolean hasFocus) {
-        updateOmniboxSuggestionsVisibility(hasFocus);
+        propagateOmniboxSessionStateChange(hasFocus);
     }
 
     /**
@@ -815,7 +815,7 @@
         mIsInZeroPrefixContext = TextUtils.isEmpty(textWithoutAutocomplete);
 
         if (mIsInZeroPrefixContext) {
-            hideSuggestions();
+            clearSuggestions();
             startCachedZeroSuggest();
         } else {
             // There may be no tabs when searching form omnibox in overview mode. In that case,
@@ -1075,39 +1075,40 @@
     }
 
     /**
-     * Update whether the omnibox suggestions are visible.
+     * Update whether the Omnibox session is active.
      *
-     * @param shouldBeVisible whether the omnibox suggestions are visible
+     * @param isActive whether session is currently active
      */
     @VisibleForTesting
-    void updateOmniboxSuggestionsVisibility(boolean shouldBeVisible) {
-        boolean wasVisible = mListPropertyModel.get(SuggestionListProperties.VISIBLE);
-        mListPropertyModel.set(SuggestionListProperties.VISIBLE, shouldBeVisible);
-        if (shouldBeVisible && !wasVisible) {
-            mIgnoreOmniboxItemSelection = true; // Reset to default value.
+    void propagateOmniboxSessionStateChange(boolean isActive) {
+        boolean wasActive = mListPropertyModel.get(SuggestionListProperties.OMNIBOX_SESSION_ACTIVE);
+        mListPropertyModel.set(SuggestionListProperties.OMNIBOX_SESSION_ACTIVE, isActive);
+
+        if (isActive != wasActive) {
+            mIgnoreOmniboxItemSelection |= isActive; // Reset to default value.
+            mOmniboxSuggestionsVisualStateObserver.ifPresent(
+                    (observer) -> observer.onOmniboxSessionStateChange(isActive));
         }
-        mOmniboxSuggestionsVisualStateObserver.ifPresent(
-                (observer) -> observer.onOmniboxSuggestionsVisibilityChanged(shouldBeVisible));
     }
 
     /**
-     * Hides the omnibox suggestion popup.
+     * Clear the list of suggestions.
      *
-     * <p>Signals the autocomplete controller to stop generating omnibox suggestions.
+     * <p>This call is used to terminate the Autocomplete session and hide the suggestions list
+     * while the Omnibox session is active.
      *
-     * @see AutocompleteController#stop(boolean)
+     * <p>This call *does not* terminate the Omnibox session.
+     *
+     * @see the {@link AutocompleteController#stop(boolean)}
      */
     @VisibleForTesting
-    void hideSuggestions() {
+    void clearSuggestions() {
         if (!mNativeInitialized || mAutocomplete == null) return;
         stopAutocomplete(true);
         dismissDeleteDialog(DialogDismissalCause.NAVIGATE_BACK_OR_TOUCH_OUTSIDE);
 
         mDropdownViewInfoListManager.clear();
         mAutocompleteResult = AutocompleteResult.EMPTY_RESULT;
-
-        mOmniboxSuggestionsVisualStateObserver.ifPresent(
-                (observer) -> observer.onOmniboxSuggestionsVisibilityChanged(false));
     }
 
     /**
@@ -1342,7 +1343,7 @@
     public void onTopResumedActivityChanged(boolean isTopResumedActivity) {
         // TODO(crbug.com/329702834): Ensuring showing Suggestions when activity resumes.
         if (!isTopResumedActivity) {
-            hideSuggestions();
+            clearSuggestions();
             mDelegate.clearOmniboxFocus();
         }
     }
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediatorUnitTest.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediatorUnitTest.java
index a38f38c..a0bd9e5 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediatorUnitTest.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediatorUnitTest.java
@@ -337,7 +337,7 @@
         mMediator.onSuggestionsReceived(AutocompleteResult.EMPTY_RESULT, /* isFinal= */ true);
 
         Assert.assertEquals(0, mSuggestionModels.size());
-        Assert.assertFalse(mListModel.get(SuggestionListProperties.VISIBLE));
+        Assert.assertFalse(mListModel.get(SuggestionListProperties.OMNIBOX_SESSION_ACTIVE));
     }
 
     @Test
@@ -351,7 +351,7 @@
         mMediator.onSuggestionsReceived(AutocompleteResult.EMPTY_RESULT, /* isFinal= */ true);
 
         Assert.assertEquals(0, mSuggestionModels.size());
-        Assert.assertFalse(mListModel.get(SuggestionListProperties.VISIBLE));
+        Assert.assertFalse(mListModel.get(SuggestionListProperties.OMNIBOX_SESSION_ACTIVE));
     }
 
     @Test
@@ -1524,21 +1524,19 @@
     }
 
     @Test
-    public void hideSuggestions_informsVisualStateObserver() {
+    public void clearSuggestions_informsVisualStateObserver() {
         mMediator.onNativeInitialized();
         mMediator.setAutocompleteProfile(mProfile);
 
-        mMediator.hideSuggestions();
-        verify(mVisualStateObserver).onOmniboxSuggestionsVisibilityChanged(eq(false));
+        mMediator.clearSuggestions();
     }
 
     @Test
-    public void updateOmniboxSuggestionsVisibility_informsVisualStateObserver() {
-        mMediator.updateOmniboxSuggestionsVisibility(true);
-        verify(mVisualStateObserver, atLeastOnce()).onOmniboxSuggestionsVisibilityChanged(eq(true));
+    public void propagateOmniboxSessionStateChange_informsVisualStateObserver() {
+        mMediator.propagateOmniboxSessionStateChange(true);
+        verify(mVisualStateObserver, atLeastOnce()).onOmniboxSessionStateChange(eq(true));
 
-        mMediator.updateOmniboxSuggestionsVisibility(false);
-        verify(mVisualStateObserver, atLeastOnce())
-                .onOmniboxSuggestionsVisibilityChanged(eq(false));
+        mMediator.propagateOmniboxSessionStateChange(false);
+        verify(mVisualStateObserver, atLeastOnce()).onOmniboxSessionStateChange(eq(false));
     }
 }
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxSuggestionsDropdown.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxSuggestionsDropdown.java
index 1b2aa8f..87776b50 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxSuggestionsDropdown.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxSuggestionsDropdown.java
@@ -42,6 +42,8 @@
 import org.chromium.ui.base.DeviceFormFactor;
 import org.chromium.ui.base.ViewUtils;
 
+import java.util.Optional;
+
 /** A widget for showing a list of omnibox suggestions. */
 public class OmniboxSuggestionsDropdown extends RecyclerView {
     /**
@@ -55,16 +57,15 @@
     private final RecyclerViewSelectionController mSelectionController;
 
     private @Nullable OmniboxSuggestionsDropdownAdapter mAdapter;
-    private @Nullable OmniboxSuggestionsDropdownEmbedder mEmbedder;
+    private Optional<OmniboxSuggestionsDropdownEmbedder> mEmbedder = Optional.empty();
     private @Nullable GestureObserver mGestureObserver;
     private @Nullable Callback<Integer> mHeightChangeListener;
-    private @Nullable Runnable mSuggestionDropdownScrollListener;
-    private @Nullable Runnable mSuggestionDropdownOverscrolledToTopListener;
     private @NonNull OmniboxAlignment mOmniboxAlignment = OmniboxAlignment.UNSPECIFIED;
 
     private int mListViewMaxHeight;
     private int mLastBroadcastedListViewMaxHeight;
-    private @Nullable Callback<OmniboxAlignment> mOmniboxAlignmentObserver;
+    private @Nullable Callback<OmniboxAlignment> mOmniboxAlignmentObserver =
+            this::onOmniboxAlignmentChanged;
     private float mChildVerticalTranslation;
 
     /**
@@ -84,9 +85,11 @@
 
     /** Scroll manager that propagates scroll event notification to registered observers. */
     @VisibleForTesting
-    /* package */ class SuggestionLayoutScrollListener extends LinearLayoutManager {
+    /* package */ static class SuggestionLayoutScrollListener extends LinearLayoutManager {
         private boolean mLastKeyboardShownState;
         private boolean mCurrentGestureAffectedKeyboardState;
+        private @Nullable Runnable mSuggestionDropdownScrollListener;
+        private @Nullable Runnable mSuggestionDropdownOverscrolledToTopListener;
 
         public SuggestionLayoutScrollListener(Context context) {
             super(context);
@@ -207,6 +210,20 @@
         /* package */ void onNewGesture() {
             mCurrentGestureAffectedKeyboardState = false;
         }
+
+        /**
+         * @param listener A listener will be invoked whenever the User scrolls the list.
+         */
+        public void setSuggestionDropdownScrollListener(@NonNull Runnable listener) {
+            mSuggestionDropdownScrollListener = listener;
+        }
+
+        /**
+         * @param listener A listener will be invoked whenever the User scrolls the list to the top.
+         */
+        public void setSuggestionDropdownOverscrolledToTopListener(@NonNull Runnable listener) {
+            mSuggestionDropdownOverscrolledToTopListener = listener;
+        }
     }
 
     /**
@@ -275,8 +292,6 @@
         getRecycledViewPool().clear();
         mGestureObserver = null;
         mHeightChangeListener = null;
-        mSuggestionDropdownScrollListener = null;
-        mSuggestionDropdownOverscrolledToTopListener = null;
     }
 
     /**
@@ -298,20 +313,6 @@
         mHeightChangeListener = listener;
     }
 
-    /**
-     * @param listener A listener will be invoked whenever the User scrolls the list.
-     */
-    public void setSuggestionDropdownScrollListener(@NonNull Runnable listener) {
-        mSuggestionDropdownScrollListener = listener;
-    }
-
-    /**
-     * @param listener A listener will be invoked whenever the User scrolls the list to the top.
-     */
-    public void setSuggestionDropdownOverscrolledToTopListener(@NonNull Runnable listener) {
-        mSuggestionDropdownOverscrolledToTopListener = listener;
-    }
-
     /** Resets selection typically in response to changes to the list. */
     public void resetSelection() {
         mSelectionController.resetSelection();
@@ -393,80 +394,15 @@
     }
 
     @Override
-    public void setVisibility(int visibility) {
-        if (OmniboxFeatures.sAsyncViewInflation.isEnabled()) {
-            if (visibility == VISIBLE) {
-                installAlignmentObserver();
-            } else {
-                removeAlignmentObserver();
-            }
-        }
-
-        super.setVisibility(visibility);
-    }
-
-    @Override
-    public void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        if (!OmniboxFeatures.sAsyncViewInflation.isEnabled()) {
-            installAlignmentObserver();
-        }
-    }
-
-    private void installAlignmentObserver() {
-        // TODO(40253396): this is needed only to permit view to be inflated from XML while keeping
-        // the old logic that relies on adding/removing the view to/from the view hierarchy.
-        // AsyncViewInflater automatically attaches the SuggestionsDropdown to the view hierarchy
-        // once it is done inflating the view, which triggers onAttachedToWindow(). We permit the
-        // old style management and remove the view from view hierarchy immediately after, which
-        // triggers onDetachedFromWindow().
-        // This should not be needed once we are ready to manage view presence using
-        // setVisibility().
-        if (mEmbedder == null) return;
-
-        mEmbedder.onAttachedToWindow();
-        mOmniboxAlignmentObserver = this::onOmniboxAlignmentChanged;
-        mOmniboxAlignment = mEmbedder.addAlignmentObserver(mOmniboxAlignmentObserver);
-        resetSelection();
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-        if (!OmniboxFeatures.sAsyncViewInflation.isEnabled()) {
-            removeAlignmentObserver();
-        }
-    }
-
-    private void removeAlignmentObserver() {
-        // TODO(40253396): this is needed only to permit view to be inflated from XML while keeping
-        // the old logic that relies on adding/removing the view to/from the view hierarchy.
-        // AsyncViewInflater automatically attaches the SuggestionsDropdown to the view hierarchy
-        // once it is done inflating the view, which triggers onAttachedToWindow(). We permit the
-        // old style management and remove the view from view hierarchy immediately after, which
-        // triggers onDetachedFromWindow().
-        // This should not be needed once we are ready to manage view presence using
-        // setVisibility().
-        if (mEmbedder == null) return;
-
-        mEmbedder.onDetachedFromWindow();
-        mOmniboxAlignment = OmniboxAlignment.UNSPECIFIED;
-        if (!OmniboxFeatures.shouldPreWarmRecyclerViewPool()) {
-            getRecycledViewPool().clear();
-        }
-        mAdapter.recordSessionMetrics();
-        mEmbedder.removeAlignmentObserver(mOmniboxAlignmentObserver);
-    }
-
-    @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        boolean isTablet = mEmbedder.map(e -> e.isTablet()).orElse(false);
+
         try (TraceEvent tracing = TraceEvent.scoped("OmniboxSuggestionsList.Measure");
                 TimingMetric metric = OmniboxMetrics.recordSuggestionListMeasureTime();
                 TimingMetric metric2 = OmniboxMetrics.recordSuggestionListMeasureWallTime()) {
-            OmniboxAlignment omniboxAlignment = mEmbedder.getCurrentAlignment();
-            maybeUpdateLayoutParams(omniboxAlignment.top);
-            int availableViewportHeight = omniboxAlignment.height;
-            int desiredWidth = omniboxAlignment.width;
+            maybeUpdateLayoutParams(mOmniboxAlignment.top);
+            int availableViewportHeight = mOmniboxAlignment.height;
+            int desiredWidth = mOmniboxAlignment.width;
             adjustHorizontalPosition();
             notifyObserversIfViewportHeightChanged(availableViewportHeight);
 
@@ -474,9 +410,9 @@
             heightMeasureSpec =
                     MeasureSpec.makeMeasureSpec(
                             availableViewportHeight,
-                            mEmbedder.isTablet() ? MeasureSpec.AT_MOST : MeasureSpec.EXACTLY);
+                            isTablet ? MeasureSpec.AT_MOST : MeasureSpec.EXACTLY);
             super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-            if (mEmbedder.isTablet()) {
+            if (isTablet) {
                 setRoundBottomCorners(
                         getMeasuredHeight() < availableViewportHeight
                                 || !KeyboardVisibilityDelegate.getInstance()
@@ -578,9 +514,43 @@
      * @param embedder the embedder of this list.
      */
     public void setEmbedder(@NonNull OmniboxSuggestionsDropdownEmbedder embedder) {
-        assert mEmbedder == null;
-        mEmbedder = embedder;
-        mOmniboxAlignment = mEmbedder.getCurrentAlignment();
+        // Don't reset the current value of `mOmniboxAlignment`, and don't read the value from newly
+        // installed embedder to ensure the `onOmniboxAlignmentChanged` does the right thing when we
+        // install our observers.
+        mEmbedder = Optional.of(embedder);
+    }
+
+    /**
+     * Respond to Omnibox session state change.
+     *
+     * @param urlHasFocus whether URL has focus (signaling the session is active)
+     */
+    /* package */ void onOmniboxSessionStateChange(boolean urlHasFocus) {
+        if (urlHasFocus) {
+            installAlignmentObserver();
+        } else {
+            removeAlignmentObserver();
+        }
+    }
+
+    private void installAlignmentObserver() {
+        mEmbedder.ifPresent(
+                e -> {
+                    e.onAttachedToWindow();
+                    mOmniboxAlignment = e.addAlignmentObserver(mOmniboxAlignmentObserver);
+                });
+    }
+
+    private void removeAlignmentObserver() {
+        mEmbedder.ifPresent(
+                e -> {
+                    e.onDetachedFromWindow();
+                    e.removeAlignmentObserver(mOmniboxAlignmentObserver);
+                });
+
+        if (!OmniboxFeatures.shouldPreWarmRecyclerViewPool()) {
+            getRecycledViewPool().clear();
+        }
     }
 
     private void onOmniboxAlignmentChanged(@NonNull OmniboxAlignment omniboxAlignment) {
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxSuggestionsDropdownUnitTest.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxSuggestionsDropdownUnitTest.java
index bcd668dc..c3c77c30 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxSuggestionsDropdownUnitTest.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxSuggestionsDropdownUnitTest.java
@@ -11,7 +11,6 @@
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
@@ -59,6 +58,7 @@
 
 /** Unit tests for {@link OmniboxSuggestionsDropdown}. */
 @RunWith(BaseRobolectricTestRunner.class)
+@Config(sdk = 28)
 public class OmniboxSuggestionsDropdownUnitTest {
     public @Rule TestRule mProcessor = new Features.JUnitProcessor();
     public @Rule MockitoRule mMockitoRule = MockitoJUnit.rule();
@@ -69,7 +69,7 @@
 
     private Context mContext;
 
-    private OmniboxSuggestionsDropdown mDropdown;
+    private TestOmniboxSuggestionsDropdown mDropdown;
     private OmniboxSuggestionsDropdown.SuggestionLayoutScrollListener mListener;
     private OmniboxAlignment mOmniboxAlignment;
     private ObservableSupplierImpl<OmniboxAlignment> mOmniboxAlignmentSupplier =
@@ -121,13 +121,31 @@
                 }
             };
 
+    // TODO(341377411): resolve issues with mockito not being able to stub the isInLayout method.
+    private static class TestOmniboxSuggestionsDropdown extends OmniboxSuggestionsDropdown {
+        private boolean mIsInLayout;
+
+        public TestOmniboxSuggestionsDropdown(Context context) {
+            super(context, null);
+        }
+
+        @Override
+        public boolean isInLayout() {
+            return mIsInLayout;
+        }
+
+        public void setIsInLayout(boolean isInLayout) {
+            mIsInLayout = isInLayout;
+        }
+    }
+
     @Before
     public void setUp() {
         mContext =
                 new ContextThemeWrapper(
                         ApplicationProvider.getApplicationContext(),
                         R.style.Theme_BrowserUI_DayNight);
-        mDropdown = new OmniboxSuggestionsDropdown(mContext, null);
+        mDropdown = new TestOmniboxSuggestionsDropdown(mContext);
         mDropdown.setAdapter(mAdapter);
         mListener = mDropdown.getLayoutScrollListener();
     }
@@ -163,7 +181,7 @@
 
     @Test
     public void testScrollListener_keyboardShouldDismissOnScrollAttemptFromTop() {
-        mDropdown.setSuggestionDropdownScrollListener(mDropdownScrollListener);
+        mListener.setSuggestionDropdownScrollListener(mDropdownScrollListener);
 
         // Scroll attempt should suppress the scroll and emit keyboard dismiss.
         assertEquals(0, mListener.updateKeyboardVisibilityAndScroll(10, 10));
@@ -178,7 +196,7 @@
 
     @Test
     public void testScrollListener_keyboardShouldDismissOnScrollAttemptFromScrolledList() {
-        mDropdown.setSuggestionDropdownScrollListener(mDropdownScrollListener);
+        mListener.setSuggestionDropdownScrollListener(mDropdownScrollListener);
 
         // Scroll attempt should suppress the scroll and emit keyboard dismiss.
         assertEquals(0, mListener.updateKeyboardVisibilityAndScroll(10, 10));
@@ -193,8 +211,8 @@
 
     @Test
     public void testScrollListener_keyboardShouldShowOnScrollToTop() {
-        mDropdown.setSuggestionDropdownScrollListener(mDropdownScrollListener);
-        mDropdown.setSuggestionDropdownOverscrolledToTopListener(mDropdownScrollToTopListener);
+        mListener.setSuggestionDropdownScrollListener(mDropdownScrollListener);
+        mListener.setSuggestionDropdownOverscrolledToTopListener(mDropdownScrollToTopListener);
 
         // Scroll attempt should suppress the scroll and emit keyboard dismiss.
         assertEquals(0, mListener.updateKeyboardVisibilityAndScroll(10, 10));
@@ -225,8 +243,8 @@
     public void testScrollListener_dismissingKeyboardWhenScrollDoesNotHappen() {
         // In some cases the list may be long enough to stretch below the keyboard, but not long
         // enough to be scrollable. We want to dismiss the keyboard in these cases, too.
-        mDropdown.setSuggestionDropdownScrollListener(mDropdownScrollListener);
-        mDropdown.setSuggestionDropdownOverscrolledToTopListener(mDropdownScrollToTopListener);
+        mListener.setSuggestionDropdownScrollListener(mDropdownScrollListener);
+        mListener.setSuggestionDropdownOverscrolledToTopListener(mDropdownScrollToTopListener);
 
         // Pretend we're scrolling down (delta=10) but there is no content to move to (scroll=0).
         assertEquals(0, mListener.updateKeyboardVisibilityAndScroll(0, 10));
@@ -249,8 +267,8 @@
 
     @Test
     public void testScrollListener_dismissingKeyboardWhenTheListIsOnlyBarelyUnderTheKeyboard() {
-        mDropdown.setSuggestionDropdownScrollListener(mDropdownScrollListener);
-        mDropdown.setSuggestionDropdownOverscrolledToTopListener(mDropdownScrollToTopListener);
+        mListener.setSuggestionDropdownScrollListener(mDropdownScrollListener);
+        mListener.setSuggestionDropdownOverscrolledToTopListener(mDropdownScrollToTopListener);
 
         // We want to scroll by 10px, but there's only 1px of slack. This means the suggestions list
         // spans entirely under the keyboard. Hide the keyboard.
@@ -271,7 +289,7 @@
 
     @Test
     public void testScrollListener_reemitsKeyboardDismissOnReset() {
-        mDropdown.setSuggestionDropdownScrollListener(mDropdownScrollListener);
+        mListener.setSuggestionDropdownScrollListener(mDropdownScrollListener);
 
         // Scroll attempt should suppress the scroll and emit keyboard dismiss.
         // This time the scroll happens, even if just by one pixel.
@@ -305,21 +323,31 @@
     }
 
     @Test
-    public void testAlignmentProvider_windowAttachment() {
+    public void onOmniboxSessionStateChange_withEmbedder() {
         mDropdown.setEmbedder(mEmbedder);
-        assertFalse(mAttachedToWindow);
 
-        mDropdown.onAttachedToWindow();
+        assertFalse(mAttachedToWindow);
+        mDropdown.onOmniboxSessionStateChange(true);
         assertTrue(mAttachedToWindow);
 
-        mDropdown.onDetachedFromWindow();
+        mDropdown.onOmniboxSessionStateChange(false);
+        assertFalse(mAttachedToWindow);
+    }
+
+    @Test
+    public void onOmniboxSessionStateChange_withoutEmbedder() {
+        assertFalse(mAttachedToWindow);
+        mDropdown.onOmniboxSessionStateChange(true);
+        assertFalse(mAttachedToWindow);
+        mDropdown.onOmniboxSessionStateChange(false);
         assertFalse(mAttachedToWindow);
     }
 
     @Test
     public void testAlignmentProvider_widthChange() {
         mDropdown.setEmbedder(mEmbedder);
-        mDropdown.onAttachedToWindow();
+        mDropdown.onOmniboxSessionStateChange(true);
+
         mOmniboxAlignment = new OmniboxAlignment(0, 100, 600, 0, 10, 10);
         mOmniboxAlignmentSupplier.set(mOmniboxAlignment);
         layoutDropdown(600, 800);
@@ -338,7 +366,8 @@
     @Test
     public void testAlignmentProvider_topChange() {
         mDropdown.setEmbedder(mEmbedder);
-        mDropdown.onAttachedToWindow();
+        mDropdown.onOmniboxSessionStateChange(true);
+
         mDropdown.setLayoutParams(
                 new LayoutParams(
                         ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
@@ -364,7 +393,8 @@
     @Test
     public void testAlignmentProvider_heightChange() {
         mDropdown.setEmbedder(mEmbedder);
-        mDropdown.onAttachedToWindow();
+        mDropdown.onOmniboxSessionStateChange(true);
+
         mDropdown.setLayoutParams(
                 new LayoutParams(
                         ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
@@ -386,12 +416,11 @@
     @Test
     @LooperMode(Mode.PAUSED)
     public void testAlignmentProvider_changeDuringlayout() {
-        mDropdown = Mockito.spy(new OmniboxSuggestionsDropdown(mContext, null));
         mDropdown.setAdapter(mAdapter);
         mDropdown.setEmbedder(mEmbedder);
-        mDropdown.onAttachedToWindow();
+        mDropdown.onOmniboxSessionStateChange(true);
 
-        doReturn(true).when(mDropdown).isInLayout();
+        mDropdown.setIsInLayout(true);
         mOmniboxAlignment = new OmniboxAlignment(0, 80, 400, 600, 10, 10);
         mOmniboxAlignmentSupplier.set(mOmniboxAlignment);
 
@@ -399,16 +428,15 @@
         assertFalse(mDropdown.isLayoutRequested());
 
         // The posted task should re-request layout.
-        Mockito.clearInvocations(mDropdown);
         ShadowLooper.runUiThreadTasks();
-        verify(mDropdown).requestLayout();
+        assertTrue(mDropdown.isLayoutRequested());
     }
 
     @Test
     public void translateChildrenVertical() {
         mDropdown.setAdapter(mAdapter);
         mDropdown.setEmbedder(mEmbedder);
-        mDropdown.onAttachedToWindow();
+        mDropdown.onOmniboxSessionStateChange(true);
 
         View childView = Mockito.mock(View.class);
 
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionListProperties.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionListProperties.java
index c248f89d..078cc459 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionListProperties.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionListProperties.java
@@ -18,8 +18,9 @@
 
     static final WritableFloatPropertyKey CHILD_TRANSLATION_Y = new WritableFloatPropertyKey();
 
-    /** Whether the suggestion list is visible. */
-    static final WritableBooleanPropertyKey VISIBLE = new WritableBooleanPropertyKey();
+    /** Whether the Omnibox session is active and Suggestions may be shown. */
+    static final WritableBooleanPropertyKey OMNIBOX_SESSION_ACTIVE =
+            new WritableBooleanPropertyKey();
 
     /** The embedder for the suggestion list. */
     static final WritableObjectPropertyKey<OmniboxSuggestionsDropdownEmbedder> EMBEDDER =
@@ -67,7 +68,7 @@
             new PropertyKey[] {
                 ALPHA,
                 CHILD_TRANSLATION_Y,
-                VISIBLE,
+                OMNIBOX_SESSION_ACTIVE,
                 EMBEDDER,
                 SUGGESTION_MODELS,
                 COLOR_SCHEME,
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionListViewBinder.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionListViewBinder.java
index 0bffd36..c80f566c 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionListViewBinder.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionListViewBinder.java
@@ -10,7 +10,6 @@
 import androidx.annotation.Nullable;
 
 import org.chromium.chrome.browser.omnibox.R;
-import org.chromium.ui.UiUtils;
 import org.chromium.ui.modelutil.ListObservable;
 import org.chromium.ui.modelutil.MVCListAdapter.ModelList;
 import org.chromium.ui.modelutil.PropertyKey;
@@ -40,38 +39,26 @@
         } else if (SuggestionListProperties.CHILD_TRANSLATION_Y.equals(propertyKey)) {
             view.dropdown.translateChildrenVertical(
                     model.get(SuggestionListProperties.CHILD_TRANSLATION_Y));
-        } else if (SuggestionListProperties.VISIBLE.equals(propertyKey)) {
-            boolean visible = model.get(SuggestionListProperties.VISIBLE);
-            // Actual View showing the dropdown.
-            View dropdownView = view.dropdown.getViewGroup();
-            if (visible) {
-                // Ensure the tracked keyboard state is consistent with actual keyboard state.
-                // The keyboard is about to be called up.
-                view.dropdown.resetKeyboardShownState();
-                if (dropdownView.getParent() == null) {
-                    view.container.addView(dropdownView);
-                    // When showing the suggestions list for the first time, make sure to apply
-                    // appropriate visibility to freshly inflated container.
-                    // This is later handled by subsequent calls to updateContainerVisibility()
-                    // performed whenever the suggestion model list changes.
-                    updateContainerVisibility(model, view);
-                }
-            } else {
-                UiUtils.removeViewFromParent(dropdownView);
-            }
         } else if (SuggestionListProperties.EMBEDDER.equals(propertyKey)) {
             view.dropdown.setEmbedder(model.get(SuggestionListProperties.EMBEDDER));
+        } else if (SuggestionListProperties.OMNIBOX_SESSION_ACTIVE.equals(propertyKey)) {
+            view.dropdown.onOmniboxSessionStateChange(
+                    model.get(SuggestionListProperties.OMNIBOX_SESSION_ACTIVE));
         } else if (SuggestionListProperties.GESTURE_OBSERVER.equals(propertyKey)) {
             view.dropdown.setGestureObserver(model.get(SuggestionListProperties.GESTURE_OBSERVER));
         } else if (SuggestionListProperties.DROPDOWN_HEIGHT_CHANGE_LISTENER.equals(propertyKey)) {
             view.dropdown.setHeightChangeListener(
                     model.get(SuggestionListProperties.DROPDOWN_HEIGHT_CHANGE_LISTENER));
         } else if (SuggestionListProperties.DROPDOWN_SCROLL_LISTENER.equals(propertyKey)) {
-            view.dropdown.setSuggestionDropdownScrollListener(
-                    model.get(SuggestionListProperties.DROPDOWN_SCROLL_LISTENER));
+            view.dropdown
+                    .getLayoutScrollListener()
+                    .setSuggestionDropdownScrollListener(
+                            model.get(SuggestionListProperties.DROPDOWN_SCROLL_LISTENER));
         } else if (SuggestionListProperties.DROPDOWN_SCROLL_TO_TOP_LISTENER.equals(propertyKey)) {
-            view.dropdown.setSuggestionDropdownOverscrolledToTopListener(
-                    model.get(SuggestionListProperties.DROPDOWN_SCROLL_TO_TOP_LISTENER));
+            view.dropdown
+                    .getLayoutScrollListener()
+                    .setSuggestionDropdownOverscrolledToTopListener(
+                            model.get(SuggestionListProperties.DROPDOWN_SCROLL_TO_TOP_LISTENER));
         } else if (SuggestionListProperties.LIST_IS_FINAL.equals(propertyKey)) {
             if (model.get(SuggestionListProperties.LIST_IS_FINAL)) {
                 view.dropdown.emitWindowContentChanged();
@@ -101,6 +88,9 @@
                             updateContainerVisibility(model, view);
                         }
                     });
+            // When the suggestions list is installed for the first time, it may already contain
+            // elements. Be sure to capture and reflect this fact appropriately.
+            updateContainerVisibility(model, view);
         } else if (SuggestionListProperties.COLOR_SCHEME.equals(propertyKey)) {
             view.dropdown.refreshPopupBackground(model.get(SuggestionListProperties.COLOR_SCHEME));
         } else if (SuggestionListProperties.DRAW_OVER_ANCHOR == propertyKey) {
diff --git a/chrome/browser/ui/android/pdf/java/src/org/chromium/chrome/browser/pdf/PdfCoordinator.java b/chrome/browser/ui/android/pdf/java/src/org/chromium/chrome/browser/pdf/PdfCoordinator.java
index d2b462f52..141125f 100644
--- a/chrome/browser/ui/android/pdf/java/src/org/chromium/chrome/browser/pdf/PdfCoordinator.java
+++ b/chrome/browser/ui/android/pdf/java/src/org/chromium/chrome/browser/pdf/PdfCoordinator.java
@@ -32,6 +32,7 @@
     private NativePageHost mHost;
     private final View mView;
     private final FragmentManager mFragmentManager;
+    private final boolean mIsIncognito;
     private int mFragmentContainerViewId;
     private String mPdfFilePath;
     private boolean mPdfIsDownloaded;
@@ -54,6 +55,7 @@
             NativePageHost host, Profile profile, Activity activity, String filepath, String url) {
         mHost = host;
         mIsPdfLoaded = false;
+        mIsIncognito = profile.isOffTheRecord();
         mView = LayoutInflater.from(host.getContext()).inflate(R.layout.pdf_page, null);
         mTextView = mView.findViewById(R.id.fake_pdf_text);
         mTextView.setText(PDF_LOADING);
@@ -145,7 +147,8 @@
         if (mView.getParent() == null) {
             return;
         }
-        PdfDocumentRequest pdfDocumentRequest = PdfUtils.getPdfDocumentRequest(mPdfFilePath);
+        PdfDocumentRequest pdfDocumentRequest =
+                PdfUtils.getPdfDocumentRequest(mPdfFilePath, mIsIncognito);
         if (pdfDocumentRequest != null) {
             mPdfViewerFragment = new PdfViewerFragment();
             mPdfEventsListener = new ChromePdfEventsListener();
diff --git a/chrome/browser/ui/android/pdf/java/src/org/chromium/chrome/browser/pdf/PdfPageUnitTest.java b/chrome/browser/ui/android/pdf/java/src/org/chromium/chrome/browser/pdf/PdfPageUnitTest.java
index 0fe789ceb..0fec76b9 100644
--- a/chrome/browser/ui/android/pdf/java/src/org/chromium/chrome/browser/pdf/PdfPageUnitTest.java
+++ b/chrome/browser/ui/android/pdf/java/src/org/chromium/chrome/browser/pdf/PdfPageUnitTest.java
@@ -28,6 +28,7 @@
 
 import org.chromium.base.supplier.DestroyableObservableSupplier;
 import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.chrome.browser.fakepdf.PdfDocumentRequest;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.ui.native_page.NativePageHost;
 import org.chromium.chrome.browser.util.ChromeFileProvider;
@@ -58,6 +59,8 @@
     private static final String PDF_LINK = "https://www.foo.com/testfiles/pdf/sample.pdf";
     private static final String EXAMPLE_URL = "https://www.example.com/";
     private static final String FILE_PATH = "/media/external/downloads/sample.pdf";
+    private static final String FILE_PATH_INCOGNITO = "/proc/5202/fd/344";
+    private static final String FILE_PATH_INCOGNITO_INVALID = "/proc/5202/fd/one";
     private static final String FILE_NAME = "sample.pdf";
     private static final String IMAGE_FILE_URL = "file:///media/external/downloads/sample.jpg";
 
@@ -193,6 +196,53 @@
     }
 
     @Test
+    public void testGetPdfDocumentRequest_WithPdfLink() {
+        PdfDocumentRequest request = PdfUtils.getPdfDocumentRequest(FILE_PATH, false);
+        Assert.assertNotNull("PdfDocumentRequest should not be null.", request);
+        Assert.assertNotNull("Uri should not be null.", request.getUri());
+        Assert.assertNull("File should be null.", request.getFile());
+        Assert.assertNull(
+                "ParcelFileDescriptor should be null.", request.getParcelFileDescriptor());
+    }
+
+    @Test
+    public void testGetPdfDocumentRequest_WithPdfLink_Incognito() {
+        PdfDocumentRequest request = PdfUtils.getPdfDocumentRequest(FILE_PATH_INCOGNITO, true);
+        Assert.assertNotNull("PdfDocumentRequest should not be null.", request);
+        Assert.assertNull("Uri should be null.", request.getUri());
+        Assert.assertNull("File should be null.", request.getFile());
+        Assert.assertNotNull(
+                "ParcelFileDescriptor should not be null.", request.getParcelFileDescriptor());
+    }
+
+    @Test
+    public void testGetPdfDocumentRequest_WithPdfLink_Incognito_InvalidFd() {
+        PdfDocumentRequest request =
+                PdfUtils.getPdfDocumentRequest(FILE_PATH_INCOGNITO_INVALID, true);
+        Assert.assertNull("PdfDocumentRequest should be null when filepath is invalid.", request);
+    }
+
+    @Test
+    public void testGetPdfDocumentRequest_WithContentUri() {
+        PdfDocumentRequest request = PdfUtils.getPdfDocumentRequest(CONTENT_URL, false);
+        Assert.assertNotNull("PdfDocumentRequest should not be null.", request);
+        Assert.assertNotNull("Uri should not be null.", request.getUri());
+        Assert.assertNull("File should be null.", request.getFile());
+        Assert.assertNull(
+                "ParcelFileDescriptor should be null.", request.getParcelFileDescriptor());
+    }
+
+    @Test
+    public void testGetPdfDocumentRequest_WithFileUri() {
+        PdfDocumentRequest request = PdfUtils.getPdfDocumentRequest(FILE_URL, false);
+        Assert.assertNotNull("PdfDocumentRequest should not be null.", request);
+        Assert.assertNull("Uri should be null.", request.getUri());
+        Assert.assertNotNull("File should not be null.", request.getFile());
+        Assert.assertNull(
+                "ParcelFileDescriptor should be null.", request.getParcelFileDescriptor());
+    }
+
+    @Test
     public void testGetFileNameFromUrl() {
         String filename = PdfUtils.getFileNameFromUrl(FILE_URL, DEFAULT_TAB_TITLE);
         Assert.assertEquals("Filename does not match for file url.", FILE_NAME, filename);
diff --git a/chrome/browser/ui/android/pdf/java/src/org/chromium/chrome/browser/pdf/PdfUtils.java b/chrome/browser/ui/android/pdf/java/src/org/chromium/chrome/browser/pdf/PdfUtils.java
index 5016ed1..e638b82 100644
--- a/chrome/browser/ui/android/pdf/java/src/org/chromium/chrome/browser/pdf/PdfUtils.java
+++ b/chrome/browser/ui/android/pdf/java/src/org/chromium/chrome/browser/pdf/PdfUtils.java
@@ -5,6 +5,7 @@
 package org.chromium.chrome.browser.pdf;
 
 import android.net.Uri;
+import android.os.ParcelFileDescriptor;
 import android.text.TextUtils;
 import android.webkit.MimeTypeMap;
 
@@ -129,7 +130,7 @@
         sShouldOpenPdfInlineForTesting = shouldOpenPdfInlineForTesting;
     }
 
-    static PdfDocumentRequest getPdfDocumentRequest(String pdfFilePath) {
+    static PdfDocumentRequest getPdfDocumentRequest(String pdfFilePath, boolean isIncognito) {
         Uri uri = Uri.parse(pdfFilePath);
         String scheme = uri.getScheme();
         PdfDocumentRequest.Builder builder = new PdfDocumentRequest.Builder();
@@ -139,13 +140,18 @@
             } else if (UrlConstants.FILE_SCHEME.equals(scheme)) {
                 File file = new File(Objects.requireNonNull(uri.getPath()));
                 builder.setFile(file);
+            } else if (isIncognito) {
+                int fd = getFileDescriptor(pdfFilePath);
+                ParcelFileDescriptor pfd = ParcelFileDescriptor.adoptFd(fd);
+                builder.setPfd(pfd);
             } else {
                 File file = new File(pdfFilePath);
+                // TODO: use builder.setFile(file) once supported.
                 Uri generatedUri = ChromeFileProvider.generateUri(file);
                 builder.setUri(generatedUri);
             }
-        } catch (IllegalArgumentException e) {
-            Log.e(TAG, "Couldn't generate URI for pdf file: " + e);
+        } catch (Exception e) {
+            Log.e(TAG, "Couldn't generate PdfDocumentRequest: " + e);
             return null;
         }
         builder.setPdfViewSettings(
@@ -153,6 +159,11 @@
         return new PdfDocumentRequest(builder);
     }
 
+    private static int getFileDescriptor(String filepath) throws NumberFormatException {
+        String fd = filepath.substring(filepath.lastIndexOf('/') + 1);
+        return Integer.parseInt(fd);
+    }
+
     static void loadPdf(
             PdfViewerFragment pdfViewerFragment,
             PdfDocumentRequest pdfDocumentRequest,
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings.grd b/chrome/browser/ui/android/strings/android_chrome_strings.grd
index a25511c..1361fbb 100644
--- a/chrome/browser/ui/android/strings/android_chrome_strings.grd
+++ b/chrome/browser/ui/android/strings/android_chrome_strings.grd
@@ -5959,7 +5959,10 @@
         Management
       </message>
       <message name="IDS_MANAGEMENT_BROWSER_NOTICE" desc="The descriptive text on chrome://management page that explains to the user the implications of browsing under a managed environment.">
-        Your administrator can change your browser setup remotely. Activity on this device may also be managed outside of Chrome.
+        Your administrator can make changes to your profile and browser remotely, analyze information about the browser through reporting, and perform other necessary tasks. Activity on this device may also be managed outside of Chrome.
+      </message>
+      <message name="IDS_MANAGEMENT_PROFILE_NOTICE" desc="The descriptive text on chrome://management page that explains to the user the implications of browsing under a managed environment.">
+        The profile you’re signed in to is a managed profile. Your administrator can make changes to your profile settings remotely, analyze information about the browser through reporting, and perform other necessary tasks.
       </message>
       <message name="IDS_MANAGEMENT_LEARN_MORE" desc="The learn more link on chrome://management page that provides more information about managed devices.">
         <ph name="BEGIN_LINK">&lt;LINK&gt;</ph>Learn More<ph name="END_LINK">&lt;/LINK&gt;</ph>
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_MANAGEMENT_BROWSER_NOTICE.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_MANAGEMENT_BROWSER_NOTICE.png.sha1
index 4f34ffd..b5a9e9a 100644
--- a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_MANAGEMENT_BROWSER_NOTICE.png.sha1
+++ b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_MANAGEMENT_BROWSER_NOTICE.png.sha1
@@ -1 +1 @@
-b02f038ccd57988410ab3f87c9a2f4d3100cacd5
\ No newline at end of file
+6eaa9ff6f4046d43cf5bbaaa50dfab564230f6f7
\ No newline at end of file
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_MANAGEMENT_PROFILE_NOTICE.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_MANAGEMENT_PROFILE_NOTICE.png.sha1
new file mode 100644
index 0000000..a76e44d
--- /dev/null
+++ b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_MANAGEMENT_PROFILE_NOTICE.png.sha1
@@ -0,0 +1 @@
+bb0a599dbf908d8b7b97edc2a876abc609e61969
\ No newline at end of file
diff --git a/chrome/browser/ui/autofill/autofill_signin_promo_tab_helper.cc b/chrome/browser/ui/autofill/autofill_signin_promo_tab_helper.cc
index a763ece..fa4c2ef4 100644
--- a/chrome/browser/ui/autofill/autofill_signin_promo_tab_helper.cc
+++ b/chrome/browser/ui/autofill/autofill_signin_promo_tab_helper.cc
@@ -130,10 +130,10 @@
   }
 
   // We only want to move the data if the sign in event has the correct
-  // operation source, so if it was performed from the tab that was opened after
+  // access point, so if it was performed from the tab that was opened after
   // clicking the sign in promo.
-  if (token_operation_source != signin_metrics::SourceForRefreshTokenOperation::
-                                    kDiceResponseHandler_PasswordPromoSignin) {
+  if (identity_manager->FindExtendedAccountInfo(account_info).access_point !=
+      state_->access_point_) {
     Reset();
     return;
   }
diff --git a/chrome/browser/ui/autofill/chrome_autofill_client.cc b/chrome/browser/ui/autofill/chrome_autofill_client.cc
index 0331633..cea1562 100644
--- a/chrome/browser/ui/autofill/chrome_autofill_client.cc
+++ b/chrome/browser/ui/autofill/chrome_autofill_client.cc
@@ -162,7 +162,6 @@
 #include "chrome/browser/ui/autofill/payments/virtual_card_manual_fallback_bubble_controller_impl.h"
 #include "chrome/browser/ui/autofill/payments/webauthn_dialog.h"
 #include "chrome/browser/ui/autofill/payments/webauthn_dialog_controller_impl.h"
-#include "chrome/browser/ui/autofill/payments/webauthn_dialog_state.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_commands.h"
 #include "chrome/browser/ui/browser_finder.h"
@@ -582,14 +581,6 @@
 #endif
 
 #if !BUILDFLAG(IS_ANDROID)
-void ChromeAutofillClient::UpdateWebauthnOfferDialogWithError() {
-  WebauthnDialogControllerImpl* controller =
-      WebauthnDialogControllerImpl::GetForPage(
-          web_contents()->GetPrimaryPage());
-  if (controller)
-    controller->UpdateDialog(WebauthnDialogState::kOfferError);
-}
-
 bool ChromeAutofillClient::CloseWebauthnDialog() {
   WebauthnDialogControllerImpl* controller =
       WebauthnDialogControllerImpl::GetForPage(
diff --git a/chrome/browser/ui/autofill/chrome_autofill_client.h b/chrome/browser/ui/autofill/chrome_autofill_client.h
index 043d0d62..805f596 100644
--- a/chrome/browser/ui/autofill/chrome_autofill_client.h
+++ b/chrome/browser/ui/autofill/chrome_autofill_client.h
@@ -148,7 +148,6 @@
   void ShowMandatoryReauthOptInConfirmation() override;
 #if !BUILDFLAG(IS_ANDROID)
   void HideVirtualCardEnrollBubbleAndIconIfVisible() override;
-  void UpdateWebauthnOfferDialogWithError() override;
   bool CloseWebauthnDialog() override;
 #else  // !BUILDFLAG(IS_ANDROID)
   void ConfirmAccountNameFixFlow(
diff --git a/chrome/browser/ui/autofill/payments/chrome_payments_autofill_client.cc b/chrome/browser/ui/autofill/payments/chrome_payments_autofill_client.cc
index 77e19e3c..cb3cb11 100644
--- a/chrome/browser/ui/autofill/payments/chrome_payments_autofill_client.cc
+++ b/chrome/browser/ui/autofill/payments/chrome_payments_autofill_client.cc
@@ -44,6 +44,7 @@
 #include "chrome/browser/ui/autofill/payments/manage_migration_ui_controller.h"
 #include "chrome/browser/ui/autofill/payments/save_card_bubble_controller_impl.h"
 #include "chrome/browser/ui/autofill/payments/webauthn_dialog_controller_impl.h"
+#include "chrome/browser/ui/autofill/payments/webauthn_dialog_state.h"
 #include "components/autofill/core/browser/payments/local_card_migration_manager.h"
 #include "components/autofill/core/common/autofill_payments_features.h"
 #endif  // BUILDFLAG(IS_ANDROID)
@@ -136,6 +137,15 @@
       web_contents()->GetPrimaryPage())
       ->ShowVerifyPendingDialog(std::move(verify_pending_dialog_callback));
 }
+
+void ChromePaymentsAutofillClient::UpdateWebauthnOfferDialogWithError() {
+  WebauthnDialogControllerImpl* controller =
+      WebauthnDialogControllerImpl::GetForPage(
+          web_contents()->GetPrimaryPage());
+  if (controller) {
+    controller->UpdateDialog(WebauthnDialogState::kOfferError);
+  }
+}
 #endif  // BUILDFLAG(IS_ANDROID)
 
 void ChromePaymentsAutofillClient::CreditCardUploadCompleted(
diff --git a/chrome/browser/ui/autofill/payments/chrome_payments_autofill_client.h b/chrome/browser/ui/autofill/payments/chrome_payments_autofill_client.h
index ef6884a..e2eab9d5 100644
--- a/chrome/browser/ui/autofill/payments/chrome_payments_autofill_client.h
+++ b/chrome/browser/ui/autofill/payments/chrome_payments_autofill_client.h
@@ -77,6 +77,7 @@
       WebauthnDialogCallback offer_dialog_callback) override;
   void ShowWebauthnVerifyPendingDialog(
       WebauthnDialogCallback verify_pending_dialog_callback) override;
+  void UpdateWebauthnOfferDialogWithError() override;
 #endif  // BUILDFLAG(IS_ANDROID)
   void CreditCardUploadCompleted(bool card_saved,
                                  std::optional<OnConfirmationClosedCallback>
diff --git a/chrome/browser/ui/chromeos/magic_boost/BUILD.gn b/chrome/browser/ui/chromeos/magic_boost/BUILD.gn
index 96b9957..14d2a85 100644
--- a/chrome/browser/ui/chromeos/magic_boost/BUILD.gn
+++ b/chrome/browser/ui/chromeos/magic_boost/BUILD.gn
@@ -6,6 +6,7 @@
 
 source_set("magic_boost") {
   sources = [
+    "magic_boost_constants.h",
     "magic_boost_controller.cc",
     "magic_boost_controller.h",
     "magic_boost_disclaimer_view.cc",
diff --git a/chrome/browser/ui/chromeos/magic_boost/magic_boost_constants.h b/chrome/browser/ui/chromeos/magic_boost/magic_boost_constants.h
new file mode 100644
index 0000000..4d14df0d
--- /dev/null
+++ b/chrome/browser/ui/chromeos/magic_boost/magic_boost_constants.h
@@ -0,0 +1,18 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_CHROMEOS_MAGIC_BOOST_MAGIC_BOOST_CONSTANTS_H_
+#define CHROME_BROWSER_UI_CHROMEOS_MAGIC_BOOST_MAGIC_BOOST_CONSTANTS_H_
+
+namespace chromeos::magic_boost {
+
+// The view ids for Magic Boost related views.
+enum ViewId {
+  OptInCardSecondaryButton = 1,
+  OptInCardPrimaryButton,
+};
+
+}  // namespace chromeos::magic_boost
+
+#endif  // CHROME_BROWSER_UI_CHROMEOS_MAGIC_BOOST_MAGIC_BOOST_CONSTANTS_H_
diff --git a/chrome/browser/ui/chromeos/magic_boost/magic_boost_controller.cc b/chrome/browser/ui/chromeos/magic_boost/magic_boost_controller.cc
index 8184e86..e7c46e8 100644
--- a/chrome/browser/ui/chromeos/magic_boost/magic_boost_controller.cc
+++ b/chrome/browser/ui/chromeos/magic_boost/magic_boost_controller.cc
@@ -34,7 +34,7 @@
   CHECK(!opt_in_widget_);
   CHECK(!disclaimer_widget_);
   opt_in_widget_ = MagicBoostOptInCard::CreateWidget(anchor_view_bounds);
-  opt_in_widget_->Show();
+  opt_in_widget_->ShowInactive();
 }
 
 void MagicBoostController::CloseOptInUi() {
@@ -49,6 +49,10 @@
   disclaimer_widget_->Show();
 }
 
+void MagicBoostController::CloseDisclaimerUi() {
+  disclaimer_widget_.reset();
+}
+
 bool MagicBoostController::ShouldQuickAnswersAndMahiShowOptIn() {
   // TODO(b/339043693): Implement this function.
   return false;
diff --git a/chrome/browser/ui/chromeos/magic_boost/magic_boost_controller.h b/chrome/browser/ui/chromeos/magic_boost/magic_boost_controller.h
index ed0da34..3ac8c1c 100644
--- a/chrome/browser/ui/chromeos/magic_boost/magic_boost_controller.h
+++ b/chrome/browser/ui/chromeos/magic_boost/magic_boost_controller.h
@@ -18,6 +18,19 @@
 
 namespace chromeos {
 
+enum class HMRConsentStatus : int {
+  // User has agreed to consent by pressing "Yes/Agree" button to all dialogs
+  // from the consent window.
+  kApproved = 0,
+  // User has disagreed to consent by pressing "No/Disagree" button to any
+  // dialog from the consent window.
+  kDeclined = 1,
+  // No explicit consent to use the feature has been received yet.
+  kPending = 2,
+  // No request has been sent to users to collect their consent.
+  kUnset = 3,
+};
+
 // The controller that manages the lifetime of opt-in and disclaimer widgets.
 // Some functions in this controller are virtual for testing.
 class MagicBoostController {
@@ -31,8 +44,9 @@
   virtual void ShowOptInUi(const gfx::Rect& anchor_view_bounds);
   virtual void CloseOptInUi();
 
-  // Shows Magic Boost disclaimer widget.
+  // Shows/closes Magic Boost disclaimer widget.
   void ShowDisclaimerUi();
+  void CloseDisclaimerUi();
 
   // For testing.
   views::Widget* opt_in_widget_for_test() { return opt_in_widget_.get(); }
@@ -40,9 +54,6 @@
     return disclaimer_widget_.get();
   }
 
-  // Closes Magic Boost disclaimer widget.
-  void CloseDisclaimerUi() {}
-
   // Whether the Quick Answers and Mahi features should show the opt in UI.
   virtual bool ShouldQuickAnswersAndMahiShowOptIn();
 
diff --git a/chrome/browser/ui/chromeos/magic_boost/magic_boost_controller_unittest.cc b/chrome/browser/ui/chromeos/magic_boost/magic_boost_controller_unittest.cc
index 494c352a..aed12f2 100644
--- a/chrome/browser/ui/chromeos/magic_boost/magic_boost_controller_unittest.cc
+++ b/chrome/browser/ui/chromeos/magic_boost/magic_boost_controller_unittest.cc
@@ -14,17 +14,21 @@
 
 namespace chromeos {
 
-using MagicBoostControllerTest = ChromeViewsTestBase;
-
-TEST_F(MagicBoostControllerTest, ShowDisclaimerUi) {
+class MagicBoostControllerTest : public ChromeViewsTestBase {
+ public:
+  MagicBoostControllerTest() {
 // Sets the default functions for the test to create image with the lottie
 // resource id. Otherwise there's no `g_parse_lottie_as_still_image_` set in the
 // `ResourceBundle`.
 #if BUILDFLAG(IS_CHROMEOS_ASH)
-  ui::ResourceBundle::SetLottieParsingFunctions(
-      &lottie::ParseLottieAsStillImage, &lottie::ParseLottieAsThemedStillImage);
+    ui::ResourceBundle::SetLottieParsingFunctions(
+        &lottie::ParseLottieAsStillImage,
+        &lottie::ParseLottieAsThemedStillImage);
 #endif
+  }
+};
 
+TEST_F(MagicBoostControllerTest, DisclaimerUi) {
   auto* controller = MagicBoostController::Get();
   EXPECT_FALSE(controller->disclaimer_widget_for_test());
 
@@ -34,6 +38,10 @@
   EXPECT_TRUE(controller->disclaimer_widget_for_test()->IsVisible());
   EXPECT_TRUE(views::IsViewClass<MagicBoostDisclaimerView>(
       controller->disclaimer_widget_for_test()->GetContentsView()));
+
+  // Test that the disclaimer widget is closed on `CloseDisclaimerUI`.
+  controller->CloseDisclaimerUi();
+  EXPECT_FALSE(controller->disclaimer_widget_for_test());
 }
 
 TEST_F(MagicBoostControllerTest, OptInUi) {
diff --git a/chrome/browser/ui/chromeos/magic_boost/magic_boost_opt_in_card.cc b/chrome/browser/ui/chromeos/magic_boost/magic_boost_opt_in_card.cc
index 28d570ad..caa7d415 100644
--- a/chrome/browser/ui/chromeos/magic_boost/magic_boost_opt_in_card.cc
+++ b/chrome/browser/ui/chromeos/magic_boost/magic_boost_opt_in_card.cc
@@ -6,6 +6,8 @@
 
 #include <string>
 
+#include "chrome/browser/ui/chromeos/magic_boost/magic_boost_constants.h"
+#include "chrome/browser/ui/chromeos/magic_boost/magic_boost_controller.h"
 #include "chrome/browser/ui/views/editor_menu/utils/utils.h"
 #include "components/vector_icons/vector_icons.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -63,13 +65,6 @@
 const std::u16string secondary_button_text = u"No thanks";
 const std::u16string primary_button_text = u"Try it";
 
-// Placeholder callbacks
-// TODO(b/340940507): Implement button callbacks.
-base::RepeatingClosure primary_callback =
-    base::BindRepeating([]() { LOG(ERROR) << "Primary button pressed"; });
-base::RepeatingClosure secondary_callback =
-    base::BindRepeating([]() { LOG(ERROR) << "Secondary button pressed"; });
-
 // Font lists
 const gfx::FontList body_text_font_list =
     gfx::FontList({"Google Sans", "Roboto"},
@@ -186,15 +181,21 @@
                                       kButtonsContainerHeight))
           .AddChildren(views::Builder<views::MdTextButton>()
                            .CopyAddressTo(&secondary_button_)
+                           .SetID(magic_boost::ViewId::OptInCardSecondaryButton)
                            .SetText(secondary_button_text)
                            .SetAccessibleName(secondary_button_text)
                            .SetStyle(ui::ButtonStyle::kText)
-                           .SetCallback(std::move(secondary_callback)),
+                           .SetCallback(base::BindRepeating(
+                               &MagicBoostOptInCard::OnSecondaryButtonPressed,
+                               weak_ptr_factory_.GetWeakPtr())),
                        views::Builder<views::MdTextButton>()
+                           .SetID(magic_boost::ViewId::OptInCardPrimaryButton)
                            .SetText(primary_button_text)
                            .SetAccessibleName(primary_button_text)
                            .SetStyle(ui::ButtonStyle::kProminent)
-                           .SetCallback(std::move(primary_callback)))
+                           .SetCallback(base::BindRepeating(
+                               &MagicBoostOptInCard::OnPrimaryButtonPressed,
+                               weak_ptr_factory_.GetWeakPtr())))
           .Build());
 }
 
@@ -238,6 +239,19 @@
   secondary_button_->RequestFocus();
 }
 
+void MagicBoostOptInCard::OnPrimaryButtonPressed() {
+  auto* controller = MagicBoostController::Get();
+  controller->CloseOptInUi();
+  controller->ShowDisclaimerUi();
+}
+
+void MagicBoostOptInCard::OnSecondaryButtonPressed() {
+  auto* controller = MagicBoostController::Get();
+  controller->CloseOptInUi();
+  // TODO(b/341158134): Disable opt-in card from showing again when "No thanks"
+  // is pressed.
+}
+
 BEGIN_METADATA(MagicBoostOptInCard)
 END_METADATA
 
diff --git a/chrome/browser/ui/chromeos/magic_boost/magic_boost_opt_in_card.h b/chrome/browser/ui/chromeos/magic_boost/magic_boost_opt_in_card.h
index 38e7ce21..46dba20 100644
--- a/chrome/browser/ui/chromeos/magic_boost/magic_boost_opt_in_card.h
+++ b/chrome/browser/ui/chromeos/magic_boost/magic_boost_opt_in_card.h
@@ -6,6 +6,7 @@
 #define CHROME_BROWSER_UI_CHROMEOS_MAGIC_BOOST_MAGIC_BOOST_OPT_IN_CARD_H_
 
 #include "base/memory/raw_ptr.h"
+#include "base/memory/weak_ptr.h"
 #include "chrome/browser/ui/views/editor_menu/utils/pre_target_handler_view.h"
 #include "ui/base/metadata/metadata_header_macros.h"
 
@@ -42,7 +43,13 @@
   void RequestFocus() override;
 
  private:
+  // Button callbacks.
+  void OnPrimaryButtonPressed();
+  void OnSecondaryButtonPressed();
+
   raw_ptr<views::MdTextButton> secondary_button_ = nullptr;
+
+  base::WeakPtrFactory<MagicBoostOptInCard> weak_ptr_factory_{this};
 };
 
 }  // namespace chromeos
diff --git a/chrome/browser/ui/chromeos/magic_boost/magic_boost_opt_in_card_unittest.cc b/chrome/browser/ui/chromeos/magic_boost/magic_boost_opt_in_card_unittest.cc
new file mode 100644
index 0000000..d556de6
--- /dev/null
+++ b/chrome/browser/ui/chromeos/magic_boost/magic_boost_opt_in_card_unittest.cc
@@ -0,0 +1,96 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/chromeos/magic_boost/magic_boost_opt_in_card.h"
+
+#include <memory>
+
+#include "chrome/browser/ui/chromeos/magic_boost/magic_boost_constants.h"
+#include "chrome/browser/ui/chromeos/magic_boost/magic_boost_controller.h"
+#include "chrome/browser/ui/chromeos/magic_boost/magic_boost_disclaimer_view.h"
+#include "chrome/test/views/chrome_views_test_base.h"
+#include "ui/events/test/event_generator.h"
+#include "ui/lottie/resource.h"
+#include "ui/views/view_utils.h"
+#include "ui/views/widget/widget.h"
+#include "ui/views/widget/widget_utils.h"
+
+namespace chromeos {
+
+namespace {
+
+views::View* GetPrimaryButton(views::Widget* opt_in_widget) {
+  return opt_in_widget->GetContentsView()->GetViewByID(
+      magic_boost::ViewId::OptInCardPrimaryButton);
+}
+
+views::View* GetSecondaryButton(views::Widget* opt_in_widget) {
+  return opt_in_widget->GetContentsView()->GetViewByID(
+      magic_boost::ViewId::OptInCardSecondaryButton);
+}
+
+void LeftClickOn(views::View* view) {
+  auto* widget = view->GetWidget();
+  ASSERT_TRUE(widget);
+  ui::test::EventGenerator event_generator(GetRootWindow(widget),
+                                           widget->GetNativeWindow());
+  event_generator.MoveMouseTo(view->GetBoundsInScreen().CenterPoint());
+  event_generator.ClickLeftButton();
+}
+
+}  // namespace
+
+class MagicBoostOptInCardTest : public ChromeViewsTestBase {
+ public:
+  MagicBoostOptInCardTest() {
+// Sets the default functions for the test to create image with the lottie
+// resource id. Otherwise there's no `g_parse_lottie_as_still_image_` set in the
+// `ResourceBundle`.
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+    ui::ResourceBundle::SetLottieParsingFunctions(
+        &lottie::ParseLottieAsStillImage,
+        &lottie::ParseLottieAsThemedStillImage);
+#endif
+  }
+};
+
+TEST_F(MagicBoostOptInCardTest, ButtonActions) {
+  auto* controller = MagicBoostController::Get();
+
+  // Show the opt-in UI card.
+  controller->ShowOptInUi(/*anchor_bounds=*/gfx::Rect());
+  auto* opt_in_widget = controller->opt_in_widget_for_test();
+  ASSERT_TRUE(opt_in_widget);
+
+  // Test that pressing the primary button closes the card and shows the
+  // disclaimer UI.
+  auto* primary_button = GetPrimaryButton(opt_in_widget);
+  ASSERT_TRUE(primary_button);
+  LeftClickOn(primary_button);
+  EXPECT_FALSE(controller->opt_in_widget_for_test());
+  EXPECT_TRUE(controller->disclaimer_widget_for_test());
+
+  // Close the disclaimer UI directly without pressing its buttons, which won't
+  // set the `CanShowOptInUi` pref to false. This may happen during shutdown.
+  controller->CloseDisclaimerUi();
+
+  // Attempt re-showing the opt-in UI card. It should show since
+  // `CanShowOptInUI` pref is still true.
+  controller->ShowOptInUi(/*anchor_bounds=*/gfx::Rect());
+  ASSERT_TRUE(controller->opt_in_widget_for_test());
+
+  // Test that pressing the secondary button closes the card and sets the pref
+  // so the card won't be able to show again.
+  auto* secondary_button = GetSecondaryButton(opt_in_widget);
+  ASSERT_TRUE(secondary_button);
+  LeftClickOn(secondary_button);
+  ASSERT_FALSE(controller->opt_in_widget_for_test());
+
+  // Attempt re-showing the opt-in UI card. It should not show again since the
+  // user declined before.
+  // TODO(b/341158134): Implement can show opt-in UI pref to test that the card
+  // won't show again.
+}
+
+}  // namespace chromeos
diff --git a/chrome/browser/ui/commerce/product_specifications_entry_point_controller.cc b/chrome/browser/ui/commerce/product_specifications_entry_point_controller.cc
index b355b3e..868a1cf 100644
--- a/chrome/browser/ui/commerce/product_specifications_entry_point_controller.cc
+++ b/chrome/browser/ui/commerce/product_specifications_entry_point_controller.cc
@@ -16,14 +16,20 @@
 
 namespace {
 
-constexpr int kEligibleWindowUrlCountForNavigation = 3;
+// Number of URLs of the same cluster that a window needs to contain in order
+// for the entry point to stay valid.
+constexpr int kEligibleWindowUrlCountForValidation = 2;
+// Number of URLs of the same cluster that a window needs to contain in order
+// for the entry point to trigger for navigation.
+constexpr int kEligibleWindowUrlCountForNavigationTriggering = 3;
 
-bool IsNavigationEligibleForEntryPoint(
+bool CheckWindowContainsEntryPointURLs(
     TabStripModel* tab_strip_model,
-    commerce::EntryPointInfo entry_point_info) {
+    commerce::EntryPointInfo entry_point_info,
+    size_t threshold) {
   std::set<GURL> similar_urls =
       entry_point_info.similar_candidate_products_urls;
-  if (similar_urls.size() < kEligibleWindowUrlCountForNavigation) {
+  if (similar_urls.size() < threshold) {
     return false;
   }
   std::set<GURL> eligible_urls_in_current_window;
@@ -31,14 +37,26 @@
     GURL tab_url = tab_strip_model->GetWebContentsAt(i)->GetLastCommittedURL();
     if (similar_urls.find(tab_url) != similar_urls.end()) {
       eligible_urls_in_current_window.insert(tab_url);
-      if (eligible_urls_in_current_window.size() >=
-          kEligibleWindowUrlCountForNavigation) {
+      if (eligible_urls_in_current_window.size() >= threshold) {
         return true;
       }
     }
   }
-  return eligible_urls_in_current_window.size() >=
-         kEligibleWindowUrlCountForNavigation;
+  return eligible_urls_in_current_window.size() >= threshold;
+}
+
+bool IsWindowValidForEntryPoint(TabStripModel* tab_strip_model,
+                                commerce::EntryPointInfo entry_point_info) {
+  return CheckWindowContainsEntryPointURLs(
+      tab_strip_model, entry_point_info, kEligibleWindowUrlCountForValidation);
+}
+
+bool IsNavigationEligibleForEntryPoint(
+    TabStripModel* tab_strip_model,
+    commerce::EntryPointInfo entry_point_info) {
+  return CheckWindowContainsEntryPointURLs(
+      tab_strip_model, entry_point_info,
+      kEligibleWindowUrlCountForNavigationTriggering);
 }
 
 }  // namespace
@@ -66,6 +84,9 @@
     TabStripModel* tab_strip_model,
     const TabStripModelChange& change,
     const TabStripSelectionChange& selection) {
+  if (change.type() == TabStripModelChange::Type::kRemoved) {
+    MaybeHideEntryPoint();
+  }
   // Filter out non-tab-selection events.
   if (change.type() != TabStripModelChange::Type::kSelectionOnly ||
       !selection.active_tab_changed() || !selection.old_contents ||
@@ -88,8 +109,10 @@
     content::WebContents* contents,
     int index,
     TabChangeType change_type) {
-  if (change_type == TabChangeType::kAll) {
+  if (change_type == TabChangeType::kAll &&
+      contents->GetLastCommittedURL() != last_committed_url_) {
     last_committed_url_ = contents->GetLastCommittedURL();
+    MaybeHideEntryPoint();
   }
 }
 
@@ -149,4 +172,15 @@
     observer.ShowEntryPointWithTitle(entry_point_info->title);
   }
 }
+
+void ProductSpecificationsEntryPointController::MaybeHideEntryPoint() {
+  if (!current_entry_point_info_.has_value() ||
+      IsWindowValidForEntryPoint(browser_->tab_strip_model(),
+                                 current_entry_point_info_.value())) {
+    return;
+  }
+  for (auto& observer : observers_) {
+    observer.HideEntryPoint();
+  }
+}
 }  // namespace commerce
diff --git a/chrome/browser/ui/commerce/product_specifications_entry_point_controller.h b/chrome/browser/ui/commerce/product_specifications_entry_point_controller.h
index 513e28b..1d849c8d 100644
--- a/chrome/browser/ui/commerce/product_specifications_entry_point_controller.h
+++ b/chrome/browser/ui/commerce/product_specifications_entry_point_controller.h
@@ -29,6 +29,9 @@
    public:
     // Called when entry points should show with `title`.
     virtual void ShowEntryPointWithTitle(const std::string title) {}
+
+    // Called when entry points should hide.
+    virtual void HideEntryPoint() {}
   };
 
   explicit ProductSpecificationsEntryPointController(Browser* browser);
@@ -69,6 +72,8 @@
   }
 
  private:
+  void MaybeHideEntryPoint();
+
   // Info of the entry point that is currently showing, when available.
   std::optional<EntryPointInfo> current_entry_point_info_;
   raw_ptr<Browser, DanglingUntriaged> browser_;
diff --git a/chrome/browser/ui/commerce/product_specifications_entry_point_controller_browsertest.cc b/chrome/browser/ui/commerce/product_specifications_entry_point_controller_browsertest.cc
index a6c5a27e5..05ffd80 100644
--- a/chrome/browser/ui/commerce/product_specifications_entry_point_controller_browsertest.cc
+++ b/chrome/browser/ui/commerce/product_specifications_entry_point_controller_browsertest.cc
@@ -14,6 +14,7 @@
 #include "components/commerce/core/product_specifications/product_specifications_service.h"
 #include "components/commerce/core/product_specifications/product_specifications_set.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
+#include "components/sync/test/mock_model_type_change_processor.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -34,12 +35,16 @@
               ShowEntryPointWithTitle,
               (const std::string title),
               (override));
+  MOCK_METHOD(void, HideEntryPoint, (), (override));
 };
 
 class MockProductSpecificationsService
     : public commerce::ProductSpecificationsService {
  public:
-  MockProductSpecificationsService() : ProductSpecificationsService(nullptr) {}
+  MockProductSpecificationsService()
+      : ProductSpecificationsService(
+            base::DoNothing(),
+            std::make_unique<syncer::MockModelTypeChangeProcessor>()) {}
   ~MockProductSpecificationsService() override = default;
   MOCK_METHOD(const std::optional<commerce::ProductSpecificationsSet>,
               AddProductSpecificationsSet,
@@ -64,6 +69,8 @@
     controller_ =
         std::make_unique<commerce::ProductSpecificationsEntryPointController>(
             browser());
+    observer_ = std::make_unique<MockObserver>();
+    controller_->AddObserver(observer_.get());
   }
 
   void SetUpInProcessBrowserTestFixture() override {
@@ -79,6 +86,8 @@
     is_browser_context_services_created = false;
   }
 
+  void TearDown() override { controller_->RemoveObserver(observer_.get()); }
+
   void OnWillCreateBrowserContextServices(content::BrowserContext* context) {
     is_browser_context_services_created = true;
     commerce::ShoppingServiceFactory::GetInstance()->SetTestingFactory(
@@ -94,6 +103,7 @@
   std::unique_ptr<commerce::ProductSpecificationsEntryPointController>
       controller_;
   base::CallbackListSubscription create_services_subscription_;
+  std::unique_ptr<MockObserver> observer_;
   bool is_browser_context_services_created{false};
 
  private:
@@ -109,9 +119,7 @@
   mock_shopping_service_->SetResponseForGetEntryPointInfoForSelection(info);
 
   // Set up observer.
-  MockObserver observer;
-  controller_->AddObserver(&observer);
-  EXPECT_CALL(observer, ShowEntryPointWithTitle(kTitle)).Times(1);
+  EXPECT_CALL(*observer_, ShowEntryPointWithTitle(kTitle)).Times(1);
 
   // Create two tabs and simulate selection.
   ASSERT_TRUE(AddTabAtIndexToBrowser(browser(), 0, GURL(kTestUrl1),
@@ -143,9 +151,7 @@
   mock_shopping_service_->SetResponseForGetEntryPointInfoForNavigation(info);
 
   // Set up observer.
-  MockObserver observer;
-  controller_->AddObserver(&observer);
-  EXPECT_CALL(observer, ShowEntryPointWithTitle(kTitle)).Times(1);
+  EXPECT_CALL(*observer_, ShowEntryPointWithTitle(kTitle)).Times(1);
 
   // Current window has to have more than three unique tabs that are similar in
   // order to trigger the entry point for navigation.
@@ -227,3 +233,73 @@
   ASSERT_EQ(commerce::GetProductSpecsTabUrlForID(uuid),
             current_tab->GetVisibleURL());
 }
+
+IN_PROC_BROWSER_TEST_F(ProductSpecificationsEntryPointControllerBrowserTest,
+                       InvalidEntryPointWithNavigation) {
+  // Mock EntryPointInfo returned by ShoppingService.
+  std::set<GURL> urls = {GURL(kTestUrl2), GURL(kTestUrl3), GURL(kTestUrl4)};
+  auto info = std::make_optional<commerce::EntryPointInfo>(kTitle, urls);
+  mock_shopping_service_->SetResponseForGetEntryPointInfoForNavigation(info);
+
+  // Set up observer.
+  EXPECT_CALL(*observer_, ShowEntryPointWithTitle(kTitle)).Times(1);
+
+  // Trigger entry point with navigations.
+  std::vector<std::string> urls_to_open = {kTestUrl2, kTestUrl3, kTestUrl4};
+  for (auto& url : urls_to_open) {
+    ASSERT_TRUE(AddTabAtIndexToBrowser(browser(), 0, GURL(url),
+                                       ui::PAGE_TRANSITION_LINK, true));
+    base::RunLoop().RunUntilIdle();
+    controller_->OnClusterFinishedForNavigation(GURL(url));
+  }
+  ASSERT_TRUE(controller_->entry_point_info_for_testing().has_value());
+
+  // Navigate to a URL that is not in cluster. After this navigation, there are
+  // two URLs in this window that belong to the cluster, and the entry point is
+  // still valid.
+  auto* web_contents_one = browser()->tab_strip_model()->GetWebContentsAt(0);
+  ASSERT_EQ(web_contents_one->GetLastCommittedURL(), GURL(kTestUrl4));
+  ASSERT_TRUE(content::NavigateToURL(web_contents_one, GURL(kTestUrl1)));
+
+  // Navigate to a URL that is not in cluster. After this navigation, there is
+  // one URL in this window that belong to the cluster, and the entry point is
+  // no longer valid.
+  EXPECT_CALL(*observer_, HideEntryPoint()).Times(1);
+  auto* web_contents_two = browser()->tab_strip_model()->GetWebContentsAt(1);
+  ASSERT_EQ(web_contents_two->GetLastCommittedURL(), GURL(kTestUrl3));
+  ASSERT_TRUE(content::NavigateToURL(web_contents_two, GURL(kTestUrl1)));
+}
+
+IN_PROC_BROWSER_TEST_F(ProductSpecificationsEntryPointControllerBrowserTest,
+                       InvalidEntryPointWithClosure) {
+  // Mock EntryPointInfo returned by ShoppingService.
+  std::set<GURL> urls = {GURL(kTestUrl2), GURL(kTestUrl3), GURL(kTestUrl4)};
+  auto info = std::make_optional<commerce::EntryPointInfo>(kTitle, urls);
+  mock_shopping_service_->SetResponseForGetEntryPointInfoForNavigation(info);
+
+  // Set up observer.
+  EXPECT_CALL(*observer_, ShowEntryPointWithTitle(kTitle)).Times(1);
+
+  // Trigger entry point with navigations.
+  std::vector<std::string> urls_to_open = {kTestUrl2, kTestUrl3, kTestUrl4};
+  for (auto& url : urls_to_open) {
+    ASSERT_TRUE(AddTabAtIndexToBrowser(browser(), 0, GURL(url),
+                                       ui::PAGE_TRANSITION_LINK, true));
+    base::RunLoop().RunUntilIdle();
+    controller_->OnClusterFinishedForNavigation(GURL(url));
+  }
+  ASSERT_TRUE(controller_->entry_point_info_for_testing().has_value());
+
+  // Close a tab with URL that is in the cluster. After this closure, there are
+  // two URLs in this window that belong to the cluster, and the entry point is
+  // still valid.
+  browser()->tab_strip_model()->CloseWebContentsAt(/*index=*/0,
+                                                   TabCloseTypes::CLOSE_NONE);
+
+  // Close a tab with URL that is in the cluster. After this closure, there is
+  // one URL in this window that belong to the cluster, and the entry point is
+  // no longer valid.
+  EXPECT_CALL(*observer_, HideEntryPoint()).Times(testing::AtLeast(1));
+  browser()->tab_strip_model()->CloseWebContentsAt(/*index=*/0,
+                                                   TabCloseTypes::CLOSE_NONE);
+}
diff --git a/chrome/browser/ui/passwords/manage_passwords_test.cc b/chrome/browser/ui/passwords/manage_passwords_test.cc
index ca6d9a1..f875a992 100644
--- a/chrome/browser/ui/passwords/manage_passwords_test.cc
+++ b/chrome/browser/ui/passwords/manage_passwords_test.cc
@@ -294,7 +294,7 @@
        password_manager::InsecurityMetadata(
            base::Time(), password_manager::IsMuted(false),
            password_manager::TriggerBackendNotification(false))});
-  fetcher_.set_insecure_credentials({&insecure_credential_});
+  fetcher_.set_insecure_credentials({insecure_credential_});
 
   fetcher_.NotifyFetchCompleted();
 
diff --git a/chrome/browser/ui/passwords/manage_passwords_ui_controller_unittest.cc b/chrome/browser/ui/passwords/manage_passwords_ui_controller_unittest.cc
index 46e24b1..ba83171 100644
--- a/chrome/browser/ui/passwords/manage_passwords_ui_controller_unittest.cc
+++ b/chrome/browser/ui/passwords/manage_passwords_ui_controller_unittest.cc
@@ -271,8 +271,7 @@
           Return(base::span<const password_manager::InteractionsStats>()));
   EXPECT_CALL(*form_manager, GetInsecureCredentials())
       .Times(AtMost(1))
-      .WillOnce(Return(std::vector<raw_ptr<const password_manager::PasswordForm,
-                                           VectorExperimental>>()));
+      .WillOnce(Return(base::span<const password_manager::PasswordForm>()));
   EXPECT_CALL(*form_manager, GetPendingCredentials())
       .WillRepeatedly(ReturnRef(*password_form));
   EXPECT_CALL(*form_manager, GetMetricsRecorder())
@@ -1247,8 +1246,9 @@
         base::MakeRefCounted<password_manager::PasswordFormMetricsRecorder>(
             true /*is_main_frame_secure*/, source_id, /*pref_service=*/nullptr);
     std::vector<PasswordForm