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..51b78d0d 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 de75b7011..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 99b69fa5..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 885aea42..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 03316331..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> matches = {test_local_form()};
-    if (is_update)
+    if (is_update) {
       matches.push_back(test_local_form());
+    }
     auto test_form_manager =
         CreateFormManagerWithBestMatches(matches, &submitted_form());
     EXPECT_CALL(*test_form_manager, GetMetricsRecorder)
@@ -1835,9 +1835,7 @@
   // Pretend that the current credential was insecure but with the updated
   // password not anymore.
   EXPECT_CALL(*test_form_manager_raw, GetInsecureCredentials())
-      .WillOnce(
-          Return(std::vector<raw_ptr<const PasswordForm, VectorExperimental>>{
-              &credential}));
+      .WillOnce(Return(std::vector<PasswordForm>{credential}));
   base::WeakPtr<password_manager::PasswordStoreConsumer> post_save_helper;
 
   EXPECT_CALL(*client().GetProfilePasswordStore(), GetAutofillableLogins)
@@ -1879,9 +1877,7 @@
   // Pretend that the current credential was insecure.
   PasswordForm credential = CreateInsecureCredential(test_local_form());
   EXPECT_CALL(*test_form_manager_raw, GetInsecureCredentials())
-      .WillOnce(
-          Return(std::vector<raw_ptr<const PasswordForm, VectorExperimental>>{
-              &credential}));
+      .WillOnce(Return(std::vector<PasswordForm>{credential}));
 
   base::WeakPtr<password_manager::PasswordStoreConsumer> post_save_helper;
 
@@ -1928,9 +1924,7 @@
   PasswordForm credential = CreateInsecureCredential(test_local_form());
   // Pretend that the current credential was insecure.
   EXPECT_CALL(*test_form_manager_raw, GetInsecureCredentials())
-      .WillOnce(
-          Return(std::vector<raw_ptr<const PasswordForm, VectorExperimental>>{
-              &credential}));
+      .WillOnce(Return(std::vector<PasswordForm>{credential}));
   controller()->SavePassword(submitted_form().username_value,
                              submitted_form().password_value);
   // The sign-in promo bubble stays open, the warning isn't shown.
diff --git a/chrome/browser/ui/startup/default_browser_prompt/default_browser_prompt_manager.cc b/chrome/browser/ui/startup/default_browser_prompt/default_browser_prompt_manager.cc
index fb2368a..292df3d 100644
--- a/chrome/browser/ui/startup/default_browser_prompt/default_browser_prompt_manager.cc
+++ b/chrome/browser/ui/startup/default_browser_prompt/default_browser_prompt_manager.cc
@@ -167,8 +167,13 @@
 
     app_menu_prompt_dismiss_timer_.Start(
         FROM_HERE, app_menu_remaining_duration, base::BindOnce([]() {
-          chrome::startup::default_prompt::UpdatePrefsForDismissedPrompt(
-              BrowserList::GetInstance()->GetLastActive()->profile());
+          Browser* last_active = BrowserList::GetInstance()->GetLastActive();
+          // If there is no active browser, just dismiss the prompts and the
+          // prefs will be updated on the next startup.
+          if (last_active) {
+            chrome::startup::default_prompt::UpdatePrefsForDismissedPrompt(
+                last_active->profile());
+          }
           DefaultBrowserPromptManager::GetInstance()->CloseAllPrompts(
               CloseReason::kDismiss);
         }));
diff --git a/chrome/browser/ui/startup/default_browser_prompt/default_browser_prompt_manager_unittest.cc b/chrome/browser/ui/startup/default_browser_prompt/default_browser_prompt_manager_unittest.cc
index 5309b25..e422a25 100644
--- a/chrome/browser/ui/startup/default_browser_prompt/default_browser_prompt_manager_unittest.cc
+++ b/chrome/browser/ui/startup/default_browser_prompt/default_browser_prompt_manager_unittest.cc
@@ -8,6 +8,7 @@
 
 #include "base/test/scoped_feature_list.h"
 #include "base/time/time.h"
+#include "chrome/browser/ui/browser_list.h"
 #include "chrome/browser/ui/startup/default_browser_prompt/default_browser_prompt_prefs.h"
 #include "chrome/browser/ui/ui_features.h"
 #include "chrome/common/pref_names.h"
@@ -386,3 +387,22 @@
             local_state()->GetTime(prefs::kDefaultBrowserLastDeclinedTime));
   EXPECT_EQ(2, local_state()->GetInteger(prefs::kDefaultBrowserDeclinedCount));
 }
+
+// This is a regression test for a crash that occurred when the default prompt
+// timer expired after a browser was closed.
+TEST_F(DefaultBrowserPromptManagerTest, DoesNotWritePrefWhenBrowserIsClosed) {
+  EnableDefaultBrowserPromptRefreshFeatureWithParams(
+      {{features::kShowDefaultBrowserAppMenuChip.name, "true"},
+       {features::kDefaultBrowserAppMenuDuration.name, "1d"}});
+  chrome::startup::default_prompt::MaybeResetAppMenuPromptPrefs(profile());
+  manager()->MaybeShowPrompt();
+  EXPECT_TRUE(manager()->get_show_app_menu_prompt());
+  EXPECT_EQ(0, local_state()->GetInteger(prefs::kDefaultBrowserDeclinedCount));
+
+  // Close browser then trigger timer
+  BrowserList::RemoveBrowser(BrowserList::GetInstance()->GetLastActive());
+  task_environment()->FastForwardBy(base::Days(1));
+
+  EXPECT_FALSE(manager()->get_show_app_menu_prompt());
+  EXPECT_EQ(0, local_state()->GetInteger(prefs::kDefaultBrowserDeclinedCount));
+}
diff --git a/chrome/browser/ui/tabs/recent_tabs_sub_menu_model_unittest.cc b/chrome/browser/ui/tabs/recent_tabs_sub_menu_model_unittest.cc
index 25eccc1..6a2250e 100644
--- a/chrome/browser/ui/tabs/recent_tabs_sub_menu_model_unittest.cc
+++ b/chrome/browser/ui/tabs/recent_tabs_sub_menu_model_unittest.cc
@@ -202,10 +202,6 @@
  public:
   void UpdateTypeAndSeverity(
       AppMenuIconController::TypeAndSeverity type_and_severity) override {}
-  SkColor GetDefaultColorForSeverity(
-      AppMenuIconController::Severity severity) const override {
-    return gfx::kPlaceholderColor;
-  }
 };
 
 TEST_P(RecentTabsSubMenuModelTest, LogMenuMetricsForShowHistory) {
diff --git a/chrome/browser/ui/toolbar/app_menu_icon_controller.cc b/chrome/browser/ui/toolbar/app_menu_icon_controller.cc
index 9a39636..926ac83 100644
--- a/chrome/browser/ui/toolbar/app_menu_icon_controller.cc
+++ b/chrome/browser/ui/toolbar/app_menu_icon_controller.cc
@@ -152,15 +152,6 @@
   return {IconType::NONE, Severity::NONE};
 }
 
-SkColor AppMenuIconController::GetIconColor(
-    const std::optional<SkColor>& severity_none_color) const {
-  const Severity severity = GetTypeAndSeverity().severity;
-  return ((severity == AppMenuIconController::Severity::NONE) &&
-          severity_none_color.has_value())
-             ? severity_none_color.value()
-             : delegate_->GetDefaultColorForSeverity(severity);
-}
-
 void AppMenuIconController::OnGlobalErrorsChanged() {
   UpdateDelegate();
 }
diff --git a/chrome/browser/ui/toolbar/app_menu_icon_controller.h b/chrome/browser/ui/toolbar/app_menu_icon_controller.h
index 6f63138e..ba81cb1c 100644
--- a/chrome/browser/ui/toolbar/app_menu_icon_controller.h
+++ b/chrome/browser/ui/toolbar/app_menu_icon_controller.h
@@ -55,9 +55,6 @@
     // |type_and_severity|.
     virtual void UpdateTypeAndSeverity(TypeAndSeverity type_and_severity) = 0;
 
-    // Get the appropriate colors for various severity levels.
-    virtual SkColor GetDefaultColorForSeverity(Severity severity) const = 0;
-
    protected:
     virtual ~Delegate() {}
   };
@@ -83,11 +80,6 @@
   // Returns the icon type and severity based on the current state.
   TypeAndSeverity GetTypeAndSeverity() const;
 
-  // Gets the color to be used for the app menu's icon.
-  // |severity_none_color|, if provided, will be used when the Severity is NONE.
-  // Otherwise the basic toolbar button icon color will be used.
-  SkColor GetIconColor(const std::optional<SkColor>& severity_none_color) const;
-
  private:
   // GlobalErrorObserver:
   void OnGlobalErrorsChanged() override;
diff --git a/chrome/browser/ui/toolbar/app_menu_model_unittest.cc b/chrome/browser/ui/toolbar/app_menu_model_unittest.cc
index 2a4f860..2e2d49e 100644
--- a/chrome/browser/ui/toolbar/app_menu_model_unittest.cc
+++ b/chrome/browser/ui/toolbar/app_menu_model_unittest.cc
@@ -104,10 +104,6 @@
   // AppMenuIconController::Delegate:
   void UpdateTypeAndSeverity(
       AppMenuIconController::TypeAndSeverity type_and_severity) override {}
-  SkColor GetDefaultColorForSeverity(
-      AppMenuIconController::Severity severity) const override {
-    return gfx::kPlaceholderColor;
-  }
 };
 
 }  // namespace
diff --git a/chrome/browser/ui/user_education/recent_session_policy.cc b/chrome/browser/ui/user_education/recent_session_policy.cc
index aa5a4be..2ee1372 100644
--- a/chrome/browser/ui/user_education/recent_session_policy.cc
+++ b/chrome/browser/ui/user_education/recent_session_policy.cc
@@ -17,34 +17,34 @@
 
 constexpr int kMaxRecords = RecentSessionTracker::kMaxRecentSessionRecords;
 
-std::optional<int> GetActivePeriods(const RecentSessionData& recent_sessions,
-                                    int num_periods,
-                                    int period_length_in_days,
-                                    int min_count) {
-  const base::Time end =
-      (recent_sessions.recent_session_start_times.front() + base::Days(1))
-          .LocalMidnight();
-  const base::Time start =
-      end - base::Days(num_periods * period_length_in_days);
+// Gets midnight at the start of the next local day.
+// Note: inaccurate if the time is *exactly* midnight, but this will happen so
+// rarely that it's not worth worrying about.
+base::Time GetEndOfDay(base::Time time) {
+  return (time + base::Days(1)).LocalMidnight();
+}
+
+// Counts the number of active days in `recent_sessions` going back `num_days`
+// from `last_day`. Returns null if session data recording does not go back far
+// enough to cover the whole span.
+std::optional<int> CountActiveDays(const RecentSessionData& recent_sessions,
+                                   base::Time last_day,
+                                   int num_days) {
+  const base::Time end = GetEndOfDay(last_day);
+  const base::Time start = end - base::Days(num_days);
+
   if (recent_sessions.enabled_time > start) {
     return std::nullopt;
   }
 
-  const base::TimeDelta period_length = base::Days(period_length_in_days);
-  std::vector<int> active_periods(num_periods, 0);
+  std::vector<bool> active_days(num_days, false);
   for (const auto& start_time : recent_sessions.recent_session_start_times) {
-    if (start_time < start) {
-      continue;
+    if (start_time >= start && start_time < end) {
+      const size_t index = (start_time - start) / base::Days(1);
+      active_days[index] = true;
     }
-    if (start_time >= end) {
-      ++active_periods.back();
-      continue;
-    }
-    const size_t index = (start_time - start) / period_length;
-    ++active_periods[index];
   }
-  return std::count_if(active_periods.begin(), active_periods.end(),
-                       [min_count](int value) { return value >= min_count; });
+  return std::count(active_days.begin(), active_days.end(), true);
 }
 
 std::optional<int> ValueOrNull(int value) {
@@ -71,12 +71,27 @@
 
 std::optional<int> RecentSessionPolicyImpl::ActiveDaysConstraint::GetCount(
     const RecentSessionData& recent_sessions) const {
-  return GetActivePeriods(recent_sessions, days_, 1, 1);
+  return CountActiveDays(recent_sessions,
+                         recent_sessions.recent_session_start_times.front(),
+                         days_);
 }
 
 std::optional<int> RecentSessionPolicyImpl::ActiveWeeksConstraint::GetCount(
     const RecentSessionData& recent_sessions) const {
-  return GetActivePeriods(recent_sessions, weeks_, 7, active_days_);
+  int count = 0;
+  base::Time counting_back_from =
+      recent_sessions.recent_session_start_times.front();
+  for (int week = 0; week < weeks_; ++week) {
+    const auto active_days =
+        CountActiveDays(recent_sessions, counting_back_from, 7);
+    if (!active_days) {
+      return active_days;
+    } else if (*active_days >= active_days_) {
+      ++count;
+    }
+    counting_back_from -= base::Days(7);
+  }
+  return count;
 }
 
 RecentSessionPolicyImpl::ConstraintInfo::ConstraintInfo() = default;
@@ -145,6 +160,8 @@
       kAllowRecentSessionTracking, "max_active_days", 3);
   const int super_active_days = base::GetFieldTrialParamByFeatureAsInt(
       kAllowRecentSessionTracking, "super_active_days", 4);
+  const int max_monthly_active_days = base::GetFieldTrialParamByFeatureAsInt(
+      kAllowRecentSessionTracking, "max_monthly_active_days", 0);
   const int max_super_active_weeks = base::GetFieldTrialParamByFeatureAsInt(
       kAllowRecentSessionTracking, "max_super_active_weeks", 0);
   const int max_weekly_sessions = base::GetFieldTrialParamByFeatureAsInt(
@@ -155,6 +172,9 @@
   result.emplace_back(std::make_unique<ActiveDaysConstraint>(kShortTermDays),
                       "UserEducation.Session.RecentActiveDays", kShortTermDays,
                       ValueOrNull(max_active_days));
+  result.emplace_back(std::make_unique<ActiveDaysConstraint>(kLongTermDays),
+                      "UserEducation.Session.MonthlyActiveDays", kLongTermDays,
+                      ValueOrNull(max_monthly_active_days));
   result.emplace_back(
       std::make_unique<ActiveWeeksConstraint>(kLongTermWeeks, 1),
       "UserEducation.Session.RecentActiveWeeks", kLongTermWeeks,
diff --git a/chrome/browser/ui/user_education/recent_session_policy_unittest.cc b/chrome/browser/ui/user_education/recent_session_policy_unittest.cc
index 0e608ff..f31e308 100644
--- a/chrome/browser/ui/user_education/recent_session_policy_unittest.cc
+++ b/chrome/browser/ui/user_education/recent_session_policy_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/ui/user_education/recent_session_policy.h"
 
+#include <sstream>
 #include <vector>
 
 #include "base/containers/map_util.h"
@@ -462,6 +463,7 @@
       CreateSessionData({base::Days(1), base::Days(2)}, base::Days(4)));
   EnsureBucketCounts("UserEducation.Session.ShortTermCount", {});
   EnsureBucketCounts("UserEducation.Session.LongTermCount", {});
+  EnsureBucketCounts("UserEducation.Session.MonthlyActiveDays", {});
   EnsureBucketCounts("UserEducation.Session.RecentActiveDays", {});
   EnsureBucketCounts("UserEducation.Session.RecentActiveWeeks", {});
   EnsureBucketCounts("UserEducation.Session.RecentSuperActiveWeeks", {});
@@ -474,6 +476,7 @@
                         base::Days(8)));
   EnsureBucketCounts("UserEducation.Session.ShortTermCount", {{5, 1}});
   EnsureBucketCounts("UserEducation.Session.LongTermCount", {});
+  EnsureBucketCounts("UserEducation.Session.MonthlyActiveDays", {});
   EnsureBucketCounts("UserEducation.Session.RecentActiveDays", {{4, 1}});
   EnsureBucketCounts("UserEducation.Session.RecentActiveWeeks", {});
   EnsureBucketCounts("UserEducation.Session.RecentSuperActiveWeeks", {});
@@ -485,6 +488,25 @@
        base::Days(6), base::Days(15), base::Days(20)}));
   EnsureBucketCounts("UserEducation.Session.ShortTermCount", {{5, 1}});
   EnsureBucketCounts("UserEducation.Session.LongTermCount", {{7, 1}});
+  EnsureBucketCounts("UserEducation.Session.MonthlyActiveDays", {{6, 1}});
+  EnsureBucketCounts("UserEducation.Session.RecentActiveDays", {{4, 1}});
+  EnsureBucketCounts("UserEducation.Session.RecentActiveWeeks", {{2, 1}});
+  EnsureBucketCounts("UserEducation.Session.RecentSuperActiveWeeks", {{1, 1}});
+}
+
+TEST_F(RecentSessionPolicyTest,
+       RecordRecentUsageMetrics_SuperActiveCountsDaysNotSessions) {
+  policy_->RecordRecentUsageMetrics(CreateSessionData(
+      {// Initial week with four active days (counting day zero) and no
+       // additional sessions.
+       base::Days(1), base::Days(2), base::Days(6),
+       // Second week with three active days but four sessions.
+       base::Days(15), base::Days(15) + base::Minutes(5),
+       base::Days(15) + base::Minutes(10),
+       base::Days(15) + base::Minutes(15)}));
+  EnsureBucketCounts("UserEducation.Session.ShortTermCount", {{4, 1}});
+  EnsureBucketCounts("UserEducation.Session.LongTermCount", {{8, 1}});
+  EnsureBucketCounts("UserEducation.Session.MonthlyActiveDays", {{5, 1}});
   EnsureBucketCounts("UserEducation.Session.RecentActiveDays", {{4, 1}});
   EnsureBucketCounts("UserEducation.Session.RecentActiveWeeks", {{2, 1}});
   EnsureBucketCounts("UserEducation.Session.RecentSuperActiveWeeks", {{1, 1}});
diff --git a/chrome/browser/ui/views/apps/app_dialog/app_dialog_view_browsertest.cc b/chrome/browser/ui/views/apps/app_dialog/app_dialog_view_browsertest.cc
index 03923bdc..a06d0ce 100644
--- a/chrome/browser/ui/views/apps/app_dialog/app_dialog_view_browsertest.cc
+++ b/chrome/browser/ui/views/apps/app_dialog/app_dialog_view_browsertest.cc
@@ -23,6 +23,7 @@
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/test/test_browser_dialog.h"
 #include "chrome/browser/ui/views/apps/app_dialog/app_block_dialog_view.h"
+#include "chrome/browser/ui/views/apps/app_dialog/app_local_block_dialog_view.h"
 #include "chrome/browser/ui/views/apps/app_dialog/app_pause_dialog_view.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "components/services/app_service/public/cpp/app_types.h"
@@ -64,8 +65,13 @@
   }
 
   AppDialogView* ActiveView(const std::string& name) {
-    if (name == "block")
+    if (name == "block") {
       return AppBlockDialogView::GetActiveViewForTesting();
+    }
+
+    if (name == "localblock") {
+      return AppLocalBlockDialogView::GetActiveViewForTesting();
+    }
 
     return AppPauseDialogView::GetActiveViewForTesting();
   }
@@ -108,6 +114,13 @@
       app_instance_->SendRefreshAppList(apps);
       app_service_proxy_->Launch(app_id_, ui::EF_NONE,
                                  apps::LaunchSource::kFromChromeInternal);
+    } else if (name == "localblock") {
+      app_service_proxy_->BlockApps({app_id_});
+      app_service_proxy_->SetDialogCreatedCallbackForTesting(
+          run_loop.QuitClosure());
+      app_service_proxy_->Launch(app_id_, ui::EF_NONE,
+                                 apps::LaunchSource::kFromChromeInternal);
+
     } else {
       std::map<std::string, apps::PauseData> pause_data;
       pause_data[app_id_].hours = 3;
@@ -131,6 +144,16 @@
           });
 
       EXPECT_TRUE(state_is_set);
+    } else if (name == "localblock") {
+      bool state_is_set = false;
+      app_service_proxy_->AppRegistryCache().ForOneApp(
+          app_id_, [&state_is_set](const apps::AppUpdate& update) {
+            state_is_set = (update.Readiness() ==
+                            apps::Readiness::kDisabledByLocalSettings);
+          });
+
+      EXPECT_TRUE(state_is_set);
+
     } else {
       if (name == "pause_close")
         ActiveView(name)->Close();
@@ -151,6 +174,10 @@
   ShowAndVerifyUi();
 }
 
+IN_PROC_BROWSER_TEST_F(AppDialogViewBrowserTest, InvokeUi_localblock) {
+  ShowAndVerifyUi();
+}
+
 IN_PROC_BROWSER_TEST_F(AppDialogViewBrowserTest, InvokeUi_pause) {
   ShowAndVerifyUi();
   EXPECT_TRUE(IsAppPaused());
diff --git a/chrome/browser/ui/views/apps/app_dialog/app_local_block_dialog_view.cc b/chrome/browser/ui/views/apps/app_dialog/app_local_block_dialog_view.cc
new file mode 100644
index 0000000..ed180126
--- /dev/null
+++ b/chrome/browser/ui/views/apps/app_dialog/app_local_block_dialog_view.cc
@@ -0,0 +1,52 @@
+// 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/views/apps/app_dialog/app_local_block_dialog_view.h"
+
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/apps/app_service/app_service_proxy.h"
+#include "chrome/grit/generated_resources.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/chromeos/devicetype_utils.h"
+#include "ui/gfx/image/image_skia.h"
+#include "ui/views/window/dialog_delegate.h"
+
+namespace {
+
+AppLocalBlockDialogView* g_app_local_block_dialog_view = nullptr;
+
+}  // namespace
+
+// static
+void apps::AppServiceProxy::CreateLocalBlockDialog(
+    const std::string& app_name,
+    const gfx::ImageSkia& image) {
+  views::DialogDelegate::CreateDialogWidget(
+      new AppLocalBlockDialogView(app_name, image), nullptr, nullptr)
+      ->Show();
+}
+
+AppLocalBlockDialogView::AppLocalBlockDialogView(const std::string& app_name,
+                                                 const gfx::ImageSkia& image)
+    : AppDialogView(ui::ImageModel::FromImageSkia(image)) {
+  SetTitle(l10n_util::GetStringFUTF16(IDS_APP_LOCAL_BLOCK_PROMPT_TITLE,
+                                      base::UTF8ToUTF16(app_name),
+                                      ui::GetChromeOSDeviceName()));
+
+  std::u16string heading_text =
+      l10n_util::GetStringUTF16(IDS_APP_LOCAL_BLOCK_HEADING);
+
+  InitializeView(heading_text);
+
+  g_app_local_block_dialog_view = this;
+}
+
+AppLocalBlockDialogView::~AppLocalBlockDialogView() {
+  g_app_local_block_dialog_view = nullptr;
+}
+
+// static
+AppLocalBlockDialogView* AppLocalBlockDialogView::GetActiveViewForTesting() {
+  return g_app_local_block_dialog_view;
+}
diff --git a/chrome/browser/ui/views/apps/app_dialog/app_local_block_dialog_view.h b/chrome/browser/ui/views/apps/app_dialog/app_local_block_dialog_view.h
new file mode 100644
index 0000000..bb60a15
--- /dev/null
+++ b/chrome/browser/ui/views/apps/app_dialog/app_local_block_dialog_view.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 CHROME_BROWSER_UI_VIEWS_APPS_APP_DIALOG_APP_LOCAL_BLOCK_DIALOG_VIEW_H_
+#define CHROME_BROWSER_UI_VIEWS_APPS_APP_DIALOG_APP_LOCAL_BLOCK_DIALOG_VIEW_H_
+
+#include <string>
+
+#include "chrome/browser/ui/views/apps/app_dialog/app_dialog_view.h"
+
+namespace gfx {
+class ImageSkia;
+}
+
+// The blocking dialog for the app blocked by local settings.
+class AppLocalBlockDialogView : public AppDialogView {
+ public:
+  AppLocalBlockDialogView(const std::string& app_name,
+                          const gfx::ImageSkia& image);
+  ~AppLocalBlockDialogView() override;
+
+  static AppLocalBlockDialogView* GetActiveViewForTesting();
+};
+
+#endif  // CHROME_BROWSER_UI_VIEWS_APPS_APP_DIALOG_APP_LOCAL_BLOCK_DIALOG_VIEW_H_
diff --git a/chrome/browser/ui/views/apps/chrome_native_app_window_views.cc b/chrome/browser/ui/views/apps/chrome_native_app_window_views.cc
index 723ead9..e26627ce 100644
--- a/chrome/browser/ui/views/apps/chrome_native_app_window_views.cc
+++ b/chrome/browser/ui/views/apps/chrome_native_app_window_views.cc
@@ -111,7 +111,9 @@
 
 void ChromeNativeAppWindowViews::InitializeDefaultWindow(
     const AppWindow::CreateParams& create_params) {
-  views::Widget::InitParams init_params(views::Widget::InitParams::TYPE_WINDOW);
+  views::Widget::InitParams init_params(
+      views::Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET,
+      views::Widget::InitParams::TYPE_WINDOW);
   init_params.delegate = this;
   init_params.remove_standard_frame = ShouldRemoveStandardFrame();
   init_params.use_system_default_icon = true;
diff --git a/chrome/browser/ui/views/autofill/popup/popup_base_view.cc b/chrome/browser/ui/views/autofill/popup/popup_base_view.cc
index 5ce402b..1f9056e 100644
--- a/chrome/browser/ui/views/autofill/popup/popup_base_view.cc
+++ b/chrome/browser/ui/views/autofill/popup/popup_base_view.cc
@@ -110,7 +110,9 @@
   explicit Widget(PopupBaseView* autofill_popup_base_view,
                   gfx::NativeView parent_native_view,
                   views::Widget::InitParams::Activatable activatable) {
-    views::Widget::InitParams params(views::Widget::InitParams::TYPE_POPUP);
+    views::Widget::InitParams params(
+        views::Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET,
+        views::Widget::InitParams::TYPE_POPUP);
     params.delegate = autofill_popup_base_view;
     params.parent = parent_native_view;
     // Ensure the popup border is not painted on an opaque background.
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_bubble_view_unittest.cc b/chrome/browser/ui/views/bookmarks/bookmark_bubble_view_unittest.cc
index afd2e8d..662af61 100644
--- a/chrome/browser/ui/views/bookmarks/bookmark_bubble_view_unittest.cc
+++ b/chrome/browser/ui/views/bookmarks/bookmark_bubble_view_unittest.cc
@@ -70,7 +70,8 @@
 
     anchor_widget_ =
         views::UniqueWidgetPtr(std::make_unique<ChromeTestWidget>());
-    views::Widget::InitParams widget_params;
+    views::Widget::InitParams widget_params(
+        views::Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET);
     widget_params.context = GetContext();
     anchor_widget_->Init(std::move(widget_params));
 
diff --git a/chrome/browser/ui/views/chrome_web_dialog_view.cc b/chrome/browser/ui/views/chrome_web_dialog_view.cc
index b45f806..14f0783c2 100644
--- a/chrome/browser/ui/views/chrome_web_dialog_view.cc
+++ b/chrome/browser/ui/views/chrome_web_dialog_view.cc
@@ -68,7 +68,8 @@
   if (extra_params && extra_params->corner_radius)
     view->set_corner_radius(*(extra_params->corner_radius));
 
-  views::Widget::InitParams params;
+  views::Widget::InitParams params(
+      views::Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET);
   if (extra_params)
     params = std::move(*extra_params);
   params.delegate = view;
diff --git a/chrome/browser/ui/views/commerce/product_specifications_button.cc b/chrome/browser/ui/views/commerce/product_specifications_button.cc
index 2be9994f..6a4ce77 100644
--- a/chrome/browser/ui/views/commerce/product_specifications_button.cc
+++ b/chrome/browser/ui/views/commerce/product_specifications_button.cc
@@ -174,6 +174,10 @@
   Show();
 }
 
+void ProductSpecificationsButton::HideEntryPoint() {
+  Hide();
+}
+
 void ProductSpecificationsButton::SetOpacity(float factor) {
   label()->layer()->SetOpacity(factor);
   close_button_->layer()->SetOpacity(factor);
diff --git a/chrome/browser/ui/views/commerce/product_specifications_button.h b/chrome/browser/ui/views/commerce/product_specifications_button.h
index eda5fedb..72ae168 100644
--- a/chrome/browser/ui/views/commerce/product_specifications_button.h
+++ b/chrome/browser/ui/views/commerce/product_specifications_button.h
@@ -57,6 +57,7 @@
 
   // commerce::ProductSpecificationsEntryPointController::Observer
   void ShowEntryPointWithTitle(const std::string title) override;
+  void HideEntryPoint() override;
 
  protected:
   // TabStripControlButton:
diff --git a/chrome/browser/ui/views/media_preview/media_view_controller_base_unittest.cc b/chrome/browser/ui/views/media_preview/media_view_controller_base_unittest.cc
index ac45fc57..b85f50d 100644
--- a/chrome/browser/ui/views/media_preview/media_view_controller_base_unittest.cc
+++ b/chrome/browser/ui/views/media_preview/media_view_controller_base_unittest.cc
@@ -116,12 +116,14 @@
     return controller_->GetNoDeviceLabelViewForTesting()->GetVisible();
   }
 
-  const std::u16string& GetComboboxAccessibleName() const {
+  std::u16string GetComboboxAccessibleName() const {
     return controller_->GetComboboxForTesting()->GetAccessibleName();
   }
+
   const std::u16string& GetDeviceNameLabel() const {
     return controller_->GetDeviceNameLabelViewForTesting()->GetText();
   }
+
   const std::u16string& GetNoDeviceLabel() const {
     return controller_->GetNoDeviceLabelViewForTesting()->GetText();
   }
diff --git a/chrome/browser/ui/views/promos/autofill_bubble_signin_promo_interactive_uitest.cc b/chrome/browser/ui/views/promos/autofill_bubble_signin_promo_interactive_uitest.cc
index 43a47f5..f4bd63e 100644
--- a/chrome/browser/ui/views/promos/autofill_bubble_signin_promo_interactive_uitest.cc
+++ b/chrome/browser/ui/views/promos/autofill_bubble_signin_promo_interactive_uitest.cc
@@ -311,7 +311,7 @@
       /*is_under_advanced_protection=*/false,
       signin_metrics::AccessPoint::ACCESS_POINT_PASSWORD_BUBBLE,
       signin_metrics::SourceForRefreshTokenOperation::
-          kDiceResponseHandler_PasswordPromoSignin);
+          kDiceResponseHandler_Signin);
   account_store_waiter.WaitOrReturn();
 
   // Check that the sign in was successful.
diff --git a/chrome/browser/ui/views/supervised_user/parent_permission_dialog_view.cc b/chrome/browser/ui/views/supervised_user/parent_permission_dialog_view.cc
index 59b2337..30cb457 100644
--- a/chrome/browser/ui/views/supervised_user/parent_permission_dialog_view.cc
+++ b/chrome/browser/ui/views/supervised_user/parent_permission_dialog_view.cc
@@ -52,6 +52,7 @@
 #include "ui/gfx/color_palette.h"
 #include "ui/gfx/geometry/insets.h"
 #include "ui/gfx/image/image_skia.h"
+#include "ui/views/accessibility/view_accessibility.h"
 #include "ui/views/border.h"
 #include "ui/views/bubble/bubble_frame_view.h"
 #include "ui/views/controls/button/radio_button.h"
@@ -79,7 +80,11 @@
 
  public:
   MaybeEmptyLabel(const std::string& text, const CustomFont& font)
-      : views::Label(base::UTF8ToUTF16(text), font) {}
+      : views::Label(base::UTF8ToUTF16(text), font) {
+    // Set the role to kAlert as this is required for
+    // sending accessibility notification alerts.
+    GetViewAccessibility().SetRole(ax::mojom::Role::kAlert);
+  }
 
   MaybeEmptyLabel& operator=(const MaybeEmptyLabel&) = delete;
   MaybeEmptyLabel(const MaybeEmptyLabel&) = delete;
@@ -93,10 +98,6 @@
     } else {
       node_data->SetNameExplicitlyEmpty();
     }
-
-    // Set the role to kAlert as this is required for
-    // sending accessibility notification alerts.
-    node_data->role = ax::mojom::Role::kAlert;
   }
 };
 
diff --git a/chrome/browser/ui/views/toolbar/back_forward_button.cc b/chrome/browser/ui/views/toolbar/back_forward_button.cc
index 0752aac..84491f2 100644
--- a/chrome/browser/ui/views/toolbar/back_forward_button.cc
+++ b/chrome/browser/ui/views/toolbar/back_forward_button.cc
@@ -39,9 +39,7 @@
   SetTriggerableEventFlags(ui::EF_LEFT_MOUSE_BUTTON |
                            ui::EF_MIDDLE_MOUSE_BUTTON);
   if (direction == Direction::kBack) {
-    SetVectorIcons(features::IsChromeRefresh2023()
-                       ? vector_icons::kBackArrowChromeRefreshIcon
-                       : vector_icons::kBackArrowIcon,
+    SetVectorIcons(vector_icons::kBackArrowChromeRefreshIcon,
                    kBackArrowTouchIcon);
     SetTooltipText(l10n_util::GetStringUTF16(IDS_TOOLTIP_BACK));
     SetAccessibleName(l10n_util::GetStringUTF16(IDS_ACCNAME_BACK));
@@ -51,9 +49,7 @@
     SetProperty(views::kElementIdentifierKey, kToolbarBackButtonElementId);
     set_menu_identifier(kToolbarBackButtonMenuElementId);
   } else {
-    SetVectorIcons(features::IsChromeRefresh2023()
-                       ? vector_icons::kForwardArrowChromeRefreshIcon
-                       : vector_icons::kForwardArrowIcon,
+    SetVectorIcons(vector_icons::kForwardArrowChromeRefreshIcon,
                    kForwardArrowTouchIcon);
     SetTooltipText(l10n_util::GetStringUTF16(IDS_TOOLTIP_FORWARD));
     SetAccessibleName(l10n_util::GetStringUTF16(IDS_ACCNAME_FORWARD));
diff --git a/chrome/browser/ui/views/toolbar/browser_app_menu_button.cc b/chrome/browser/ui/views/toolbar/browser_app_menu_button.cc
index 4217fd8..4bcc7d6 100644
--- a/chrome/browser/ui/views/toolbar/browser_app_menu_button.cc
+++ b/chrome/browser/ui/views/toolbar/browser_app_menu_button.cc
@@ -71,13 +71,11 @@
                                         base::Unretained(this))),
       toolbar_view_(toolbar_view) {
   SetHorizontalAlignment(gfx::ALIGN_RIGHT);
-  if (features::IsChromeRefresh2023()) {
-    SetImageLabelSpacing(kChromeRefreshImageLabelPadding);
-    label()->SetPaintToLayer();
-    label()->SetSkipSubpixelRenderingOpacityCheck(true);
-    label()->layer()->SetFillsBoundsOpaquely(false);
-    label()->SetSubpixelRenderingEnabled(false);
-  }
+  SetImageLabelSpacing(kChromeRefreshImageLabelPadding);
+  label()->SetPaintToLayer();
+  label()->SetSkipSubpixelRenderingOpacityCheck(true);
+  label()->layer()->SetFillsBoundsOpaquely(false);
+  label()->SetSubpixelRenderingEnabled(false);
 }
 
 BrowserAppMenuButton::~BrowserAppMenuButton() {}
@@ -141,39 +139,24 @@
   // Call `UpdateIcon()` after `UpdateTextAndHighlightColor()` as the icon color
   // depends on if the container is in an expanded state.
   UpdateIcon();
-  if (features::IsChromeRefresh2023()) {
-    UpdateInkdrop();
-    // Outset focus ring should be present for the chip but not when only
-    // the icon is visible.
-    views::FocusRing::Get(this)->SetOutsetFocusRingDisabled(
-        !IsLabelPresentAndVisible());
-  }
+  UpdateInkdrop();
+  // Outset focus ring should be present for the chip but not when only
+  // the icon is visible.
+  views::FocusRing::Get(this)->SetOutsetFocusRingDisabled(
+      !IsLabelPresentAndVisible());
 }
 
 void BrowserAppMenuButton::UpdateIcon() {
-  const gfx::VectorIcon& icon =
-      ui::TouchUiController::Get()->touch_ui()
-          ? kBrowserToolsTouchIcon
-          : (features::IsChromeRefresh2023() ? kBrowserToolsChromeRefreshIcon
-                                             : kBrowserToolsIcon);
+  const gfx::VectorIcon& icon = ui::TouchUiController::Get()->touch_ui()
+                                    ? kBrowserToolsTouchIcon
+                                    : kBrowserToolsChromeRefreshIcon;
   for (auto state : kButtonStates) {
-    // `app_menu_icon_controller()->GetIconColor()` set different colors based
-    // on the severity. However with chrome refresh all the severities should
-    // have the same color. Decouple the logic from
-    // `app_menu_icon_controller()->GetIconColor()` to avoid impact from
-    // multiple call sites.
-    SkColor icon_color =
-        features::IsChromeRefresh2023()
-            ? GetForegroundColor(state)
-            : toolbar_view_->app_menu_icon_controller()->GetIconColor(
-                  GetForegroundColor(state));
+    SkColor icon_color = GetForegroundColor(state);
     SetImageModel(state, ui::ImageModel::FromVectorIcon(icon, icon_color));
   }
 }
 
 void BrowserAppMenuButton::UpdateInkdrop() {
-  CHECK(features::IsChromeRefresh2023());
-
   if (IsLabelPresentAndVisible()) {
     ConfigureToolbarInkdropForRefresh2023(this, kColorAppMenuChipInkDropHover,
                                           kColorAppMenuChipInkDropRipple);
@@ -191,7 +174,7 @@
 }
 
 SkColor BrowserAppMenuButton::GetForegroundColor(ButtonState state) const {
-  if (features::IsChromeRefresh2023() && IsLabelPresentAndVisible()) {
+  if (IsLabelPresentAndVisible()) {
     const auto* const color_provider = GetColorProvider();
     if (type_and_severity_.use_primary_colors) {
       return color_provider->GetColor(kColorAppMenuExpandedForegroundPrimary);
@@ -247,14 +230,10 @@
 }
 
 bool BrowserAppMenuButton::ShouldPaintBorder() const {
-  return !features::IsChromeRefresh2023();
+  return false;
 }
 
 void BrowserAppMenuButton::UpdateLayoutInsets() {
-  if (!features::IsChromeRefresh2023()) {
-    return;
-  }
-
   if (IsLabelPresentAndVisible()) {
     SetLayoutInsets(::GetLayoutInsets(BROWSER_APP_MENU_CHIP_PADDING));
   } else {
@@ -263,7 +242,7 @@
 }
 
 std::optional<SkColor> BrowserAppMenuButton::GetHighlightTextColor() const {
-  if (features::IsChromeRefresh2023() && IsLabelPresentAndVisible()) {
+  if (IsLabelPresentAndVisible()) {
     const auto* const color_provider = GetColorProvider();
     if (type_and_severity_.use_primary_colors) {
       return color_provider->GetColor(kColorAppMenuExpandedForegroundPrimary);
@@ -275,19 +254,12 @@
 
 std::optional<SkColor> BrowserAppMenuButton::GetHighlightColor() const {
   const auto* const color_provider = GetColorProvider();
-  if (features::IsChromeRefresh2023() &&
-      type_and_severity_.use_primary_colors) {
-    return color_provider->GetColor(kColorAppMenuHighlightPrimary);
-  }
-  switch (type_and_severity_.severity) {
-    case AppMenuIconController::Severity::NONE:
-      return std::nullopt;
-    case AppMenuIconController::Severity::LOW:
-      return color_provider->GetColor(kColorAppMenuHighlightSeverityLow);
-    case AppMenuIconController::Severity::MEDIUM:
-      return color_provider->GetColor(kColorAppMenuHighlightSeverityMedium);
-    case AppMenuIconController::Severity::HIGH:
-      return color_provider->GetColor(kColorAppMenuHighlightSeverityHigh);
+  if (type_and_severity_.severity == AppMenuIconController::Severity::NONE) {
+    return std::nullopt;
+  } else {
+    return color_provider->GetColor(type_and_severity_.use_primary_colors
+                                        ? kColorAppMenuHighlightPrimary
+                                        : kColorAppMenuHighlightDefault);
   }
 }
 
diff --git a/chrome/browser/ui/views/toolbar/chrome_labs/chrome_labs_button.cc b/chrome/browser/ui/views/toolbar/chrome_labs/chrome_labs_button.cc
index cdb39e00..e2fd80a 100644
--- a/chrome/browser/ui/views/toolbar/chrome_labs/chrome_labs_button.cc
+++ b/chrome/browser/ui/views/toolbar/chrome_labs/chrome_labs_button.cc
@@ -33,9 +33,7 @@
       browser_view_(browser_view),
       model_(model) {
   SetProperty(views::kElementIdentifierKey, kToolbarChromeLabsButtonElementId);
-  SetVectorIcons(features::IsChromeRefresh2023() ? kChromeLabsChromeRefreshIcon
-                                                 : kChromeLabsIcon,
-                 kChromeLabsTouchIcon);
+  SetVectorIcons(kChromeLabsChromeRefreshIcon, kChromeLabsTouchIcon);
   SetAccessibleName(l10n_util::GetStringUTF16(IDS_ACCNAME_CHROMELABS_BUTTON));
   SetTooltipText(l10n_util::GetStringUTF16(IDS_TOOLTIP_CHROMELABS_BUTTON));
   button_controller()->set_notify_action(
diff --git a/chrome/browser/ui/views/toolbar/home_button.cc b/chrome/browser/ui/views/toolbar/home_button.cc
index 87a8016..775f02ff 100644
--- a/chrome/browser/ui/views/toolbar/home_button.cc
+++ b/chrome/browser/ui/views/toolbar/home_button.cc
@@ -137,10 +137,7 @@
   SetProperty(views::kElementIdentifierKey, kToolbarHomeButtonElementId);
   SetTriggerableEventFlags(ui::EF_LEFT_MOUSE_BUTTON |
                            ui::EF_MIDDLE_MOUSE_BUTTON);
-  SetVectorIcons(features::IsChromeRefresh2023()
-                     ? kNavigateHomeChromeRefreshIcon
-                     : kNavigateHomeIcon,
-                 kNavigateHomeTouchIcon);
+  SetVectorIcons(kNavigateHomeChromeRefreshIcon, kNavigateHomeTouchIcon);
   SetTooltipText(l10n_util::GetStringUTF16(IDS_TOOLTIP_HOME));
   SetAccessibleName(l10n_util::GetStringUTF16(IDS_ACCNAME_HOME));
   SetID(VIEW_ID_HOME_BUTTON);
diff --git a/chrome/browser/ui/views/toolbar/reload_button.cc b/chrome/browser/ui/views/toolbar/reload_button.cc
index d7325a5..9d6f7da3 100644
--- a/chrome/browser/ui/views/toolbar/reload_button.cc
+++ b/chrome/browser/ui/views/toolbar/reload_button.cc
@@ -39,13 +39,9 @@
                     CreateMenuModel(),
                     nullptr),
       command_updater_(command_updater),
-      reload_icon_(features::IsChromeRefresh2023()
-                       ? vector_icons::kReloadChromeRefreshIcon
-                       : vector_icons::kReloadIcon),
+      reload_icon_(vector_icons::kReloadChromeRefreshIcon),
       reload_touch_icon_(kReloadTouchIcon),
-      stop_icon_(features::IsChromeRefresh2023()
-                     ? kNavigateStopChromeRefreshIcon
-                     : kNavigateStopIcon),
+      stop_icon_(kNavigateStopChromeRefreshIcon),
       stop_touch_icon_(kNavigateStopTouchIcon),
       double_click_timer_delay_(
           base::Milliseconds(views::GetDoubleClickInterval())),
diff --git a/chrome/browser/ui/views/toolbar/side_panel_toolbar_button.cc b/chrome/browser/ui/views/toolbar/side_panel_toolbar_button.cc
index b3f9501..182241e 100644
--- a/chrome/browser/ui/views/toolbar/side_panel_toolbar_button.cc
+++ b/chrome/browser/ui/views/toolbar/side_panel_toolbar_button.cc
@@ -63,14 +63,9 @@
   const bool is_right_aligned = browser_->profile()->GetPrefs()->GetBoolean(
       prefs::kSidePanelHorizontalAlignment);
   if (is_right_aligned) {
-    SetVectorIcons(features::IsChromeRefresh2023() ? kSidePanelChromeRefreshIcon
-                                                   : kSidePanelIcon,
-                   kSidePanelTouchIcon);
+    SetVectorIcons(kSidePanelChromeRefreshIcon, kSidePanelTouchIcon);
   } else {
-    SetVectorIcons(features::IsChromeRefresh2023()
-                       ? kSidePanelLeftChromeRefreshIcon
-                       : kSidePanelLeftIcon,
-                   kSidePanelLeftTouchIcon);
+    SetVectorIcons(kSidePanelLeftChromeRefreshIcon, kSidePanelLeftTouchIcon);
   }
 }
 
diff --git a/chrome/browser/ui/views/toolbar/side_panel_toolbar_button_unittest.cc b/chrome/browser/ui/views/toolbar/side_panel_toolbar_button_unittest.cc
index e9f07bf..572b7a3 100644
--- a/chrome/browser/ui/views/toolbar/side_panel_toolbar_button_unittest.cc
+++ b/chrome/browser/ui/views/toolbar/side_panel_toolbar_button_unittest.cc
@@ -55,8 +55,7 @@
   ASSERT_TRUE(gfx::test::AreImagesEqual(
       gfx::Image(side_panel_button->GetImage(views::Button::STATE_NORMAL)),
       gfx::Image(gfx::CreateVectorIcon(
-          features::IsChromeRefresh2023() ? kSidePanelChromeRefreshIcon
-                                          : kSidePanelIcon,
+          kSidePanelChromeRefreshIcon,
           color_provider->GetColor(kColorToolbarButtonIcon)))));
 
   // Left aligned side panels should use the left aligned icon.
@@ -66,8 +65,7 @@
   ASSERT_TRUE(gfx::test::AreImagesEqual(
       gfx::Image(side_panel_button->GetImage(views::Button::STATE_NORMAL)),
       gfx::Image(gfx::CreateVectorIcon(
-          features::IsChromeRefresh2023() ? kSidePanelLeftChromeRefreshIcon
-                                          : kSidePanelLeftIcon,
+          kSidePanelLeftChromeRefreshIcon,
           color_provider->GetColor(kColorToolbarButtonIcon)))));
 }
 
@@ -101,8 +99,7 @@
   EXPECT_TRUE(gfx::test::AreImagesEqual(
       gfx::Image(side_panel_button->GetImage(views::Button::STATE_NORMAL)),
       gfx::Image(gfx::CreateVectorIcon(
-          features::IsChromeRefresh2023() ? kSidePanelChromeRefreshIcon
-                                          : kSidePanelIcon,
+          kSidePanelChromeRefreshIcon,
           color_provider->GetColor(kColorToolbarButtonIcon)))));
 
   // Left aligned side panels should use the left aligned icon.
@@ -112,7 +109,6 @@
   EXPECT_TRUE(gfx::test::AreImagesEqual(
       gfx::Image(side_panel_button->GetImage(views::Button::STATE_NORMAL)),
       gfx::Image(gfx::CreateVectorIcon(
-          features::IsChromeRefresh2023() ? kSidePanelLeftChromeRefreshIcon
-                                          : kSidePanelLeftIcon,
+          kSidePanelLeftChromeRefreshIcon,
           color_provider->GetColor(kColorToolbarButtonIcon)))));
 }
diff --git a/chrome/browser/ui/views/toolbar/toolbar_button.cc b/chrome/browser/ui/views/toolbar/toolbar_button.cc
index 3a6ad54..cc2e9f0 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_button.cc
+++ b/chrome/browser/ui/views/toolbar/toolbar_button.cc
@@ -263,8 +263,7 @@
     return kDefaultTouchableIconSize;
   }
 
-  return features::IsChromeRefresh2023() ? kDefaultIconSizeChromeRefresh
-                                         : kDefaultIconSize;
+  return kDefaultIconSizeChromeRefresh;
 }
 
 bool ToolbarButton::ShouldPaintBorder() const {
@@ -272,7 +271,7 @@
 }
 
 bool ToolbarButton::ShouldBlendHighlightColor() const {
-  return !features::IsChromeRefresh2023();
+  return false;
 }
 
 bool ToolbarButton::ShouldDirectlyUseHighlightAsBackground() const {
diff --git a/chrome/browser/ui/views/toolbar/toolbar_ink_drop_util.cc b/chrome/browser/ui/views/toolbar/toolbar_ink_drop_util.cc
index 908a5e0..e6c3026e 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_ink_drop_util.cc
+++ b/chrome/browser/ui/views/toolbar/toolbar_ink_drop_util.cc
@@ -92,18 +92,14 @@
       kToolbarInkDropHighlightVisibleAlpha / float{SK_AlphaOPAQUE});
   views::InkDrop::Get(host)->SetBaseColorCallback(
       base::BindRepeating(&GetToolbarInkDropBaseColor, host));
-
-  if (features::IsChromeRefresh2023()) {
-    ConfigureToolbarInkdropForRefresh2023(host, kColorToolbarInkDropHover,
-                                          kColorToolbarInkDropRipple);
-  }
+  ConfigureToolbarInkdropForRefresh2023(host, kColorToolbarInkDropHover,
+                                        kColorToolbarInkDropRipple);
 }
 
 void ConfigureToolbarInkdropForRefresh2023(
     views::View* const host,
     const ChromeColorIds hover_color_id,
     const ChromeColorIds ripple_color_id) {
-  CHECK(features::IsChromeRefresh2023());
   views::InkDrop::Get(host)->SetLayerRegion(views::LayerRegion::kAbove);
   CreateToolbarInkdropCallbacks(host, hover_color_id, ripple_color_id);
 }
diff --git a/chrome/browser/ui/views/toolbar/toolbar_ink_drop_util_unittest.cc b/chrome/browser/ui/views/toolbar/toolbar_ink_drop_util_unittest.cc
index 8f70959..5482586 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_ink_drop_util_unittest.cc
+++ b/chrome/browser/ui/views/toolbar/toolbar_ink_drop_util_unittest.cc
@@ -28,21 +28,12 @@
 
 }  // namespace
 
-class ToolbarInkDropUtilTest : public ChromeViewsTestBase,
-                               public ::testing::WithParamInterface<bool> {
+class ToolbarInkDropUtilTest : public ChromeViewsTestBase {
  public:
   ToolbarInkDropUtilTest() = default;
 
   void SetUp() override {
     ChromeViewsTestBase::SetUp();
-
-    // Enable or disable the feature based on the test parameter
-    if (GetParam()) {
-      feature_list_.InitAndEnableFeature(features::kChromeRefresh2023);
-    } else {
-      feature_list_.InitAndDisableFeature(features::kChromeRefresh2023);
-    }
-
     button_host_ = std::make_unique<TestButton>();
   }
 
@@ -58,25 +49,11 @@
 };
 
 // Basic test to check for various inkdrop properties for toolbar buttons.
-TEST_P(ToolbarInkDropUtilTest, ConfigureInkDropForToolbarTest) {
+TEST_F(ToolbarInkDropUtilTest, ConfigureInkDropForToolbarTest) {
   ConfigureInkDropForToolbar(button_host_.get());
-
-  if (!features::IsChromeRefresh2023()) {
-    EXPECT_TRUE(button_host_->GetHasInkDropActionOnClick());
-    EXPECT_EQ(views::InkDrop::Get(button_host_.get())->GetMode(),
-              views::InkDropHost::InkDropMode::ON);
-    EXPECT_EQ(views::InkDrop::Get(button_host_.get())->GetVisibleOpacity(),
-              kToolbarInkDropVisibleOpacity);
-  } else {
     EXPECT_EQ(views::InkDrop::Get(button_host_.get())->GetLayerRegion(),
               views::LayerRegion::kAbove);
     std::unique_ptr<views::InkDropHighlight> highlight =
         views::InkDrop::Get(button_host_.get())->CreateInkDropHighlight();
     EXPECT_NE(highlight, nullptr);
-  }
 }
-
-// Parameterized test cases. The parameter is whether the feature is enabled.
-INSTANTIATE_TEST_SUITE_P(ChromeRefresh2023OnOff,
-                         ToolbarInkDropUtilTest,
-                         testing::Bool());
diff --git a/chrome/browser/ui/views/toolbar/toolbar_view.cc b/chrome/browser/ui/views/toolbar/toolbar_view.cc
index dea35af3..cc2b1b2 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_view.cc
+++ b/chrome/browser/ui/views/toolbar/toolbar_view.cc
@@ -263,7 +263,6 @@
 #endif
 
   // The background views must be behind container_view_.
-  if (features::IsChromeRefresh2023()) {
     background_view_left_ = AddChildViewAt(std::make_unique<View>(), 0);
     background_view_left_->SetBackground(
         std::make_unique<TabstripLikeBackground>(browser_view_));
@@ -274,7 +273,6 @@
     active_state_subscription_ =
         GetWidget()->RegisterPaintAsActiveChangedCallback(base::BindRepeating(
             &ToolbarView::ActiveStateChanged, base::Unretained(this)));
-  }
 
   auto location_bar = std::make_unique<LocationBarView>(
       browser_, browser_->profile(), browser_->command_controller(), this,
@@ -360,9 +358,7 @@
     extensions_container =
         std::make_unique<ExtensionsToolbarContainer>(browser_);
 
-    if (features::IsChromeRefresh2023()) {
-      toolbar_divider = std::make_unique<views::View>();
-    }
+    toolbar_divider = std::make_unique<views::View>();
   }
   std::unique_ptr<media_router::CastToolbarButton> cast;
   if (media_router::MediaRouterEnabled(browser_->profile()))
@@ -809,14 +805,12 @@
   // The container view should be the exact same size/position as ToolbarView.
   container_view_->SetSize(size());
 
-  if (features::IsChromeRefresh2023()) {
-    // The background views should be behind the top-left and top-right corners
-    // of the container_view_.
-    const int corner_radius = GetLayoutConstant(TOOLBAR_CORNER_RADIUS);
-    background_view_left_->SetBounds(0, 0, corner_radius, corner_radius);
-    background_view_right_->SetBounds(width() - corner_radius, 0, corner_radius,
-                                      corner_radius);
-  }
+  // The background views should be behind the top-left and top-right corners
+  // of the container_view_.
+  const int corner_radius = GetLayoutConstant(TOOLBAR_CORNER_RADIUS);
+  background_view_left_->SetBounds(0, 0, corner_radius, corner_radius);
+  background_view_right_->SetBounds(width() - corner_radius, 0, corner_radius,
+                                    corner_radius);
 
   if (display_mode_ == DisplayMode::CUSTOM_TAB) {
     custom_tab_bar_->SetBounds(0, 0, width(),
@@ -827,10 +821,7 @@
 
   if (display_mode_ == DisplayMode::NORMAL) {
     LayoutCommon();
-
-    if (features::IsChromeRefresh2023()) {
-      UpdateClipPath();
-    }
+    UpdateClipPath();
   }
 
   // Use two-pass solution to avoid the overflow button interfering with toolbar
@@ -1051,7 +1042,7 @@
                           ? LayoutInset::WEBUI_TAB_STRIP_TOOLBAR_INTERIOR_MARGIN
                           : LayoutInset::TOOLBAR_INTERIOR_MARGIN);
 
-  if (features::IsChromeRefresh2023() && !browser_view_->webui_tab_strip()) {
+  if (!browser_view_->webui_tab_strip()) {
     if (app_menu_button_->IsLabelPresentAndVisible()) {
       // The interior margin in an expanded state should be more than in a
       // collapsed state.
@@ -1128,25 +1119,6 @@
   }
 }
 
-SkColor ToolbarView::GetDefaultColorForSeverity(
-    AppMenuIconController::Severity severity) const {
-  ui::ColorId color_id;
-  switch (severity) {
-    case AppMenuIconController::Severity::NONE:
-      return GetColorProvider()->GetColor(kColorToolbarButtonIcon);
-    case AppMenuIconController::Severity::LOW:
-      color_id = kColorAppMenuHighlightSeverityLow;
-      break;
-    case AppMenuIconController::Severity::MEDIUM:
-      color_id = kColorAppMenuHighlightSeverityMedium;
-      break;
-    case AppMenuIconController::Severity::HIGH:
-      color_id = kColorAppMenuHighlightSeverityHigh;
-      break;
-  }
-  return GetColorProvider()->GetColor(color_id);
-}
-
 ExtensionsToolbarContainer* ToolbarView::GetExtensionsToolbarContainer() {
   return extensions_container_;
 }
diff --git a/chrome/browser/ui/views/toolbar/toolbar_view.h b/chrome/browser/ui/views/toolbar/toolbar_view.h
index f5242c0..340e6a3 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_view.h
+++ b/chrome/browser/ui/views/toolbar/toolbar_view.h
@@ -237,8 +237,6 @@
   // AppMenuIconController::Delegate:
   void UpdateTypeAndSeverity(
       AppMenuIconController::TypeAndSeverity type_and_severity) override;
-  SkColor GetDefaultColorForSeverity(
-      AppMenuIconController::Severity severity) const override;
 
   // ToolbarButtonProvider:
   ExtensionsToolbarContainer* GetExtensionsToolbarContainer() override;
diff --git a/chrome/browser/ui/webui/access_code_cast/access_code_cast_dialog.cc b/chrome/browser/ui/webui/access_code_cast/access_code_cast_dialog.cc
index 0272968..277a7a12 100644
--- a/chrome/browser/ui/webui/access_code_cast/access_code_cast_dialog.cc
+++ b/chrome/browser/ui/webui/access_code_cast/access_code_cast_dialog.cc
@@ -186,7 +186,8 @@
 
 views::Widget::InitParams AccessCodeCastDialog::CreateParams(
     AccessCodeCastDialogMode dialog_mode) {
-  views::Widget::InitParams params;
+  views::Widget::InitParams params(
+      views::Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET);
   params.remove_standard_frame = true;
   // If we are acting as a system dialog, use the appropriate corner radius.
   // Otherwise, the widget will default to the correct value for browser
diff --git a/chrome/browser/ui/webui/ash/login/gaia_password_changed_screen_handler.cc b/chrome/browser/ui/webui/ash/login/gaia_password_changed_screen_handler.cc
deleted file mode 100644
index 41c0327..0000000
--- a/chrome/browser/ui/webui/ash/login/gaia_password_changed_screen_handler.cc
+++ /dev/null
@@ -1,86 +0,0 @@
-// Copyright 2020 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/ui/webui/ash/login/gaia_password_changed_screen_handler.h"
-
-#include "ash/constants/ash_features.h"
-#include "base/values.h"
-#include "chrome/browser/ash/login/oobe_screen.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
-#include "chrome/grit/branded_strings.h"
-#include "chrome/grit/generated_resources.h"
-#include "components/login/localized_values_builder.h"
-#include "ui/chromeos/devicetype_utils.h"
-
-namespace ash {
-
-GaiaPasswordChangedScreenHandler::GaiaPasswordChangedScreenHandler()
-    : BaseScreenHandler(kScreenId) {}
-
-GaiaPasswordChangedScreenHandler::~GaiaPasswordChangedScreenHandler() = default;
-
-void GaiaPasswordChangedScreenHandler::DeclareLocalizedValues(
-    ::login::LocalizedValuesBuilder* builder) {
-  builder->Add("nextButtonText", IDS_OFFLINE_LOGIN_NEXT_BUTTON_TEXT);
-  builder->Add("oldPasswordHint", IDS_LOGIN_PASSWORD_CHANGED_OLD_PASSWORD_HINT);
-  builder->Add("oldPasswordIncorrect",
-               IDS_LOGIN_PASSWORD_CHANGED_INCORRECT_OLD_PASSWORD);
-  builder->Add("forgotOldPasswordButtonText",
-               IDS_LOGIN_PASSWORD_CHANGED_FORGOT_PASSWORD);
-  builder->AddF("passwordChangedTitle", IDS_LOGIN_PASSWORD_CHANGED_TITLE,
-                ui::GetChromeOSDeviceName());
-  builder->Add("passwordChangedProceedAnywayTitle",
-               IDS_LOGIN_PASSWORD_CHANGED_PROCEED_ANYWAY);
-  builder->Add("passwordChangedTryAgain", IDS_LOGIN_PASSWORD_CHANGED_TRY_AGAIN);
-  builder->Add("dataLossWarningTitle",
-               IDS_LOGIN_PASSWORD_CHANGED_DATA_LOSS_WARNING_TITLE);
-  builder->Add("dataLossWarningSubtitle",
-               IDS_LOGIN_PASSWORD_CHANGED_DATA_LOSS_WARNING_SUBTITLE);
-  builder->Add("recoverLocalDataTitle",
-               IDS_LOGIN_PASSWORD_CHANGED_RECOVER_DATA_TITLE);
-  builder->Add("recoverLocalDataSubtitle",
-               IDS_LOGIN_PASSWORD_CHANGED_RECOVER_DATA_SUBTITLE);
-  builder->Add("continueAndDeleteDataButton",
-               IDS_LOGIN_PASSWORD_CHANGED_CONTINUE_AND_DELETE_BUTTON);
-  builder->Add("forgotOldPasswordButton",
-               IDS_LOGIN_PASSWORD_CHANGED_FORGOT_OLD_PASSWORD_BUTTON);
-
-  builder->Add("recoveryOptInTitle", IDS_LOGIN_PASSWORD_CHANGED_RECOVERY_TITLE);
-  builder->Add("recoveryOptInSubtitle",
-               IDS_LOGIN_PASSWORD_CHANGED_RECOVERY_SUBTITLE);
-  builder->Add("recoveryOptInNoButton",
-               IDS_LOGIN_PASSWORD_CHANGED_RECOVERY_NO_BUTTON);
-  builder->Add("recoveryOptInEnableButton",
-               IDS_LOGIN_PASSWORD_CHANGED_RECOVERY_ENABLE_BUTTON);
-}
-
-void GaiaPasswordChangedScreenHandler::Show(const std::string& email,
-                                            bool has_error) {
-  base::Value::Dict data;
-  data.Set("email", email);
-  data.Set("showError", has_error);
-  ShowInWebUI(std::move(data));
-}
-
-void GaiaPasswordChangedScreenHandler::Show(const std::string& email) {
-  base::Value::Dict data;
-  data.Set("email", email);
-  data.Set("showError", false);
-  ShowInWebUI(std::move(data));
-}
-
-void GaiaPasswordChangedScreenHandler::ShowWrongPasswordError() {
-  CallExternalAPI("showWrongPasswordError");
-}
-
-void GaiaPasswordChangedScreenHandler::SuggestRecovery() {
-  CallExternalAPI("suggestRecovery");
-}
-
-base::WeakPtr<GaiaPasswordChangedView>
-GaiaPasswordChangedScreenHandler::AsWeakPtr() {
-  return weak_ptr_factory_.GetWeakPtr();
-}
-
-}  // namespace ash
diff --git a/chrome/browser/ui/webui/ash/login/gaia_password_changed_screen_handler.h b/chrome/browser/ui/webui/ash/login/gaia_password_changed_screen_handler.h
deleted file mode 100644
index 4adb2d28..0000000
--- a/chrome/browser/ui/webui/ash/login/gaia_password_changed_screen_handler.h
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright 2020 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_UI_WEBUI_ASH_LOGIN_GAIA_PASSWORD_CHANGED_SCREEN_HANDLER_H_
-#define CHROME_BROWSER_UI_WEBUI_ASH_LOGIN_GAIA_PASSWORD_CHANGED_SCREEN_HANDLER_H_
-
-#include <string>
-
-#include "base/memory/weak_ptr.h"
-#include "chrome/browser/ui/webui/ash/login/base_screen_handler.h"
-
-namespace ash {
-
-// TODO(b/315829727): remove now unused handler + all dependent resources.
-// Interface for dependency injection between GaiaPasswordChangedScreen and its
-// WebUI representation.
-class GaiaPasswordChangedView {
- public:
-  inline constexpr static StaticOobeScreenId kScreenId{
-      "gaia-password-changed", "GaiaPasswordChangedScreen"};
-
-  virtual ~GaiaPasswordChangedView() = default;
-
-  // Shows the contents of the screen.
-  virtual void Show(const std::string& email, bool has_error) = 0;
-
-  virtual void Show(const std::string& email) = 0;
-  virtual void ShowWrongPasswordError() = 0;
-  virtual void SuggestRecovery() = 0;
-  virtual base::WeakPtr<GaiaPasswordChangedView> AsWeakPtr() = 0;
-};
-
-class GaiaPasswordChangedScreenHandler final : public GaiaPasswordChangedView,
-                                               public BaseScreenHandler {
- public:
-  using TView = GaiaPasswordChangedView;
-
-  GaiaPasswordChangedScreenHandler();
-  GaiaPasswordChangedScreenHandler(const GaiaPasswordChangedScreenHandler&) =
-      delete;
-  GaiaPasswordChangedScreenHandler& operator=(
-      const GaiaPasswordChangedScreenHandler&) = delete;
-  ~GaiaPasswordChangedScreenHandler() override;
-
- private:
-  void Show(const std::string& email, bool has_error) override;
-  void Show(const std::string& email) override;
-  void ShowWrongPasswordError() override;
-  void SuggestRecovery() override;
-  base::WeakPtr<GaiaPasswordChangedView> AsWeakPtr() override;
-
-  // BaseScreenHandler:
-  void DeclareLocalizedValues(
-      ::login::LocalizedValuesBuilder* builder) override;
-
-  base::WeakPtrFactory<GaiaPasswordChangedView> weak_ptr_factory_{this};
-};
-
-}  // namespace ash
-
-#endif  // CHROME_BROWSER_UI_WEBUI_ASH_LOGIN_GAIA_PASSWORD_CHANGED_SCREEN_HANDLER_H_
diff --git a/chrome/browser/ui/webui/ash/login/oobe_ui.cc b/chrome/browser/ui/webui/ash/login/oobe_ui.cc
index aab7be29..9aa14ac 100644
--- a/chrome/browser/ui/webui/ash/login/oobe_ui.cc
+++ b/chrome/browser/ui/webui/ash/login/oobe_ui.cc
@@ -74,7 +74,6 @@
 #include "chrome/browser/ui/webui/ash/login/family_link_notice_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/fingerprint_setup_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/gaia_info_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/gesture_navigation_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/guest_tos_screen_handler.h"
@@ -530,8 +529,6 @@
 
   AddScreenHandler(std::make_unique<MarketingOptInScreenHandler>());
 
-  AddScreenHandler(std::make_unique<GaiaPasswordChangedScreenHandler>());
-
   AddScreenHandler(std::make_unique<GaiaScreenHandler>(network_state_informer_,
                                                        error_screen));
 
diff --git a/chrome/browser/ui/webui/ash/login/quick_start_screen_handler.cc b/chrome/browser/ui/webui/ash/login/quick_start_screen_handler.cc
index fbd903b..a73665c2 100644
--- a/chrome/browser/ui/webui/ash/login/quick_start_screen_handler.cc
+++ b/chrome/browser/ui/webui/ash/login/quick_start_screen_handler.cc
@@ -24,8 +24,9 @@
   CallExternalAPI("setPin", pin);
 }
 
-void QuickStartScreenHandler::SetQRCode(base::Value::List blob) {
-  CallExternalAPI("setQRCode", std::move(blob));
+void QuickStartScreenHandler::SetQRCode(base::Value::List blob,
+                                        const std::string url) {
+  CallExternalAPI("setQRCode", std::move(blob), url);
 }
 
 void QuickStartScreenHandler::SetDiscoverableName(
diff --git a/chrome/browser/ui/webui/ash/login/quick_start_screen_handler.h b/chrome/browser/ui/webui/ash/login/quick_start_screen_handler.h
index e33dbc7..18d6a8a 100644
--- a/chrome/browser/ui/webui/ash/login/quick_start_screen_handler.h
+++ b/chrome/browser/ui/webui/ash/login/quick_start_screen_handler.h
@@ -27,7 +27,7 @@
 
   virtual void Show() = 0;
   virtual void SetPIN(const std::string pin) = 0;
-  virtual void SetQRCode(base::Value::List blob) = 0;
+  virtual void SetQRCode(base::Value::List blob, const std::string url) = 0;
   virtual void SetDiscoverableName(const std::string& discoverable_name) = 0;
   virtual void ShowInitialUiStep() = 0;
   virtual void ShowBluetoothDialog() = 0;
@@ -60,7 +60,7 @@
   // QuickStartView:
   void Show() override;
   void SetPIN(const std::string pin) override;
-  void SetQRCode(base::Value::List blob) override;
+  void SetQRCode(base::Value::List blob, const std::string url) override;
   void SetDiscoverableName(const std::string& discoverable_name) override;
   void ShowInitialUiStep() override;
   void ShowBluetoothDialog() override;
diff --git a/chrome/browser/ui/webui/ash/login/testapi/oobe_test_api_handler.cc b/chrome/browser/ui/webui/ash/login/testapi/oobe_test_api_handler.cc
index a165c554..992c9929 100644
--- a/chrome/browser/ui/webui/ash/login/testapi/oobe_test_api_handler.cc
+++ b/chrome/browser/ui/webui/ash/login/testapi/oobe_test_api_handler.cc
@@ -117,24 +117,8 @@
   dict->Set("testapi_shouldSkipConsolidatedConsent",
             !BUILDFLAG(GOOGLE_CHROME_BRANDING));
   dict->Set("testapi_isHPSEnabled", ash::features::IsQuickDimEnabled());
-
-  bool skip_touchpad_scroll =
-      !features::IsOobeTouchpadScrollEnabled() ||
-      InputDeviceSettingsController::Get()->GetConnectedTouchpads().empty();
-  // TODO(b/327270907) Remove `testapi_shouldSkipTouchpadScroll`.
-  dict->Set("testapi_shouldSkipTouchpadScroll", skip_touchpad_scroll);
-
-  bool skip_display_size = !features::IsOobeDisplaySizeEnabled();
-  dict->Set("testapi_shouldSkipDisplaySize", skip_display_size);
-
-  // CHOOBE screen is only skipped if the number of optional screens is less
-  // than 3, since theme selection is always shown, CHOOBE should be skipped
-  // when display size Screen or touchpad scroll screen is skipped.
-  bool skip_choobe = !features::IsOobeChoobeEnabled() || skip_touchpad_scroll ||
-                     skip_display_size;
-  // TODO(b/327270907) Remove `testapi_shouldSkipChoobe`.
-  dict->Set("testapi_shouldSkipChoobe", skip_choobe);
-
+  dict->Set("testapi_shouldSkipDisplaySize",
+            !features::IsOobeDisplaySizeEnabled());
   dict->Set("testapi_shouldSkipGaiaInfoScreen",
             !features::IsOobeGaiaInfoScreenEnabled());
   dict->Set("testapi_isOobeQuickStartEnabled",
diff --git a/chrome/browser/ui/webui/ash/system_web_dialog_delegate.cc b/chrome/browser/ui/webui/ash/system_web_dialog_delegate.cc
index c4de5e44..ba0732f5 100644
--- a/chrome/browser/ui/webui/ash/system_web_dialog_delegate.cc
+++ b/chrome/browser/ui/webui/ash/system_web_dialog_delegate.cc
@@ -44,7 +44,8 @@
 // shadow since the dialog frame's border has its own shadow.
 views::Widget::InitParams CreateWidgetParams(
     SystemWebDialogDelegate::FrameKind frame_kind) {
-  views::Widget::InitParams params;
+  views::Widget::InitParams params(
+      views::Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET);
   params.corner_radius = kSystemDialogCornerRadiusDp;
   // Set shadow type according to the frame kind.
   switch (frame_kind) {
diff --git a/chrome/browser/ui/webui/new_tab_page/new_tab_page.mojom b/chrome/browser/ui/webui/new_tab_page/new_tab_page.mojom
index d158e0d7..1bfaf28 100644
--- a/chrome/browser/ui/webui/new_tab_page/new_tab_page.mojom
+++ b/chrome/browser/ui/webui/new_tab_page/new_tab_page.mojom
@@ -99,17 +99,6 @@
   most_visited.mojom.MostVisitedTheme most_visited;
 };
 
-// Specifies look of the doodle share button.
-struct DoodleShareButton {
-  // Position in pixels relative to the doodle's upper left corner.
-  int32 x;
-  int32 y;
-  // Button background color.
-  skia.mojom.SkColor background_color;
-  // Data URL of icon shown on the button.
-  url.mojom.Url icon_url;
-};
-
 // Type of image of an image doodle. Used for metrics logging only.
 enum DoodleImageType {
   // Animation of an animated doodle.
@@ -132,8 +121,6 @@
   // Color of the background the doodle was designed for. If the NTP background
   // differs from that color we show the doodle in a box of that color.
   skia.mojom.SkColor background_color;
-  // Specification of the share button.
-  DoodleShareButton? share_button;
   // URLs to be pinged when an image has been shown.
   url.mojom.Url image_impression_log_url;
   url.mojom.Url? animation_impression_log_url;
diff --git a/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.cc b/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.cc
index 6180b0b..4727ca7 100644
--- a/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.cc
+++ b/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.cc
@@ -314,11 +314,6 @@
     int width_px,
     int height_px,
     const std::string& background_color,
-    int share_button_x,
-    int share_button_y,
-    const std::string& share_button_icon,
-    const std::string& share_button_bg,
-    double share_button_opacity,
     GURL log_url,
     GURL cta_log_url) {
   auto doodle = new_tab_page::mojom::ImageDoodle::New();
@@ -331,16 +326,6 @@
   doodle->width = width_px;
   doodle->height = height_px;
   doodle->background_color = ParseHexColor(background_color);
-  if (!share_button_icon.empty()) {
-    doodle->share_button = new_tab_page::mojom::DoodleShareButton::New();
-    doodle->share_button->x = share_button_x;
-    doodle->share_button->y = share_button_y;
-    doodle->share_button->icon_url = GURL(base::StringPrintf(
-        "data:image/png;base64,%s", share_button_icon.c_str()));
-    doodle->share_button->background_color = SkColorSetA(
-        ParseHexColor(share_button_bg),
-        std::clamp(share_button_opacity, 0.0, 1.0) * SK_AlphaOPAQUE);
-  }
   if (type == search_provider_logos::LogoType::ANIMATED) {
     doodle->image_impression_log_url = cta_log_url;
     doodle->animation_impression_log_url = log_url;
@@ -1375,21 +1360,14 @@
         logo->metadata.type, logo->encoded_image->as_string(),
         logo->metadata.mime_type, logo->metadata.animated_url,
         logo->metadata.width_px, logo->metadata.height_px, "#ffffff",
-        logo->metadata.share_button_x, logo->metadata.share_button_y,
-        logo->metadata.share_button_icon, logo->metadata.share_button_bg,
-        logo->metadata.share_button_opacity, logo->metadata.log_url,
-        logo->metadata.cta_log_url);
+        logo->metadata.log_url, logo->metadata.cta_log_url);
     if (logo->dark_encoded_image) {
       image_doodle->dark = MakeImageDoodle(
           logo->metadata.type, logo->dark_encoded_image->as_string(),
           logo->metadata.dark_mime_type, logo->metadata.dark_animated_url,
           logo->metadata.dark_width_px, logo->metadata.dark_height_px,
           logo->metadata.dark_background_color,
-          logo->metadata.dark_share_button_x,
-          logo->metadata.dark_share_button_y,
-          logo->metadata.dark_share_button_icon,
-          logo->metadata.dark_share_button_bg,
-          logo->metadata.dark_share_button_opacity, logo->metadata.dark_log_url,
+          logo->metadata.dark_log_url,
           logo->metadata.dark_cta_log_url);
     }
     if (logo->metadata.on_click_url.is_valid()) {
diff --git a/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler_unittest.cc b/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler_unittest.cc
index 82a8fd8..5492d78 100644
--- a/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler_unittest.cc
+++ b/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler_unittest.cc
@@ -756,16 +756,6 @@
   logo.metadata.height_px = 2;
   logo.metadata.dark_width_px = 3;
   logo.metadata.dark_height_px = 4;
-  logo.metadata.share_button_x = 5;
-  logo.metadata.dark_share_button_x = 6;
-  logo.metadata.share_button_y = 7;
-  logo.metadata.dark_share_button_y = 8;
-  logo.metadata.share_button_opacity = 0.5;
-  logo.metadata.dark_share_button_opacity = 0.7;
-  logo.metadata.share_button_icon = "light share button";
-  logo.metadata.dark_share_button_icon = "dark share button";
-  logo.metadata.share_button_bg = "#000100";
-  logo.metadata.dark_share_button_bg = "#010000";
 
   auto doodle = GetDoodle(logo);
 
@@ -779,12 +769,6 @@
   EXPECT_EQ(1u, doodle->image->light->width);
   EXPECT_EQ(2u, doodle->image->light->height);
   EXPECT_EQ(SK_ColorWHITE, doodle->image->light->background_color);
-  EXPECT_EQ(5, doodle->image->light->share_button->x);
-  EXPECT_EQ(7, doodle->image->light->share_button->y);
-  EXPECT_EQ(SkColorSetARGB(127, 0, 1, 0),
-            doodle->image->light->share_button->background_color);
-  EXPECT_EQ("data:image/png;base64,light share button",
-            doodle->image->light->share_button->icon_url);
   EXPECT_EQ("https://doodle.com/light_cta_log_url",
             doodle->image->light->image_impression_log_url);
   EXPECT_EQ("https://doodle.com/light_log_url",
@@ -796,12 +780,6 @@
   EXPECT_EQ(3u, doodle->image->dark->width);
   EXPECT_EQ(4u, doodle->image->dark->height);
   EXPECT_EQ(SkColorSetRGB(0, 0, 1), doodle->image->dark->background_color);
-  EXPECT_EQ(6, doodle->image->dark->share_button->x);
-  EXPECT_EQ(8, doodle->image->dark->share_button->y);
-  EXPECT_EQ(SkColorSetARGB(178, 1, 0, 0),
-            doodle->image->dark->share_button->background_color);
-  EXPECT_EQ("data:image/png;base64,dark share button",
-            doodle->image->dark->share_button->icon_url);
   EXPECT_EQ("https://doodle.com/dark_cta_log_url",
             doodle->image->dark->image_impression_log_url);
   EXPECT_EQ("https://doodle.com/dark_log_url",
diff --git a/chrome/browser/ui/webui/print_preview/OWNERS b/chrome/browser/ui/webui/print_preview/OWNERS
index 9b08c66..ab04a06 100644
--- a/chrome/browser/ui/webui/print_preview/OWNERS
+++ b/chrome/browser/ui/webui/print_preview/OWNERS
@@ -1 +1,8 @@
 file://printing/OWNERS
+
+per-file extension_printer_handler_adapter*=xiangdongkong@google.com
+per-file extension_printer_handler_adapter*=jennyz@chromium.org
+per-file extension_printer_handler_adapter*=erikchen@chromium.org
+per-file extension_printer_service*=xiangdongkong@google.com
+per-file extension_printer_service*=jennyz@chromium.org
+per-file extension_printer_service*=erikchen@chromium.org
diff --git a/chrome/browser/ui/webui/webui_gallery/webui_gallery_ui.cc b/chrome/browser/ui/webui/webui_gallery/webui_gallery_ui.cc
index dbed78f..791cabf 100644
--- a/chrome/browser/ui/webui/webui_gallery/webui_gallery_ui.cc
+++ b/chrome/browser/ui/webui/webui_gallery/webui_gallery_ui.cc
@@ -13,6 +13,7 @@
 #include "chrome/grit/webui_gallery_resources.h"
 #include "chrome/grit/webui_gallery_resources_map.h"
 #include "components/favicon_base/favicon_url_parser.h"
+#include "components/strings/grit/components_strings.h"
 #include "content/public/browser/web_ui.h"
 #include "content/public/browser/web_ui_data_source.h"
 #include "services/network/public/mojom/content_security_policy.mojom.h"
@@ -40,6 +41,7 @@
   // TODO(colehorvitz): Promote to a place where it can be easily registered
   // by many WebUIs.
   source->AddString("opensInNewTab", "Opens in new tab");
+  source->AddLocalizedString("backButton", IDS_ACCNAME_BACK);
 
   // Add shared SidePanel resources so that those elements can be demonstrated
   // as well.
diff --git a/chrome/browser/visited_url_ranking/visited_url_ranking_service_factory.cc b/chrome/browser/visited_url_ranking/visited_url_ranking_service_factory.cc
index c7c5aa1..18b39044 100644
--- a/chrome/browser/visited_url_ranking/visited_url_ranking_service_factory.cc
+++ b/chrome/browser/visited_url_ranking/visited_url_ranking_service_factory.cc
@@ -144,7 +144,7 @@
 }
 
 bool VisitedURLRankingServiceFactory::ServiceIsNULLWhileTesting() const {
-  return false;
+  return true;
 }
 
 }  // namespace visited_url_ranking
diff --git a/chrome/browser/web_applications/commands/external_app_resolution_command_unittest.cc b/chrome/browser/web_applications/commands/external_app_resolution_command_unittest.cc
index 50cd015..6764ef2 100644
--- a/chrome/browser/web_applications/commands/external_app_resolution_command_unittest.cc
+++ b/chrome/browser/web_applications/commands/external_app_resolution_command_unittest.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/web_applications/commands/external_app_resolution_command.h"
 
 #include <memory>
+#include <optional>
 #include <string>
 #include <tuple>
 
@@ -43,6 +44,7 @@
 #include "components/webapps/browser/installable/installable_logging.h"
 #include "components/webapps/browser/web_contents/web_app_url_loader.h"
 #include "components/webapps/common/web_app_id.h"
+#include "components/webapps/common/web_page_metadata.mojom.h"
 #include "net/http/http_status_code.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "third_party/skia/include/core/SkColor.h"
@@ -836,100 +838,6 @@
   EXPECT_FALSE(registrar().IsLocallyInstalled(kWebAppId));
 }
 
-TEST_F(ExternalAppResolutionCommandTest,
-       InstallWebAppWithParams_DisplayModeFromWebAppInstallInfo) {
-  {
-    GURL url("https://example1.com/");
-    auto data_retriever = std::make_unique<FakeDataRetriever>();
-    data_retriever->BuildDefaultDataToRetrieve(url, url);
-
-    auto web_app_info = std::make_unique<WebAppInstallInfo>();
-    web_app_info->user_display_mode = mojom::UserDisplayMode::kBrowser;
-    data_retriever->SetRendererWebAppInstallInfo(std::move(web_app_info));
-    SetPageState({url, std::nullopt, ExternalInstallSource::kInternalDefault});
-
-    ExternalInstallOptions install_options(
-        url, /*user_display_mode=*/std::nullopt,
-        ExternalInstallSource::kExternalDefault);
-    auto result = InstallAndWait(install_options, std::move(data_retriever));
-
-    ASSERT_TRUE(result.app_id.has_value());
-    EXPECT_EQ(mojom::UserDisplayMode::kBrowser, provider()
-                                                    ->registrar_unsafe()
-                                                    .GetAppById(*result.app_id)
-                                                    ->user_display_mode());
-  }
-  {
-    GURL url("https://example2.com/");
-    auto data_retriever = std::make_unique<FakeDataRetriever>();
-    data_retriever->BuildDefaultDataToRetrieve(url, url);
-
-    auto web_app_info = std::make_unique<WebAppInstallInfo>();
-    web_app_info->user_display_mode = mojom::UserDisplayMode::kStandalone;
-    data_retriever->SetRendererWebAppInstallInfo(std::move(web_app_info));
-    SetPageState({url, std::nullopt, ExternalInstallSource::kInternalDefault});
-
-    ExternalInstallOptions install_options(
-        url, /*user_display_mode=*/std::nullopt,
-        ExternalInstallSource::kExternalDefault);
-    auto result = InstallAndWait(install_options, std::move(data_retriever));
-
-    ASSERT_TRUE(result.app_id.has_value());
-    EXPECT_EQ(mojom::UserDisplayMode::kStandalone,
-              provider()
-                  ->registrar_unsafe()
-                  .GetAppById(*result.app_id)
-                  ->user_display_mode());
-  }
-}
-
-TEST_F(ExternalAppResolutionCommandTest,
-       InstallWebAppWithParams_DisplayModeOverrideByExternalInstallOptions) {
-  {
-    GURL url("https://example3.com/");
-    auto data_retriever = std::make_unique<FakeDataRetriever>();
-    data_retriever->BuildDefaultDataToRetrieve(url, url);
-
-    auto web_app_info = std::make_unique<WebAppInstallInfo>();
-    web_app_info->user_display_mode = mojom::UserDisplayMode::kStandalone;
-    data_retriever->SetRendererWebAppInstallInfo(std::move(web_app_info));
-    SetPageState({url, std::nullopt, ExternalInstallSource::kInternalDefault});
-
-    ExternalInstallOptions install_options(
-        url, mojom::UserDisplayMode::kBrowser,
-        ExternalInstallSource::kExternalDefault);
-    auto result = InstallAndWait(install_options, std::move(data_retriever));
-
-    ASSERT_TRUE(result.app_id.has_value());
-    EXPECT_EQ(mojom::UserDisplayMode::kBrowser, provider()
-                                                    ->registrar_unsafe()
-                                                    .GetAppById(*result.app_id)
-                                                    ->user_display_mode());
-  }
-  {
-    GURL url("https://example4.com/");
-    auto data_retriever = std::make_unique<FakeDataRetriever>();
-    data_retriever->BuildDefaultDataToRetrieve(url, url);
-    SetPageState({url, std::nullopt, ExternalInstallSource::kInternalDefault});
-
-    auto web_app_info = std::make_unique<WebAppInstallInfo>();
-    web_app_info->user_display_mode = mojom::UserDisplayMode::kBrowser;
-    data_retriever->SetRendererWebAppInstallInfo(std::move(web_app_info));
-
-    ExternalInstallOptions install_options(
-        url, mojom::UserDisplayMode::kStandalone,
-        ExternalInstallSource::kExternalDefault);
-    auto result = InstallAndWait(install_options, std::move(data_retriever));
-
-    ASSERT_TRUE(result.app_id.has_value());
-    EXPECT_EQ(mojom::UserDisplayMode::kStandalone,
-              provider()
-                  ->registrar_unsafe()
-                  .GetAppById(*result.app_id)
-                  ->user_display_mode());
-  }
-}
-
 TEST_F(ExternalAppResolutionCommandTest, UpgradeLock) {
   ExternalInstallOptions install_options(
       kWebAppUrl, mojom::UserDisplayMode::kStandalone,
@@ -1011,10 +919,8 @@
   {
     auto data_retriever = std::make_unique<FakeDataRetriever>();
     data_retriever->BuildDefaultDataToRetrieve(kWebAppUrl, kWebAppScope);
-
-    auto web_app_info = std::make_unique<WebAppInstallInfo>();
-    web_app_info->user_display_mode = mojom::UserDisplayMode::kStandalone;
-    data_retriever->SetRendererWebAppInstallInfo(std::move(web_app_info));
+    data_retriever->SetWebPageMetadata(kWebAppUrl, u"Page Title",
+                                       /*opt_metadata=*/std::nullopt);
     SetPageState(
         {kWebAppUrl, std::nullopt, ExternalInstallSource::kInternalDefault});
 
@@ -1049,7 +955,8 @@
     new_data_retriever->SetIcons(std::move(icons_map));
     new_data_retriever->SetManifest(
         std::move(manifest), webapps::InstallableStatusCode::NO_ERROR_DETECTED);
-    new_data_retriever->SetEmptyRendererWebAppInstallInfo();
+    new_data_retriever->SetWebPageMetadata(kWebAppUrl, u"Page Title",
+                                           /*opt_metadata=*/std::nullopt);
     SetPageState(
         {kWebAppUrl, std::nullopt, ExternalInstallSource::kInternalDefault});
 
@@ -1088,10 +995,6 @@
       http_results[IconUrlWithSize::CreateForUnspecifiedSize(
           url_and_bitmap.first)] = net::HttpStatusCode::HTTP_OK;
     }
-
-    auto web_app_info = std::make_unique<WebAppInstallInfo>();
-    web_app_info->user_display_mode = mojom::UserDisplayMode::kStandalone;
-
     auto data_retriever = std::make_unique<FakeDataRetriever>();
     data_retriever->BuildDefaultDataToRetrieve(kWebAppUrl, kWebAppScope);
     data_retriever->SetIconsDownloadedResult(IconsDownloadedResult::kCompleted);
@@ -1099,7 +1002,8 @@
     data_retriever->SetIcons(std::move(icons_map));
     data_retriever->SetManifest(
         std::move(manifest), webapps::InstallableStatusCode::NO_ERROR_DETECTED);
-    data_retriever->SetRendererWebAppInstallInfo(std::move(web_app_info));
+    data_retriever->SetWebPageMetadata(kWebAppUrl, u"Page Title",
+                                       /*opt_metadata=*/std::nullopt);
     SetPageState(
         {kWebAppUrl, std::nullopt, ExternalInstallSource::kInternalDefault});
 
@@ -1143,7 +1047,8 @@
     new_data_retriever->SetManifest(
         std::move(new_manifest),
         webapps::InstallableStatusCode::NO_ERROR_DETECTED);
-    new_data_retriever->SetEmptyRendererWebAppInstallInfo();
+    new_data_retriever->SetWebPageMetadata(kWebAppUrl, u"Page Title",
+                                           /*opt_metadata=*/std::nullopt);
     SetPageState(
         {kWebAppUrl, std::nullopt, ExternalInstallSource::kInternalDefault});
 
@@ -1185,9 +1090,6 @@
           url_and_bitmap.first)] = net::HttpStatusCode::HTTP_OK;
     }
 
-    auto web_app_info = std::make_unique<WebAppInstallInfo>();
-    web_app_info->user_display_mode = mojom::UserDisplayMode::kStandalone;
-
     auto data_retriever = std::make_unique<FakeDataRetriever>();
     data_retriever->BuildDefaultDataToRetrieve(kWebAppUrl, kWebAppScope);
     data_retriever->SetIconsDownloadedResult(IconsDownloadedResult::kCompleted);
@@ -1195,7 +1097,8 @@
     data_retriever->SetIcons(std::move(icons_map));
     data_retriever->SetManifest(
         std::move(manifest), webapps::InstallableStatusCode::NO_ERROR_DETECTED);
-    data_retriever->SetRendererWebAppInstallInfo(std::move(web_app_info));
+    data_retriever->SetWebPageMetadata(kWebAppUrl, u"Page Title",
+                                       /*opt_metadata=*/std::nullopt);
     SetPageState(
         {kWebAppUrl, std::nullopt, ExternalInstallSource::kInternalDefault});
 
@@ -1240,7 +1143,8 @@
     new_data_retriever->SetManifest(
         std::move(new_manifest),
         webapps::InstallableStatusCode::NO_ERROR_DETECTED);
-    new_data_retriever->SetEmptyRendererWebAppInstallInfo();
+    new_data_retriever->SetWebPageMetadata(kWebAppUrl, u"Page Title",
+                                           /*opt_metadata=*/std::nullopt);
     SetPageState(
         {kWebAppUrl, std::nullopt, ExternalInstallSource::kInternalDefault});
 
diff --git a/chrome/browser/web_applications/test/fake_data_retriever.cc b/chrome/browser/web_applications/test/fake_data_retriever.cc
index 7fdced67..62d4be5b 100644
--- a/chrome/browser/web_applications/test/fake_data_retriever.cc
+++ b/chrome/browser/web_applications/test/fake_data_retriever.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/web_applications/test/fake_data_retriever.h"
 
+#include <memory>
 #include <optional>
 #include <utility>
 
@@ -12,9 +13,12 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/task/sequenced_task_runner.h"
 #include "chrome/browser/web_applications/web_app_helpers.h"
+#include "chrome/browser/web_applications/web_app_install_info.h"
 #include "chrome/browser/web_applications/web_app_install_utils.h"
+#include "chrome/browser/web_applications/web_contents/web_app_data_retriever.h"
 #include "components/webapps/browser/installable/installable_logging.h"
 #include "components/webapps/browser/installable/installable_params.h"
+#include "components/webapps/common/web_page_metadata.mojom.h"
 #include "third_party/blink/public/mojom/manifest/manifest.mojom.h"
 
 namespace web_app {
@@ -60,13 +64,23 @@
   icons_http_results_.clear();
 }
 
-void FakeDataRetriever::SetRendererWebAppInstallInfo(
-    std::unique_ptr<WebAppInstallInfo> web_app_info) {
-  web_app_info_ = std::move(web_app_info);
-}
-
-void FakeDataRetriever::SetEmptyRendererWebAppInstallInfo() {
-  SetRendererWebAppInstallInfo(std::make_unique<WebAppInstallInfo>());
+void FakeDataRetriever::SetWebPageMetadata(
+    const GURL& last_committed_url,
+    const std::u16string& title,
+    std::optional<webapps::mojom::WebPageMetadata> opt_metadata) {
+  CHECK(last_committed_url.is_valid());
+  GURL fallback_start_url = last_committed_url;
+  std::u16string fallback_title = title;
+  if (fallback_title.empty()) {
+    fallback_title = base::UTF8ToUTF16(fallback_start_url.spec());
+  }
+  web_app_info_ = std::make_unique<WebAppInstallInfo>(
+      GenerateManifestIdFromStartUrlOnly(fallback_start_url),
+      fallback_start_url);
+  if (opt_metadata) {
+    WebAppDataRetriever::PopulateWebAppInfoFromMetadata(web_app_info_.get(),
+                                                        opt_metadata.value());
+  }
 }
 
 void FakeDataRetriever::SetManifest(blink::mojom::ManifestPtr manifest,
@@ -96,7 +110,8 @@
 
 void FakeDataRetriever::BuildDefaultDataToRetrieve(const GURL& url,
                                                    const GURL& scope) {
-  SetEmptyRendererWebAppInstallInfo();
+  SetWebPageMetadata(url, u"Page Title",
+                     /*opt_metadata=*/std::nullopt);
 
   auto manifest = blink::mojom::Manifest::New();
   manifest->start_url = url;
diff --git a/chrome/browser/web_applications/test/fake_data_retriever.h b/chrome/browser/web_applications/test/fake_data_retriever.h
index f941ccd..11f75403 100644
--- a/chrome/browser/web_applications/test/fake_data_retriever.h
+++ b/chrome/browser/web_applications/test/fake_data_retriever.h
@@ -7,6 +7,7 @@
 
 #include <memory>
 #include <optional>
+#include <string>
 
 #include "base/functional/callback.h"
 #include "base/memory/weak_ptr.h"
@@ -44,9 +45,10 @@
                 GetIconsCallback callback) override;
 
   // Set info to respond on |GetWebAppInstallInfo|.
-  void SetRendererWebAppInstallInfo(
-      std::unique_ptr<WebAppInstallInfo> web_app_info);
-  void SetEmptyRendererWebAppInstallInfo();
+  void SetWebPageMetadata(
+      const GURL& last_committed_url,
+      const std::u16string& title,
+      std::optional<webapps::mojom::WebPageMetadata> opt_metadata);
   // Set arguments to respond on |CheckInstallabilityAndRetrieveManifest|.
   void SetManifest(blink::mojom::ManifestPtr manifest,
                    webapps::InstallableStatusCode error_code,
diff --git a/chrome/browser/web_applications/web_contents/web_app_data_retriever_unittest.cc b/chrome/browser/web_applications/web_contents/web_app_data_retriever_unittest.cc
index d410569..f337aca 100644
--- a/chrome/browser/web_applications/web_contents/web_app_data_retriever_unittest.cc
+++ b/chrome/browser/web_applications/web_contents/web_app_data_retriever_unittest.cc
@@ -62,32 +62,21 @@
         std::move(handle)));
   }
 
-  // Set |web_app_info| to respond on |GetWebAppInstallInfo|.
-  void SetWebAppInstallInfo(std::unique_ptr<WebAppInstallInfo> web_app_info) {
-    web_app_info_ = std::move(web_app_info);
+  void SetWebPageMetadata(const GURL& application_url,
+                          const std::u16string& title,
+                          const std::u16string& description) {
+    web_page_metadata_->application_name = title;
+    web_page_metadata_->description = description;
+    web_page_metadata_->application_url = application_url;
   }
 
   void GetWebPageMetadata(GetWebPageMetadataCallback callback) override {
-    webapps::mojom::WebPageMetadataPtr web_page_metadata(
-        webapps::mojom::WebPageMetadata::New());
-    if (!web_app_info_) {
-      std::move(callback).Run(std::move(web_page_metadata));
-      return;
-    }
-    web_page_metadata->application_name = web_app_info_->title;
-    web_page_metadata->description = web_app_info_->description;
-    web_page_metadata->application_url = web_app_info_->start_url;
-
-    // Convert more fields as needed.
-    DCHECK(web_app_info_->manifest_icons.empty());
-    DCHECK(web_app_info_->mobile_capable ==
-           WebAppInstallInfo::MOBILE_CAPABLE_UNSPECIFIED);
-
-    std::move(callback).Run(std::move(web_page_metadata));
+    std::move(callback).Run(web_page_metadata_.Clone());
   }
 
  private:
-  std::unique_ptr<WebAppInstallInfo> web_app_info_;
+  webapps::mojom::WebPageMetadataPtr web_page_metadata_ =
+      webapps::mojom::WebPageMetadata::New();
 
   mojo::AssociatedReceiver<webapps::mojom::WebPageMetadataAgent> receiver_{
       this};
@@ -122,9 +111,11 @@
         web_contents()->GetPrimaryMainFrame());
   }
 
-  void SetRendererWebAppInstallInfo(
-      std::unique_ptr<WebAppInstallInfo> web_app_info) {
-    fake_chrome_render_frame_.SetWebAppInstallInfo(std::move(web_app_info));
+  void SetRendererWebPageMetadata(const GURL& application_url,
+                                  const std::u16string& title,
+                                  const std::u16string& description) {
+    fake_chrome_render_frame_.SetWebPageMetadata(application_url, title,
+                                                 description);
   }
 
   void GetWebAppInstallInfoCallback(
@@ -178,7 +169,8 @@
   web_contents_tester()->NavigateAndCommit(kFooUrl);
 
   // No install info present.
-  SetRendererWebAppInstallInfo(nullptr);
+  SetRendererWebPageMetadata(/*application_url=*/GURL(), /*title=*/u"",
+                             /*description=*/u"");
 
   base::RunLoop run_loop;
   WebAppDataRetriever retriever;
@@ -199,8 +191,9 @@
   web_contents_tester()->NavigateAndCommit(GURL("https://foo.example"));
 
   GURL other_app_url = GURL("https://bar.example");
-  SetRendererWebAppInstallInfo(
-      WebAppInstallInfo::CreateWithStartUrlForTesting(other_app_url));
+  std::u16string other_app_title = u"Other App Title";
+  SetRendererWebPageMetadata(other_app_url, other_app_title,
+                             /*description=*/u"");
 
   base::RunLoop run_loop;
   WebAppDataRetriever retriever;
@@ -211,6 +204,7 @@
   run_loop.Run();
 
   EXPECT_EQ(other_app_url, web_app_info()->start_url);
+  EXPECT_EQ(other_app_title, web_app_info()->title);
 }
 
 TEST_F(WebAppDataRetrieverTest, GetWebAppInstallInfo_TitleAbsentFromRenderer) {
@@ -220,12 +214,8 @@
 
   web_contents_tester()->SetTitle(kFooTitle);
 
-  auto original_web_app_info = WebAppInstallInfo::CreateWithStartUrlForTesting(
-      GURL("https://foo.example"));
-  original_web_app_info->title = u"";
-
-  SetRendererWebAppInstallInfo(std::move(original_web_app_info));
-
+  SetRendererWebPageMetadata(GURL("https://foo.example"), /*title=*/u"",
+                             /*description=*/u"");
   base::RunLoop run_loop;
   WebAppDataRetriever retriever;
   retriever.GetWebAppInstallInfo(
@@ -234,8 +224,7 @@
                      base::Unretained(this), run_loop.QuitClosure()));
   run_loop.Run();
 
-  // If the WebAppInstallInfo has no title, we fallback to the WebContents
-  // title.
+  // If the metadata has no title, we fallback to the WebContents title.
   EXPECT_EQ(kFooTitle, web_app_info()->title);
 }
 
@@ -247,11 +236,8 @@
 
   web_contents_tester()->SetTitle(u"");
 
-  auto original_web_app_info = WebAppInstallInfo::CreateWithStartUrlForTesting(
-      GURL("https://foo.example"));
-  original_web_app_info->title = u"";
-
-  SetRendererWebAppInstallInfo(std::move(original_web_app_info));
+  SetRendererWebPageMetadata(GURL("https://foo.example"), /*title=*/u"",
+                             /*description=*/u"");
 
   base::RunLoop run_loop;
   WebAppDataRetriever retriever;
@@ -362,8 +348,6 @@
   const GURL kFooUrl("https://foo.example/bar");
   web_contents_tester()->NavigateAndCommit(kFooUrl.DeprecatedGetOriginAsURL());
 
-  SetRendererWebAppInstallInfo(nullptr);
-
   base::RunLoop run_loop;
   WebAppDataRetriever retriever;
   retriever.GetWebAppInstallInfo(
diff --git a/chrome/browser/webauthn/OWNERS b/chrome/browser/webauthn/OWNERS
index ffa5b2e..dd5ad77 100644
--- a/chrome/browser/webauthn/OWNERS
+++ b/chrome/browser/webauthn/OWNERS
@@ -1,4 +1 @@
 file://device/fido/OWNERS
-
-# For updates to MockTrustedVaultConnection
-per-file enclave_authenticator_browsertest.cc=file://components/trusted_vault/OWNERS
diff --git a/chrome/browser/webauthn/chrome_authenticator_request_delegate.cc b/chrome/browser/webauthn/chrome_authenticator_request_delegate.cc
index 6c23e92..32cf77a 100644
--- a/chrome/browser/webauthn/chrome_authenticator_request_delegate.cc
+++ b/chrome/browser/webauthn/chrome_authenticator_request_delegate.cc
@@ -689,6 +689,15 @@
   return enclave_controller_.get();
 }
 
+#if BUILDFLAG(IS_CHROMEOS)
+chromeos::PasskeyDialogController&
+ChromeAuthenticatorRequestDelegate::chromeos_passkey_controller_for_testing()
+    const {
+  CHECK(chromeos_passkey_controller_);
+  return *chromeos_passkey_controller_;
+}
+#endif
+
 void ChromeAuthenticatorRequestDelegate::SetRelyingPartyId(
     const std::string& rp_id) {
   dialog_model_->relying_party_id = rp_id;
@@ -850,7 +859,7 @@
         std::make_unique<chromeos::PasskeyDiscovery>(GetRenderFrameHost()));
   }
 #endif
-  return {};
+  return discoveries;
 }
 
 void ChromeAuthenticatorRequestDelegate::ConfigureDiscoveries(
diff --git a/chrome/browser/webauthn/chrome_authenticator_request_delegate.h b/chrome/browser/webauthn/chrome_authenticator_request_delegate.h
index ff0cc39..4257590 100644
--- a/chrome/browser/webauthn/chrome_authenticator_request_delegate.h
+++ b/chrome/browser/webauthn/chrome_authenticator_request_delegate.h
@@ -179,6 +179,10 @@
   }
 
   GPMEnclaveController* enclave_controller_for_testing() const;
+#if BUILDFLAG(IS_CHROMEOS)
+  chromeos::PasskeyDialogController& chromeos_passkey_controller_for_testing()
+      const;
+#endif
 
   // content::AuthenticatorRequestClientDelegate:
   void SetRelyingPartyId(const std::string& rp_id) override;
diff --git a/chrome/browser/webauthn/chromeos/passkey_authenticator.cc b/chrome/browser/webauthn/chromeos/passkey_authenticator.cc
index ffdb70ef..1fbce86 100644
--- a/chrome/browser/webauthn/chromeos/passkey_authenticator.cc
+++ b/chrome/browser/webauthn/chromeos/passkey_authenticator.cc
@@ -208,6 +208,7 @@
       base::as_byte_span(unsealed_credential_secrets.private_key()),
       signed_over_data);
   if (!assertion_signature) {
+    FIDO_LOG(ERROR) << "Generating assertion signature failed";
     std::move(callback).Run(CtapDeviceResponseCode::kCtap2ErrNoCredentials, {});
     return;
   }
@@ -226,15 +227,6 @@
                           std::move(responses));
 }
 
-void PasskeyAuthenticator::GetPlatformCredentialInfoForRequest(
-    const CtapGetAssertionRequest& request,
-    const CtapGetAssertionOptions& options,
-    GetPlatformCredentialInfoForRequestCallback callback) {
-  std::move(callback).Run(
-      {},
-      FidoRequestHandlerBase::RecognizedCredential::kHasRecognizedCredential);
-}
-
 void PasskeyAuthenticator::Cancel() {
   NOTIMPLEMENTED();
 }
diff --git a/chrome/browser/webauthn/chromeos/passkey_authenticator.h b/chrome/browser/webauthn/chromeos/passkey_authenticator.h
index a053ef8..30d4c8d80 100644
--- a/chrome/browser/webauthn/chromeos/passkey_authenticator.h
+++ b/chrome/browser/webauthn/chromeos/passkey_authenticator.h
@@ -43,10 +43,6 @@
   void GetAssertion(device::CtapGetAssertionRequest request,
                     device::CtapGetAssertionOptions options,
                     GetAssertionCallback callback) override;
-  void GetPlatformCredentialInfoForRequest(
-      const device::CtapGetAssertionRequest& request,
-      const device::CtapGetAssertionOptions& options,
-      GetPlatformCredentialInfoForRequestCallback callback) override;
   void Cancel() override;
   device::AuthenticatorType GetType() const override;
   std::string GetId() const override;
diff --git a/chrome/browser/webauthn/chromeos/passkey_browsertest.cc b/chrome/browser/webauthn/chromeos/passkey_browsertest.cc
new file mode 100644
index 0000000..fbd1c323
--- /dev/null
+++ b/chrome/browser/webauthn/chromeos/passkey_browsertest.cc
@@ -0,0 +1,413 @@
+// 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 "base/task/sequenced_task_runner.h"
+#include "base/test/bind.h"
+#include "base/time/time.h"
+#include "chrome/browser/signin/identity_manager_factory.h"
+#include "chrome/browser/signin/identity_test_environment_profile_adaptor.h"
+#include "chrome/browser/sync/sync_service_factory.h"
+#include "chrome/browser/sync/test/integration/sessions_helper.h"
+#include "chrome/browser/sync/test/integration/sync_service_impl_harness.h"
+#include "chrome/browser/sync/test/integration/sync_test.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_commands.h"
+#include "chrome/browser/webauthn/authenticator_request_dialog_model.h"
+#include "chrome/browser/webauthn/chrome_authenticator_request_delegate.h"
+#include "chrome/browser/webauthn/chromeos/passkey_dialog_controller.h"
+#include "chrome/browser/webauthn/chromeos/passkey_in_session_auth.h"
+#include "chrome/browser/webauthn/chromeos/passkey_service.h"
+#include "chrome/browser/webauthn/chromeos/passkey_service_factory.h"
+#include "chrome/browser/webauthn/passkey_model_factory.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "components/network_session_configurator/common/network_switches.h"
+#include "components/signin/public/base/consent_level.h"
+#include "components/signin/public/identity_manager/identity_manager.h"
+#include "components/sync/protocol/webauthn_credential_specifics.pb.h"
+#include "components/sync/service/sync_service.h"
+#include "components/sync/service/sync_service_impl.h"
+#include "components/trusted_vault/test/fake_trusted_vault_client.h"
+#include "components/trusted_vault/test/mock_trusted_vault_connection.h"
+#include "content/public/test/browser_test.h"
+#include "content/public/test/browser_test_utils.h"
+#include "device/bluetooth/bluetooth_adapter_factory.h"
+#include "device/fido/features.h"
+#include "net/dns/mock_host_resolver.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+#include "chromeos/lacros/lacros_service.h"
+#include "services/device/public/cpp/test/fake_hid_manager.h"
+#endif
+
+using testing::_;
+
+namespace chromeos {
+namespace {
+
+constexpr std::string_view kGetAssertionRequest = R"((() => {
+  return navigator.credentials.get({ publicKey: {
+    challenge: new Uint8Array([0]),
+    allowCredentials: [],
+  }}).then(c => window.domAutomationController.send('webauthn: OK'),
+           e => window.domAutomationController.send('error ' + e));
+})())";
+
+constexpr std::array<uint8_t, 32> kTrustedVaultKey{'k'};
+constexpr uint8_t kTrustedVaultKeyVersion = 0;
+
+constexpr std::string_view kRpId = "www.example.com";
+
+// ScopedInSessionAuthOverride disables the InSessionAuth dialog that the
+// authenticator uses to assert UV.
+class ScopedInSessionAuthOverride : public PasskeyInSessionAuthProvider {
+ public:
+  ScopedInSessionAuthOverride() {
+    PasskeyInSessionAuthProvider::SetInstanceForTesting(this);
+  }
+
+  ~ScopedInSessionAuthOverride() override {
+    PasskeyInSessionAuthProvider::SetInstanceForTesting(nullptr);
+  }
+
+  void ShowPasskeyInSessionAuthDialog(
+      aura::Window* window,
+      const std::string& rp_id,
+      base::OnceCallback<void(bool)> result_callback) override {
+    base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
+        FROM_HERE, base::BindOnce(std::move(result_callback), true));
+  }
+};
+
+// TestObserver lets us inspect and instrument the UI by observing the
+// ChromeAuthenticatorRequestDelegate and its associated
+// AuthenticatorRequestDialogModel.
+class TestObserver : public ChromeAuthenticatorRequestDelegate::TestObserver,
+                     public AuthenticatorRequestDialogModel::Observer {
+ public:
+  TestObserver() {
+    ChromeAuthenticatorRequestDelegate::SetGlobalObserverForTesting(this);
+  }
+
+  ~TestObserver() override {
+    ChromeAuthenticatorRequestDelegate::SetGlobalObserverForTesting(nullptr);
+  }
+
+  void WaitForUI() {
+    if (ui_shown_) {
+      return;
+    }
+    wait_ui_loop_ = std::make_unique<base::RunLoop>();
+    wait_ui_loop_->Run();
+    wait_ui_loop_.reset();
+    CHECK(ui_shown_);
+  }
+
+  bool gpm_ready() { return gpm_ready_; }
+
+  void WaitForGPMReady() {
+    if (gpm_ready_) {
+      return;
+    }
+    gpm_ready_loop_.Run();
+  }
+
+  void WaitForStep(AuthenticatorRequestDialogModel::Step step) {
+    CHECK(request_delegate_);
+    while (request_delegate_->dialog_model()->step() != step) {
+      wait_step_loop_ = std::make_unique<base::RunLoop>();
+      wait_step_loop_->Run();
+    }
+  }
+
+  ChromeAuthenticatorRequestDelegate& request_delegate() {
+    CHECK(request_delegate_) << "No WebAuthn request in progress?";
+    return *request_delegate_;
+  }
+
+  // AuthenticatorRequestDialogModel::Observer:
+  void OnStepTransition() override {
+    if (wait_step_loop_) {
+      wait_step_loop_->QuitWhenIdle();
+    }
+  }
+
+  void OnChromeOSGPMRequestReady() override {
+    CHECK(!gpm_ready_);
+    gpm_ready_ = true;
+    gpm_ready_loop_.QuitWhenIdle();
+  }
+
+  // ChromeAuthenticatorRequestDelegate::TestObserver:
+  void UIShown(ChromeAuthenticatorRequestDelegate* delegate) override {
+    CHECK(!ui_shown_);
+    ui_shown_ = true;
+    if (wait_ui_loop_) {
+      wait_ui_loop_->QuitWhenIdle();
+    }
+  }
+
+  void Created(ChromeAuthenticatorRequestDelegate* delegate) override {
+    CHECK(!request_delegate_);
+    request_delegate_ = delegate;
+    request_delegate_->dialog_model()->observers.AddObserver(this);
+  }
+
+  void OnDestroy(ChromeAuthenticatorRequestDelegate* delegate) override {
+    CHECK(request_delegate_);
+    request_delegate_->dialog_model()->observers.RemoveObserver(this);
+    request_delegate_ = nullptr;
+  }
+
+  std::vector<std::unique_ptr<device::cablev2::Pairing>>
+  GetCablePairingsFromSyncedDevices() override {
+    return {};
+  }
+
+  void OnTransportAvailabilityEnumerated(
+      ChromeAuthenticatorRequestDelegate* delegate,
+      device::FidoRequestHandlerBase::TransportAvailabilityInfo* tai) override {
+  }
+
+  void CableV2ExtensionSeen(
+      base::span<const uint8_t> server_link_data) override {}
+
+  void AccountSelectorShown(
+      const std::vector<device::AuthenticatorGetAssertionResponse>& responses)
+      override {}
+
+ private:
+  bool ui_shown_ = false;
+  std::unique_ptr<base::RunLoop> wait_ui_loop_;
+  bool gpm_ready_ = false;
+  base::RunLoop gpm_ready_loop_;
+  std::unique_ptr<base::RunLoop> wait_step_loop_;
+
+  raw_ptr<ChromeAuthenticatorRequestDelegate> request_delegate_ = nullptr;
+};
+
+class ChromeOSPasskeyBrowserTest : public SyncTest {
+ public:
+  ChromeOSPasskeyBrowserTest() : SyncTest(SINGLE_CLIENT) {}
+  ~ChromeOSPasskeyBrowserTest() override = default;
+
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+  void CreatedBrowserMainParts(
+      content::BrowserMainParts* browser_main_parts) override {
+    SyncTest::CreatedBrowserMainParts(browser_main_parts);
+    // Initialize a FakeHidManager. Otherwise, the FidoHidDiscovery instantiated
+    // for the WebAuthn request fails to enumerate devices and holds up the
+    // request indefinitely.
+    mojo::PendingRemote<device::mojom::HidManager> pending_remote;
+    fake_hid_manager_.Bind(pending_remote.InitWithNewPipeAndPassReceiver());
+    chromeos::LacrosService::Get()->InjectRemoteForTesting(
+        std::move(pending_remote));
+  }
+#endif
+
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    SyncTest::SetUpCommandLine(command_line);
+    command_line->AppendSwitch(switches::kIgnoreCertificateErrors);
+    command_line->AppendSwitch(switches::kDisableFakeServerFailureOutput);
+  }
+
+  void SetUp() override {
+    https_server_.ServeFilesFromSourceDirectory(GetChromeTestDataDir());
+    ASSERT_TRUE(https_server_.InitializeAndListen());
+
+    // Disable Bluetooth to avoid requests handlers attempting to enumerate
+    // BLE-based authenticators. This significantly speeds up the tests.
+    bluetooth_values_for_testing_ =
+        device::BluetoothAdapterFactory::Get()->InitGlobalValuesForTesting();
+    bluetooth_values_for_testing_->SetLESupported(false);
+
+    SyncTest::SetUp();
+  }
+
+  void SetUpOnMainThread() override {
+    SyncTest::SetUpOnMainThread();
+
+    host_resolver()->AddRule("*", "127.0.0.1");
+    https_server_.StartAcceptingConnections();
+  }
+
+  void TearDownOnMainThread() override {
+    test_observer_.reset();
+    trusted_vault_connection_ = nullptr;
+    passkey_service_ = nullptr;
+    ASSERT_TRUE(https_server_.ShutdownAndWaitUntilComplete());
+    SyncTest::TearDownOnMainThread();
+  }
+
+  TestObserver& test_observer() { return *test_observer_; }
+
+  webauthn::PasskeyModel* passkey_model() {
+    return PasskeyModelFactory::GetInstance()->GetForProfile(GetProfile(0));
+  }
+
+  ChromeAuthenticatorRequestDelegate& request_delegate() {
+    return test_observer().request_delegate();
+  }
+
+  AuthenticatorRequestDialogModel& dialog_model() {
+    return *test_observer().request_delegate().dialog_model();
+  }
+
+  sync_pb::WebauthnCredentialSpecifics InjectTestPasskey() {
+    std::vector<uint8_t> unused_public_key_spki_der;
+    return passkey_model()->CreatePasskey(
+        kRpId,
+        webauthn::PasskeyModel::UserEntity(std::vector<uint8_t>(32, 'u'),
+                                           "example user", "user@example.com"),
+        kTrustedVaultKey, kTrustedVaultKeyVersion, &unused_public_key_spki_der);
+  }
+
+  void SimulateTrustedVaultRecovery() {
+    const std::vector<std::vector<uint8_t>> trusted_vault_keys = {
+        {kTrustedVaultKey.begin(), kTrustedVaultKey.end()}};
+    fake_trusted_vault_client_->server()->StoreKeysOnServer(
+        GetSyncService(0)->GetAccountInfo().gaia, trusted_vault_keys);
+    fake_trusted_vault_client_->StoreKeys(
+        GetSyncService(0)->GetAccountInfo().gaia, trusted_vault_keys,
+        kTrustedVaultKeyVersion);
+  }
+
+  [[nodiscard]] bool SetupSyncAndPasskeyService() {
+    // Set up sync and enable password datatype.
+    if (!SetupClients()) {
+      LOG(ERROR) << "SetupClients() failed";
+      return false;
+    }
+    if (!GetClient(0)->SignInPrimaryAccount() ||
+        !GetClient(0)->AwaitSyncTransportActive()) {
+      LOG(ERROR) << "SignInPrimaryAccount() failed";
+      return false;
+    }
+    if (!GetClient(0)->SetupSync(base::BindLambdaForTesting(
+            [](syncer::SyncUserSettings* user_settings) {
+              user_settings->SetSelectedTypes(
+                  /*sync_everything=*/false,
+                  /*types=*/{syncer::UserSelectableType::kPasswords});
+            }))) {
+      LOG(ERROR) << "SetupSync() failed";
+      return false;
+    }
+
+    // Set up the passkey service.
+    // TODO(crbug.com/40187814): Use the real service instances here and point
+    // them to a `FakeSecurityDomainsServer`.
+    passkey_service_ = reinterpret_cast<PasskeyService*>(
+        PasskeyServiceFactory::GetInstance()->SetTestingFactoryAndUse(
+            GetProfile(0),
+            base::BindRepeating(
+                &ChromeOSPasskeyBrowserTest::CreatePasskeyService,
+                base::Unretained(this))));
+    test_observer_ = std::make_unique<TestObserver>();
+    return true;
+  }
+
+  std::unique_ptr<KeyedService> CreatePasskeyService(
+      content::BrowserContext* browser_context) {
+    CHECK(!passkey_service_) << "PasskeyServiceFactory invoked twice";
+    Profile* profile = Profile::FromBrowserContext(browser_context);
+    CHECK_EQ(profile, GetProfile(0));
+    scoped_in_session_auth_override_ =
+        std::make_unique<ScopedInSessionAuthOverride>();
+    trusted_vault_connection_holder_ =
+        std::make_unique<trusted_vault::MockTrustedVaultConnection>();
+    trusted_vault_connection_ = trusted_vault_connection_holder_.get();
+    fake_trusted_vault_client_ =
+        std::make_unique<trusted_vault::FakeTrustedVaultClient>(
+            /*auto_complete_requests=*/true);
+    return std::make_unique<PasskeyService>(
+        IdentityManagerFactory::GetForProfile(profile), GetSyncService(0),
+        fake_trusted_vault_client_.get(),
+        std::move(trusted_vault_connection_holder_));
+  }
+
+  void SetAuthFactorRegistrationState(
+      trusted_vault::DownloadAuthenticationFactorsRegistrationStateResult::State
+          state) {
+    ON_CALL(*trusted_vault_connection_,
+            DownloadAuthenticationFactorsRegistrationState(_, _))
+        .WillByDefault(
+            [state](
+                const CoreAccountInfo&,
+                base::OnceCallback<void(
+                    trusted_vault::
+                        DownloadAuthenticationFactorsRegistrationStateResult)>
+                    callback) {
+              trusted_vault::
+                  DownloadAuthenticationFactorsRegistrationStateResult result;
+              result.state = state;
+              base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
+                  FROM_HERE,
+                  base::BindOnce(std::move(callback), std::move(result)));
+              return std::make_unique<
+                  trusted_vault::TrustedVaultConnection::Request>();
+            });
+  }
+
+  net::EmbeddedTestServer https_server_{net::EmbeddedTestServer::TYPE_HTTPS};
+  std::unique_ptr<TestObserver> test_observer_;
+  std::unique_ptr<ScopedInSessionAuthOverride> scoped_in_session_auth_override_;
+  raw_ptr<PasskeyService> passkey_service_;
+  std::unique_ptr<trusted_vault::MockTrustedVaultConnection>
+      trusted_vault_connection_holder_;
+  raw_ptr<trusted_vault::MockTrustedVaultConnection> trusted_vault_connection_;
+  std::unique_ptr<trusted_vault::FakeTrustedVaultClient>
+      fake_trusted_vault_client_;
+
+  base::test::ScopedFeatureList scoped_feature_list_{device::kChromeOsPasskeys};
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+  device::FakeHidManager fake_hid_manager_;
+#endif
+  std::unique_ptr<device::BluetoothAdapterFactory::GlobalValuesForTesting>
+      bluetooth_values_for_testing_;
+};
+
+}  // namespace
+
+IN_PROC_BROWSER_TEST_F(ChromeOSPasskeyBrowserTest, GetAssertion_Success) {
+  ASSERT_TRUE(SetupSyncAndPasskeyService());
+  chrome::NewTab(GetBrowser(0));
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(
+      GetBrowser(0), https_server_.GetURL("www.example.com", "/title1.html")));
+
+  SetAuthFactorRegistrationState(
+      trusted_vault::DownloadAuthenticationFactorsRegistrationStateResult::
+          State::kRecoverable);
+  sync_pb::WebauthnCredentialSpecifics passkey = InjectTestPasskey();
+
+  content::WebContents* web_contents =
+      GetBrowser(0)->tab_strip_model()->GetActiveWebContents();
+  content::DOMMessageQueue message_queue(web_contents);
+  content::ExecuteScriptAsync(web_contents, kGetAssertionRequest);
+
+  test_observer().WaitForUI();
+
+  EXPECT_EQ(dialog_model().step(),
+            AuthenticatorRequestDialogModel::Step::kSelectPriorityMechanism);
+
+  EXPECT_EQ(request_delegate()
+                .chromeos_passkey_controller_for_testing()
+                .account_state_for_testing(),
+            PasskeyService::AccountState::kNeedsRecovery);
+
+  dialog_model().OnUserConfirmedPriorityMechanism();
+
+  test_observer().WaitForStep(
+      AuthenticatorRequestDialogController::Step::kRecoverSecurityDomain);
+  ASSERT_FALSE(test_observer().gpm_ready());
+  SimulateTrustedVaultRecovery();
+  test_observer().WaitForGPMReady();
+
+  std::string script_result;
+  ASSERT_TRUE(message_queue.WaitForMessage(&script_result));
+  EXPECT_EQ(script_result, "\"webauthn: OK\"");
+}
+
+}  // namespace chromeos
diff --git a/chrome/browser/webauthn/chromeos/passkey_dialog_controller.cc b/chrome/browser/webauthn/chromeos/passkey_dialog_controller.cc
index 9172c20..24003fc 100644
--- a/chrome/browser/webauthn/chromeos/passkey_dialog_controller.cc
+++ b/chrome/browser/webauthn/chromeos/passkey_dialog_controller.cc
@@ -64,6 +64,12 @@
   return account_state_.has_value();
 }
 
+PasskeyService::AccountState
+PasskeyDialogController::account_state_for_testing() const {
+  CHECK(account_state_.has_value());
+  return *account_state_;
+}
+
 void PasskeyDialogController::OnFetchAccountState(
     PasskeyService::AccountState state) {
   account_state_ = state;
diff --git a/chrome/browser/webauthn/chromeos/passkey_dialog_controller.h b/chrome/browser/webauthn/chromeos/passkey_dialog_controller.h
index ff04731..aa6c5bf 100644
--- a/chrome/browser/webauthn/chromeos/passkey_dialog_controller.h
+++ b/chrome/browser/webauthn/chromeos/passkey_dialog_controller.h
@@ -43,6 +43,8 @@
   const std::vector<sync_pb::WebauthnCredentialSpecifics>& credentials() const;
   bool ready_for_ui() const;
 
+  PasskeyService::AccountState account_state_for_testing() const;
+
  private:
   void OnFetchAccountState(PasskeyService::AccountState state);
   void StartAuthenticatorRequest();
diff --git a/chrome/browser/webauthn/enclave_authenticator_browsertest.cc b/chrome/browser/webauthn/enclave_authenticator_browsertest.cc
index d9e4392..ef6bb93 100644
--- a/chrome/browser/webauthn/enclave_authenticator_browsertest.cc
+++ b/chrome/browser/webauthn/enclave_authenticator_browsertest.cc
@@ -56,6 +56,7 @@
 #include "components/trusted_vault/proto/vault.pb.h"
 #include "components/trusted_vault/proto_string_bytes_conversion.h"
 #include "components/trusted_vault/securebox.h"
+#include "components/trusted_vault/test/mock_trusted_vault_connection.h"
 #include "components/trusted_vault/trusted_vault_connection.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
@@ -95,6 +96,8 @@
 
 namespace {
 
+using trusted_vault::MockTrustedVaultConnection;
+
 constexpr int32_t kSecretVersion = 417;
 constexpr uint8_t kSecurityDomainSecret[32] = {0};
 constexpr char kEmail[] = "test@gmail.com";
@@ -373,58 +376,6 @@
   base::ScopedTempDir dir_;
 };
 
-class MockTrustedVaultConnection
-    : public trusted_vault::TrustedVaultConnection {
- public:
-  MockTrustedVaultConnection() = default;
-  ~MockTrustedVaultConnection() override = default;
-  MOCK_METHOD(
-      std::unique_ptr<Request>,
-      RegisterAuthenticationFactor,
-      (const CoreAccountInfo& account_info,
-       const trusted_vault::MemberKeysSource& member_key_source,
-       const trusted_vault::SecureBoxPublicKey&
-           authentication_factor_public_key,
-       trusted_vault::AuthenticationFactorType authentication_factor_type,
-       base::OnceCallback<
-           void(const trusted_vault::TrustedVaultRegistrationStatus,
-                /*key_version=*/int)> callback),
-      (override));
-  MOCK_METHOD(std::unique_ptr<Request>,
-              RegisterDeviceWithoutKeys,
-              (const CoreAccountInfo& account_info,
-               const trusted_vault::SecureBoxPublicKey& device_public_key,
-               base::OnceCallback<
-                   void(const trusted_vault::TrustedVaultRegistrationStatus,
-                        /*key_version=*/int)> callback),
-              (override));
-  MOCK_METHOD(std::unique_ptr<Request>,
-              DownloadNewKeys,
-              (const CoreAccountInfo& account_info,
-               const trusted_vault::TrustedVaultKeyAndVersion&
-                   last_trusted_vault_key_and_version,
-               std::unique_ptr<trusted_vault::SecureBoxKeyPair> device_key_pair,
-               base::OnceCallback<
-                   void(trusted_vault::TrustedVaultDownloadKeysStatus,
-                        const std::vector<std::vector<uint8_t>>& /*keys*/,
-                        int /*last_key_version*/)> callback),
-              (override));
-  MOCK_METHOD(std::unique_ptr<Request>,
-              DownloadIsRecoverabilityDegraded,
-              (const CoreAccountInfo& account_info,
-               base::OnceCallback<
-                   void(trusted_vault::TrustedVaultRecoverabilityStatus)>),
-              (override));
-  MOCK_METHOD(
-      std::unique_ptr<Request>,
-      DownloadAuthenticationFactorsRegistrationState,
-      (const CoreAccountInfo& account_info,
-       base::OnceCallback<void(
-           trusted_vault::DownloadAuthenticationFactorsRegistrationStateResult)>
-           callback),
-      (override));
-};
-
 class EnclaveAuthenticatorBrowserTest : public SyncTest {
  public:
   class DelegateObserver
diff --git a/chrome/build/android-arm64.pgo.txt b/chrome/build/android-arm64.pgo.txt
index 2898ada..03dfddc 100644
--- a/chrome/build/android-arm64.pgo.txt
+++ b/chrome/build/android-arm64.pgo.txt
@@ -1 +1 @@
-chrome-android64-main-1716220190-2c2a5c796ffdc7b4a581384157167f4f7fab8cdf-678d8f10d39d33e2e735073ce1601c09c8a8bd15.profdata
+chrome-android64-main-1716299730-e75578151c3f0c3c6983e77f61604889b8a60265-3eb53510002da3e277a074c0daaace6e1afae508.profdata
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt
index 0f2d12d..07ff225 100644
--- a/chrome/build/mac-arm.pgo.txt
+++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@
-chrome-mac-arm-main-1716299730-309e40c5466859b28d969dfbec213e7e1d59d717-3eb53510002da3e277a074c0daaace6e1afae508.profdata
+chrome-mac-arm-main-1716314398-b3d933feb993baba3b9ae103b325818e9477f0e4-337f5100c5a83dd193b52a64debac0f4c5bd628c.profdata
diff --git a/chrome/build/win-arm64.pgo.txt b/chrome/build/win-arm64.pgo.txt
index ff29c57..dba805a4 100644
--- a/chrome/build/win-arm64.pgo.txt
+++ b/chrome/build/win-arm64.pgo.txt
@@ -1 +1 @@
-chrome-win-arm64-main-1716292223-6e6df880abec59e1aecc6d22f328df307ef2dba8-91b42d17cc4bc786672d0c231b96e61444b06fb7.profdata
+chrome-win-arm64-main-1716314388-7440d94ba6fdff69b56638d6b8f83e5189ab48c0-a00a44365853aadae1e7b2d4ecb02ebba67f43fb.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index 6a8daef4..870926a 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1716292223-21ba5692148dbad78aa02b546715b59e72aa7179-91b42d17cc4bc786672d0c231b96e61444b06fb7.profdata
+chrome-win32-main-1716303592-4752bd66507934963a86964cbe3407631ab742c6-f96abf2336241b3d628383cb4e152106ec8dcb07.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index c24e83e8..e8be995a 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1716292223-1856e618ee224d2756350457879b97711856c171-91b42d17cc4bc786672d0c231b96e61444b06fb7.profdata
+chrome-win64-main-1716303592-a1c1b96e951031be2488b4eae138d1e4a8116ab9-f96abf2336241b3d628383cb4e152106ec8dcb07.profdata
diff --git a/chrome/common/media/cdm_registration.cc b/chrome/common/media/cdm_registration.cc
index 5a1f94a..32eb7bb 100644
--- a/chrome/common/media/cdm_registration.cc
+++ b/chrome/common/media/cdm_registration.cc
@@ -46,6 +46,10 @@
 #include <gnu/libc-version.h>
 #endif  // defined(WIDEVINE_CDM_MIN_GLIBC_VERSION)
 #endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+#include "base/metrics/histogram_functions.h"
+#include "content/public/common/content_switches.h"
+#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
 #endif  // BUILDFLAG(ENABLE_WIDEVINE)
 
 #if BUILDFLAG(IS_ANDROID)
@@ -57,6 +61,29 @@
 using Robustness = content::CdmInfo::Robustness;
 
 #if BUILDFLAG(ENABLE_WIDEVINE)
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused.
+enum class LacrosBundledWidevine {
+  kNone = 0,
+  kLacros = 1,
+  kAsh = 2,
+  kMaxValue = kAsh,
+};
+
+// Record which Widevine CDM was loaded for Lacros.
+void ReportLacrosUMA(LacrosBundledWidevine value) {
+  // Only recorded by the browser process. If recorded by the pre-zygote
+  // process, then all processes will end up with this value reported.
+  const auto* command_line = base::CommandLine::ForCurrentProcess();
+  std::string process_type =
+      command_line->GetSwitchValueASCII(switches::kProcessType);
+  if (process_type.empty()) {
+    base::UmaHistogramEnumeration("Media.EME.Widevine.LacrosBundledCdm", value);
+  }
+}
+#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
+
 #if (BUILDFLAG(BUNDLE_WIDEVINE_CDM) ||            \
      BUILDFLAG(ENABLE_WIDEVINE_CDM_COMPONENT)) && \
     (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS))
@@ -135,6 +162,7 @@
   if (base::FeatureList::IsEnabled(media::kLacrosUseAshWidevine)) {
     const auto* command_line = base::CommandLine::ForCurrentProcess();
     if (command_line->HasSwitch(switches::kCrosWidevineBundledDir)) {
+      ReportLacrosUMA(LacrosBundledWidevine::kAsh);
       base::FilePath install_dir =
           command_line->GetSwitchValuePath(switches::kCrosWidevineBundledDir);
       return CreateCdmInfoFromWidevineDirectory(install_dir);
@@ -147,8 +175,10 @@
   // with Lacros if available.
   // TODO(b/332962687): Remove Lacros bundled Widevine CDM once all versions of
   // Ash updated to set the command line argument.
+  ReportLacrosUMA(LacrosBundledWidevine::kLacros);
   return GetBundledWidevine();
 #else
+  ReportLacrosUMA(LacrosBundledWidevine::kNone);
   return nullptr;
 #endif  // BUILDFLAG(BUNDLE_WIDEVINE_CDM)
 }
diff --git a/chrome/renderer/accessibility/ax_tree_distiller.cc b/chrome/renderer/accessibility/ax_tree_distiller.cc
index 2643633..1a29b93 100644
--- a/chrome/renderer/accessibility/ax_tree_distiller.cc
+++ b/chrome/renderer/accessibility/ax_tree_distiller.cc
@@ -17,8 +17,10 @@
 #include "services/metrics/public/cpp/ukm_builders.h"
 #include "third_party/blink/public/common/browser_interface_broker_proxy.h"
 #include "ui/accessibility/accessibility_features.h"
+#include "ui/accessibility/ax_computed_node_data.h"
 #include "ui/accessibility/ax_enums.mojom-shared.h"
 #include "ui/accessibility/ax_node.h"
+#include "ui/accessibility/ax_serializable_tree.h"
 #include "ui/accessibility/ax_tree.h"
 
 namespace {
@@ -46,11 +48,13 @@
 // which lie outside of the main and article node.
 // TODO(crbug.com/40802192): Replace this with a call to
 // OneShotAccessibilityTreeSearch.
-void GetContentRootNodes(const ui::AXNode* root,
+void GetContentRootNodes(const ui::AXTree& tree,
                          std::vector<const ui::AXNode*>* content_root_nodes) {
+  const ui::AXNode* root = tree.root();
   if (!root) {
     return;
   }
+
   std::queue<const ui::AXNode*> queue;
   queue.push(root);
   bool has_main_or_heading = false;
@@ -67,11 +71,30 @@
     }
     // If a heading node is found, add it to the list of content root nodes,
     // too. It may be removed later if the tree doesn't contain a main or
-    // article node.
+    // article node. Do not add it if it is offscreen.
     if (node->GetRole() == ax::mojom::Role::kHeading) {
+      bool offscreen = false;
+      tree.GetTreeBounds(node, &offscreen);
+      if (offscreen) {
+        continue;
+      }
       content_root_nodes->push_back(node);
       continue;
     }
+
+    // Add all nodes that can be expanded. Collapsed nodes will be removed
+    // later.
+    if (node->HasHtmlAttribute("aria-expanded")) {
+      content_root_nodes->push_back(node);
+      continue;
+    }
+
+    if (node->HasState(ax::mojom::State::kRichlyEditable)) {
+      content_root_nodes->push_back(node);
+      continue;
+    }
+
+    // Search through all children.
     for (auto iter = node->UnignoredChildrenBegin();
          iter != node->UnignoredChildrenEnd(); ++iter) {
       queue.push(iter.get());
@@ -100,8 +123,23 @@
     content_node_ids->emplace_back(node->id());
     return;
   }
-  if (base::Contains(kRolesToSkip, node->GetRole()))
+
+  if (node->HasState(ax::mojom::State::kRichlyEditable) &&
+      node->id() == node->tree()->data().focus_id) {
+    content_node_ids->push_back(node->id());
     return;
+  }
+
+  auto aria_expanded_state =
+      base::UTF16ToUTF8(node->GetHtmlAttribute("aria-expanded"));
+  if (aria_expanded_state == "true") {
+    content_node_ids->push_back(node->id());
+    return;
+  }
+
+  if (base::Contains(kRolesToSkip, node->GetRole())) {
+    return;
+  }
   for (auto iter = node->UnignoredChildrenBegin();
        iter != node->UnignoredChildrenEnd(); ++iter) {
     AddContentNodesToVector(iter.get(), content_node_ids);
@@ -153,7 +191,7 @@
     std::vector<ui::AXNodeID>* content_node_ids) {
   base::TimeTicks start_time = base::TimeTicks::Now();
   std::vector<const ui::AXNode*> content_root_nodes;
-  GetContentRootNodes(tree.root(), &content_root_nodes);
+  GetContentRootNodes(tree, &content_root_nodes);
   for (const ui::AXNode* content_root_node : content_root_nodes) {
     AddContentNodesToVector(content_root_node, content_node_ids);
   }
diff --git a/chrome/renderer/accessibility/ax_tree_distiller_browsertest.cc b/chrome/renderer/accessibility/ax_tree_distiller_browsertest.cc
index 9cea7c5..1ea1dfb 100644
--- a/chrome/renderer/accessibility/ax_tree_distiller_browsertest.cc
+++ b/chrome/renderer/accessibility/ax_tree_distiller_browsertest.cc
@@ -206,6 +206,35 @@
         <h1>Heading</h1>
       <body>)HTML",
      {}},
+    /* ----------------------- */
+    {"simple_page_heading_offscreen",
+     R"HTML(<!doctype html>
+      <body>
+        <h1 style="
+        position: absolute;
+        left: -10000px;
+        top: -10000px;
+        width: 1px;
+        height: 1px;"
+        >
+          Heading
+        </h1>
+        <main>
+          <p>Main</p>
+        </main>
+      <body>)HTML",
+     {"Main"}},
+    /* ----------------------- */
+    {"simple_page_aria_expanded",
+     R"HTML(<!doctype html>
+      <body>
+        <main>
+          <p>Main</p>
+          <div aria-expanded='true'>Expanded</div>
+          <div aria-expanded='false'>Collapsed</div>
+        </main>
+      <body>)HTML",
+     {"Main", "Expanded"}},
 };
 
 TEST_P(AXTreeDistillerTest, DistillsWebPage) {
diff --git a/chrome/renderer/accessibility/read_anything_app_controller.cc b/chrome/renderer/accessibility/read_anything_app_controller.cc
index 5e66258..5ffc8d9c 100644
--- a/chrome/renderer/accessibility/read_anything_app_controller.cc
+++ b/chrome/renderer/accessibility/read_anything_app_controller.cc
@@ -11,6 +11,7 @@
 #include <utility>
 #include <vector>
 
+#include "base/debug/stack_trace.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/metrics/metrics_hashes.h"
 #include "base/notreached.h"
@@ -38,6 +39,7 @@
 #include "ui/accessibility/accessibility_features.h"
 #include "ui/accessibility/ax_enums.mojom-shared.h"
 #include "ui/accessibility/ax_node.h"
+#include "ui/accessibility/ax_node_id_forward.h"
 #include "ui/accessibility/ax_role_properties.h"
 #include "ui/accessibility/ax_selection.h"
 #include "ui/accessibility/ax_serializable_tree.h"
@@ -61,6 +63,10 @@
 
 constexpr char kUndeterminedLocale[] = "und";
 
+// The number of seconds to wait before distilling after a user has stopped
+// entering text into a richly editable text field.
+const double kPostInputDistillSeconds = 1.5;
+
 // The following methods convert v8::Value types to an AXTreeUpdate. This is not
 // a complete conversion (thus way gin::Converter<ui::AXTreeUpdate> is not used
 // or implemented) but just converting the bare minimum data types needed for
@@ -379,7 +385,13 @@
 
 ReadAnythingAppController::ReadAnythingAppController(
     content::RenderFrame* render_frame)
-    : frame_token_(render_frame->GetWebFrame()->GetLocalFrameToken()) {
+    : frame_token_(render_frame->GetWebFrame()->GetLocalFrameToken()),
+      post_user_entry_draw_timer_(
+          FROM_HERE,
+          base::Seconds(kPostInputDistillSeconds),
+          base::BindRepeating(&ReadAnythingAppController::Draw,
+                              base::Unretained(this),
+                              /* recompute_display_nodes= */ true)) {
   renderer_load_triggered_time_ms_ = base::TimeTicks::Now();
   distiller_ = std::make_unique<AXTreeDistiller>(
       base::BindRepeating(&ReadAnythingAppController::OnAXTreeDistilled,
@@ -394,14 +406,47 @@
 ReadAnythingAppController::~ReadAnythingAppController() {
   RecordNumSelections();
   LogSpeechEventCounts();
+  // Stop the timer for base::unretained.
+  post_user_entry_draw_timer_.Stop();
+}
+
+void ReadAnythingAppController::OnNodeDataChanged(
+    ui::AXTree* tree,
+    const ui::AXNodeData& old_node_data,
+    const ui::AXNodeData& new_node_data) {
+  if (tree->GetAXTreeID() == model_.active_tree_id() &&
+      old_node_data.GetHtmlAttribute("aria-expanded") !=
+          new_node_data.GetHtmlAttribute("aria-expanded")) {
+    model_.set_last_expanded_node_id(new_node_data.id);
+  }
+}
+
+void ReadAnythingAppController::OnNodeWillBeDeleted(ui::AXTree* tree,
+                                                    ui::AXNode* node) {
+  ui::AXNodeID node_id = node->id();
+  if (model_.display_node_ids().contains(node_id)) {
+    displayed_nodes_pending_deletion_.insert(node_id);
+  }
+}
+
+void ReadAnythingAppController::OnNodeDeleted(ui::AXTree* tree,
+                                              ui::AXNodeID node_id) {
+  if (displayed_nodes_pending_deletion_.contains(node_id)) {
+    displayed_nodes_pending_deletion_.erase(node_id);
+    if (displayed_nodes_pending_deletion_.empty()) {
+      Draw(false);
+    }
+  }
 }
 
 void ReadAnythingAppController::ProcessAccessibilityUpdatesAndEvents(
     const ui::AXTreeID& tree_id,
     ui::AXUpdatesAndEvents& updates_and_events) {
-  // This updates the model, which may require us to start distillation based on
-  // the `requires_distillation()` state below.
-  //
+  // We will need to observe the tree which is added only after the model
+  // processes an accessibility event. So check to see if the tree exists or not
+  // yet.
+  bool had_tree = model_.ContainsTree(tree_id);
+
   // Remove the const-ness of the data here so that subsequent methods can move
   // the data.
   model_.ProcessAccessibilityUpdatesAndEvents(tree_id,
@@ -411,6 +456,13 @@
     return;
   }
 
+  // If the tree was added, start observing.
+  if (!had_tree && model_.ContainsTree(tree_id)) {
+    // Observe the tree.
+    ui::AXSerializableTree* tree = model_.GetTreeFromId(tree_id);
+    tree->AddObserver(this);
+  }
+
   if (model_.requires_distillation()) {
     Distill();
     if (model_.is_empty() && IsGoogleDocs() && model_.page_finished_loading()) {
@@ -418,6 +470,11 @@
     }
   }
 
+  if (model_.redraw_required()) {
+    model_.reset_redraw_required();
+    Draw(/* recompute_display_nodes= */ true);
+  }
+
   if (model_.image_to_update_node_id() != ui::kInvalidAXNodeID) {
     ExecuteJavaScript("chrome.readingMode.updateImage(" +
                       base::ToString(model_.image_to_update_node_id()) + ");");
@@ -429,6 +486,13 @@
   if (model_.requires_post_process_selection()) {
     PostProcessSelection();
   }
+
+  // If the user typed something, this value will be true and it will reset the
+  // timer to distill.
+  if (model_.reset_draw_timer()) {
+    post_user_entry_draw_timer_.Reset();
+    model_.set_reset_draw_timer(false);
+  }
 }
 
 void ReadAnythingAppController::ExecuteJavaScript(const std::string& script) {
@@ -449,6 +513,10 @@
     return;
   }
   RecordNumSelections();
+
+  // Cancel any running draw timers.
+  post_user_entry_draw_timer_.Stop();
+
   model_.set_active_tree_id(tree_id);
   model_.set_ukm_source_id(ukm_source_id);
   model_.set_is_pdf(is_pdf);
@@ -471,14 +539,16 @@
 }
 
 void ReadAnythingAppController::RecordNumSelections() {
-  ukm::builders::Accessibility_ReadAnything_EmptyState(
-      model_.ukm_source_id())
+  ukm::builders::Accessibility_ReadAnything_EmptyState(model_.ukm_source_id())
       .SetTotalNumSelections(model_.num_selections())
       .Record(ukm_recorder_.get());
   model_.set_num_selections(0);
 }
 
 void ReadAnythingAppController::OnAXTreeDestroyed(const ui::AXTreeID& tree_id) {
+  // Cancel any running draw timers.
+  post_user_entry_draw_timer_.Stop();
+
   model_.OnAXTreeDestroyed(tree_id);
 }
 
@@ -549,17 +619,13 @@
       !model_.ContainsTree(tree_id) || tree_id == ui::AXTreeIDUnknown()) {
     return;
   }
-  if (!model_.content_node_ids().empty()) {
-    // If there are content_node_ids, this means the AXTree was successfully
-    // distilled.
-    model_.ComputeDisplayNodeIdsForDistilledTree();
-  }
 
   // Draw the selection in the side panel (if one exists in the main panel).
   if (!PostProcessSelection()) {
     // If a draw did not occur, make sure to draw. This will happen if there is
     // no main content selection when the tree is distilled.
-    Draw();
+    bool should_recompute_display_nodes = !model_.content_node_ids().empty();
+    Draw(should_recompute_display_nodes);
   }
 
   if (model_.is_empty()) {
@@ -603,12 +669,14 @@
 
 bool ReadAnythingAppController::PostProcessSelection() {
   bool did_draw = false;
+  // Note post `model_.PostProcessSelection` returns true if a draw is required.
   if (model_.PostProcessSelection()) {
     did_draw = true;
     // TODO(b/40927698): When Read Aloud is playing and content is selected
     // in the main panel, don't re-draw with the updated selection until
     // Read Aloud is paused.
-    Draw();
+    bool should_recompute_display_nodes = !model_.content_node_ids().empty();
+    Draw(should_recompute_display_nodes);
   }
   // Skip drawing the selection in the side panel if the selection originally
   // came from there.
@@ -619,7 +687,10 @@
   return did_draw;
 }
 
-void ReadAnythingAppController::Draw() {
+void ReadAnythingAppController::Draw(bool recompute_display_nodes) {
+  if (recompute_display_nodes) {
+    model_.ComputeDisplayNodeIdsForDistilledTree();
+  }
   // This call should check that the active tree isn't in an undistilled state
   // -- that is, it is awaiting distillation or never requested distillation.
   ExecuteJavaScript("chrome.readingMode.updateContent();");
@@ -658,8 +729,7 @@
   bool needs_redraw_for_links = model_.links_enabled() != links_enabled;
   model_.OnSettingsRestoredFromPrefs(
       line_spacing, letter_spacing, font, font_size, links_enabled, color,
-      speech_rate, &voices,
-      &languages_enabled_in_pref, granularity);
+      speech_rate, &voices, &languages_enabled_in_pref, granularity);
   ExecuteJavaScript("chrome.readingMode.restoreSettingsFromPrefs();");
   // Only redraw if there is an active tree.
   if (needs_redraw_for_links &&
diff --git a/chrome/renderer/accessibility/read_anything_app_controller.h b/chrome/renderer/accessibility/read_anything_app_controller.h
index 593cc4da..ed9bbed 100644
--- a/chrome/renderer/accessibility/read_anything_app_controller.h
+++ b/chrome/renderer/accessibility/read_anything_app_controller.h
@@ -24,6 +24,7 @@
 #include "ui/accessibility/ax_node_id_forward.h"
 #include "ui/accessibility/ax_node_position.h"
 #include "ui/accessibility/ax_position.h"
+#include "ui/accessibility/ax_tree_observer.h"
 #include "ui/accessibility/ax_tree_update_forward.h"
 
 namespace content {
@@ -66,7 +67,8 @@
 //
 class ReadAnythingAppController
     : public gin::Wrappable<ReadAnythingAppController>,
-      public read_anything::mojom::UntrustedPage {
+      public read_anything::mojom::UntrustedPage,
+      public ui::AXTreeObserver {
  public:
   static gin::WrapperInfo kWrapperInfo;
 
@@ -88,6 +90,15 @@
   gin::ObjectTemplateBuilder GetObjectTemplateBuilder(
       v8::Isolate* isolate) override;
 
+  // ui::AXTreeObserver:
+  void OnNodeDataChanged(ui::AXTree* tree,
+                         const ui::AXNodeData& old_node_data,
+                         const ui::AXNodeData& new_node_data) override;
+
+  void OnNodeWillBeDeleted(ui::AXTree* tree, ui::AXNode* node) override;
+
+  void OnNodeDeleted(ui::AXTree* tree, ui::AXNodeID node) override;
+
   // read_anything::mojom::UntrustedPage:
   void ProcessAccessibilityUpdatesAndEvents(
       const ui::AXTreeID& tree_id,
@@ -221,7 +232,7 @@
       const std::string& display_locale) const;
 
   void Distill();
-  void Draw();
+  void Draw(bool recompute_display_nodes);
   void DrawSelection();
 
   void ExecuteJavaScript(const std::string& script);
@@ -341,6 +352,10 @@
   // Model that holds state for this controller.
   ReadAnythingAppModel model_;
 
+  // Set of nodes that will be deleted that are also displayed. A draw will
+  // occur when the set becomes empty.
+  std::set<ui::AXNodeID> displayed_nodes_pending_deletion_;
+
   // For metrics logging
 
   std::unique_ptr<ukm::MojoUkmRecorder> ukm_recorder_;
@@ -351,6 +366,10 @@
   // The time when the WebUI connects i.e. when onConnected is called.
   base::TimeTicks web_ui_connected_time_ms_;
 
+  // A timer that causes a distillation after a user stops typing for a set
+  // number of seconds.
+  base::RetainingOneShotTimer post_user_entry_draw_timer_;
+
   base::WeakPtrFactory<ReadAnythingAppController> weak_ptr_factory_{this};
 };
 
diff --git a/chrome/renderer/accessibility/read_anything_app_model.cc b/chrome/renderer/accessibility/read_anything_app_model.cc
index c5e70b9..57606844 100644
--- a/chrome/renderer/accessibility/read_anything_app_model.cc
+++ b/chrome/renderer/accessibility/read_anything_app_model.cc
@@ -339,6 +339,9 @@
     return;
   }
 
+  // Clear the map to store new expanded states.
+  aria_expanded_node_states_.clear();
+
   // Display nodes are the nodes which will be displayed by the rendering
   // algorithm of Read Anything app.ts. We wish to create a subtree which
   // stretches down from tree root to every content node and includes the
@@ -356,6 +359,21 @@
       continue;
     }
 
+    // Ignore aria-expanded for editables.
+    if (content_node->HasHtmlAttribute("aria-expanded") &&
+        !content_node->HasState(ax::mojom::State::kRichlyEditable)) {
+      // Capture the expanded state. ARIA expanded is not supported by all
+      // element types, but gmail for example uses it anyways. Check the
+      // attribute directly for that reason.
+      auto aria_expanded_state =
+          base::UTF16ToUTF8(content_node->GetHtmlAttribute("aria-expanded"));
+      aria_expanded_node_states_[content_node_id] = aria_expanded_state;
+      // Don't include collapsed aria-expanded items.
+      if (aria_expanded_state != "true") {
+        continue;
+      }
+    }
+
     // Add all ancestor ids, including the content node itself, which is the
     // first ancestor in the queue. Exit the loop early if an ancestor is
     // already in display_node_ids(); this means that all of the
@@ -542,6 +560,7 @@
         std::make_unique<ui::AXSerializableTree>();
     AddTree(tree_id, std::move(new_tree));
   }
+
   // If a tree update on the active tree is received while distillation is in
   // progress, cache updates that are received but do not yet unserialize them.
   // Drawing must be done on the same tree that was sent to the distiller,
@@ -866,7 +885,9 @@
       case ax::mojom::Event::kTooltipClosed:
       case ax::mojom::Event::kTooltipOpened:
       case ax::mojom::Event::kTreeChanged:
+        break;
       case ax::mojom::Event::kValueChanged:
+        reset_draw_timer_ = true;
         break;
       case ax::mojom::Event::kAriaAttributeChangedDeprecated:
       case ax::mojom::Event::kMenuListValueChangedDeprecated:
@@ -956,7 +977,16 @@
       case ui::AXEventGenerator::Event::MENU_POPUP_START:
       case ui::AXEventGenerator::Event::MULTILINE_STATE_CHANGED:
       case ui::AXEventGenerator::Event::MULTISELECTABLE_STATE_CHANGED:
+        break;
       case ui::AXEventGenerator::Event::NAME_CHANGED:
+        // TODO(francisjp): Determine if this logic should be specific to gmail.
+        if (last_expanded_node_id_ == event.node_id) {
+          ResetSelection();
+          requires_post_process_selection_ = false;
+          reset_last_expanded_node_id();
+          redraw_required_ = true;
+        }
+        break;
       case ui::AXEventGenerator::Event::OBJECT_ATTRIBUTE_CHANGED:
       case ui::AXEventGenerator::Event::ORIENTATION_CHANGED:
       case ui::AXEventGenerator::Event::PARENT_CHANGED:
diff --git a/chrome/renderer/accessibility/read_anything_app_model.h b/chrome/renderer/accessibility/read_anything_app_model.h
index 6fc58fb..e5573012 100644
--- a/chrome/renderer/accessibility/read_anything_app_model.h
+++ b/chrome/renderer/accessibility/read_anything_app_model.h
@@ -132,6 +132,22 @@
   void set_requires_post_process_selection(bool value) {
     requires_post_process_selection_ = value;
   }
+  bool reset_draw_timer() { return reset_draw_timer_; }
+  void set_reset_draw_timer(bool value) { reset_draw_timer_ = value; }
+
+  const ui::AXNodeID& last_expanded_node_id() { return last_expanded_node_id_; }
+
+  void set_last_expanded_node_id(const ui::AXNodeID& node_id) {
+    last_expanded_node_id_ = node_id;
+  }
+
+  void reset_last_expanded_node_id() {
+    set_last_expanded_node_id(ui::kInvalidAXNodeID);
+  }
+
+  bool redraw_required() { return redraw_required_; }
+  void reset_redraw_required() { redraw_required_ = false; }
+
   const ui::AXNodeID& image_to_update_node_id() {
     return image_to_update_node_id_;
   }
@@ -528,6 +544,12 @@
 
   // The current base language code used for fonts or reading aloud.
   std::string base_language_code_ = "en";
+  // The current language code used for fonts or reading aloud.
+  std::string language_code_ = "en-US";
+  std::map<ui::AXNodeID, std::string> aria_expanded_node_states_;
+
+  bool redraw_required_ = false;
+  ui::AXNodeID last_expanded_node_id_ = ui::kInvalidAXNodeID;
 
   // The default language code, used as a fallback in case base_language_code_
   // is invalid. It's not guaranteed that default_language_code_ will always
@@ -559,6 +581,7 @@
   int32_t start_offset_ = -1;
   int32_t end_offset_ = -1;
   bool requires_distillation_ = false;
+  bool reset_draw_timer_ = false;
   bool requires_post_process_selection_ = false;
   ui::AXNodeID image_to_update_node_id_ = ui::kInvalidAXNodeID;
   bool selection_from_action_ = false;
diff --git a/chrome/renderer/accessibility/read_anything_app_model_browsertest.cc b/chrome/renderer/accessibility/read_anything_app_model_browsertest.cc
index 52efb70d..c90e89c2 100644
--- a/chrome/renderer/accessibility/read_anything_app_model_browsertest.cc
+++ b/chrome/renderer/accessibility/read_anything_app_model_browsertest.cc
@@ -4,13 +4,17 @@
 
 #include "chrome/renderer/accessibility/read_anything_app_model.h"
 
+#include <vector>
+
 #include "base/memory/raw_ptr.h"
 #include "base/threading/platform_thread.h"
 #include "chrome/test/base/chrome_render_view_test.h"
 #include "services/strings/grit/services_strings.h"
 #include "ui/accessibility/ax_enums.mojom-shared.h"
 #include "ui/accessibility/ax_event.h"
+#include "ui/accessibility/ax_node_id_forward.h"
 #include "ui/accessibility/ax_serializable_tree.h"
+#include "ui/accessibility/ax_updates_and_events.h"
 #include "ui/base/l10n/l10n_util.h"
 
 class ReadAnythingAppModelTest : public ChromeRenderViewTest {
@@ -62,6 +66,12 @@
     model_->set_speech_playing(speech_playing);
   }
 
+  void SetLastExpandedNodeId(ui::AXNodeID id) {
+    model_->set_last_expanded_node_id(id);
+  }
+
+  ui::AXNodeID LastExpandedNodeId() { return model_->last_expanded_node_id(); }
+
   bool AreAllPendingUpdatesEmpty() {
     size_t count = 0;
     for (auto const& [tree_id, updates] :
@@ -116,6 +126,12 @@
                                                  std::move(updates_and_events));
   }
 
+  void ProcessAccessibilityUpdatesAndEvents(
+      ui::AXUpdatesAndEvents updates_and_events) {
+    model_->ProcessAccessibilityUpdatesAndEvents(updates_and_events.ax_tree_id,
+                                                 std::move(updates_and_events));
+  }
+
   void set_active_tree_id(ui::AXTreeID tree_id) {
     model_->set_active_tree_id(tree_id);
   }
@@ -189,6 +205,8 @@
     return base::Contains(model_->selection_node_ids(), ax_node_id);
   }
 
+  bool SelectionNodeIdsEmpty() { return model_->selection_node_ids().empty(); }
+
   void ProcessDisplayNodes(const std::vector<ui::AXNodeID>& content_node_ids) {
     Reset(content_node_ids);
     model_->ComputeDisplayNodeIdsForDistilledTree();
@@ -198,6 +216,10 @@
 
   bool RequiresDistillation() { return model_->requires_distillation(); }
 
+  bool RequiresRedraw() { return model_->redraw_required(); }
+
+  bool DrawTimerReset() { return model_->reset_draw_timer(); }
+
   bool RequiresPostProcessSelection() {
     return model_->requires_post_process_selection();
   }
@@ -786,6 +808,7 @@
 
   // Create a couple of updates which add additional nodes to the tree.
   std::vector<ui::AXTreeUpdate> updates;
+  std::vector<ui::AXEvent> events;
   std::vector<int> child_ids = {2, 3, 4};
   for (int i = 0; i < 2; i++) {
     int id = i + 5;
@@ -2162,3 +2185,56 @@
   EXPECT_EQ(GetWordLength(sentence.length()), 0);
   EXPECT_EQ(GetWordLength(sentence.length() + 1), 0);
 }
+
+TEST_F(ReadAnythingAppModelTest, LastExpandedNodeNamedChanged_TriggersRedraw) {
+  ui::AXTreeUpdate inital_update;
+  SetUpdateTreeID(&inital_update);
+  ui::AXNodeData inital_node;
+  inital_node.id = 2;
+  inital_node.role = ax::mojom::Role::kStaticText;
+  inital_node.SetNameChecked("Old Name");
+  inital_update.nodes = {inital_node};
+  ProcessAccessibilityUpdatesAndEvents({inital_update});
+
+  ui::AXTreeUpdate update;
+  SetUpdateTreeID(&update);
+  ui::AXNodeData updated_node;
+  updated_node.id = inital_node.id;
+  updated_node.role = ax::mojom::Role::kStaticText;
+  updated_node.SetNameChecked("New Name");
+  update.nodes = {updated_node};
+  SetLastExpandedNodeId(inital_node.id);
+  EXPECT_EQ(LastExpandedNodeId(), inital_node.id);
+  ProcessAccessibilityUpdatesAndEvents({update});
+
+  EXPECT_FALSE(RequiresPostProcessSelection());
+  EXPECT_TRUE(RequiresRedraw());
+  EXPECT_EQ(LastExpandedNodeId(), ui::kInvalidAXNodeID);
+  // Check selection reset.
+  EXPECT_FALSE(HasSelection());
+  EXPECT_EQ(StartOffset(), -1);
+  EXPECT_EQ(EndOffset(), -1);
+  EXPECT_EQ(StartNodeId(), ui::kInvalidAXNodeID);
+  EXPECT_EQ(EndNodeId(), ui::kInvalidAXNodeID);
+  EXPECT_TRUE(SelectionNodeIdsEmpty());
+}
+
+TEST_F(ReadAnythingAppModelTest, ContentEditableValueChanged_ResetsDrawTimer) {
+  ui::AXTreeUpdate update;
+  SetUpdateTreeID(&update);
+  ui::AXNodeData node1;
+  node1.id = 1;
+  update.nodes = {node1};
+
+  ui::AXEvent event;
+  event.id = node1.id;
+  event.event_type = ax::mojom::Event::kValueChanged;
+  // This update changes the structure of the tree. When the controller receives
+  // it in AccessibilityEventReceived, it will re-distill the tree.
+  ui::AXUpdatesAndEvents event_and_update;
+  event_and_update.events = {event};
+  event_and_update.updates = {update};
+  event_and_update.ax_tree_id = update.tree_data.tree_id;
+  ProcessAccessibilityUpdatesAndEvents(std::move(event_and_update));
+  EXPECT_TRUE(DrawTimerReset());
+}
diff --git a/chrome/renderer/autofill/password_autofill_agent_browsertest.cc b/chrome/renderer/autofill/password_autofill_agent_browsertest.cc
index 91f6c41..fcca36e 100644
--- a/chrome/renderer/autofill/password_autofill_agent_browsertest.cc
+++ b/chrome/renderer/autofill/password_autofill_agent_browsertest.cc
@@ -346,6 +346,10 @@
     "</form>"
     "</body>";
 
+constexpr std::string_view kUnownedFieldsWithPasswordDisabled =
+    "<input type='text' id='username'>"
+    "<input type='password' disabled id='password'>";
+
 // Sets the "readonly" attribute of `element` to the value given by `read_only`.
 void SetElementReadOnly(WebInputElement& element, bool read_only) {
   element.SetAttribute(WebString::FromUTF8("readonly"),
@@ -1611,42 +1615,135 @@
   CheckIfEventsAreCalled(event_checkers, true);
 }
 
-// Tests that `FillSuggestion` properly fills the username and password.
-TEST_F(PasswordAutofillAgentTest, FillSuggestion) {
+// Tests that `FillSuggestion` properly fills the username and password on
+// focused `username_element_`.
+TEST_F(PasswordAutofillAgentTest, FillSuggestionOnUsernameField) {
   // Simulate the browser sending the login info, but set `wait_for_username`
   // to prevent the form from being immediately filled.
   fill_data_.wait_for_username = true;
   SimulateOnFillPasswordForm(fill_data_);
 
-  for (const auto& selected_element : {username_element_, password_element_}) {
-    SimulateElementClick(selected_element);
-    // Neither field should be autocompleted.
-    CheckTextFieldsDOMState(std::string(), false, std::string(), false);
+  SimulateElementClick(username_element_);
 
-    // If the password field is not autocompletable, it should not be affected.
-    SetElementReadOnly(password_element_, true);
-    password_autofill_agent_->FillPasswordSuggestion(kAliceUsername16,
-                                                     kAlicePassword16);
-    CheckTextFieldsDOMState(std::string(), false, std::string(), false);
-    SetElementReadOnly(password_element_, false);
+  // Neither field should be autocompleted.
+  CheckTextFieldsDOMState(/*username=*/std::string(),
+                          /*username_autofilled=*/false,
+                          /*password=*/std::string(),
+                          /*password_autofilled=*/false);
 
-    // After filling with the suggestion, both fields should be autocompleted.
-    password_autofill_agent_->FillPasswordSuggestion(kAliceUsername16,
-                                                     kAlicePassword16);
-    CheckTextFieldsDOMState(kAliceUsername, true, kAlicePassword, true);
-    size_t username_length = strlen(kAliceUsername);
-    CheckUsernameSelection(username_length, username_length);
+  // If the username field is not autocompletable, no element will be filled.
+  SetElementReadOnly(username_element_, /*read_only=*/true);
+  password_autofill_agent_->FillPasswordSuggestion(kAliceUsername16,
+                                                   kAlicePassword16);
+  CheckTextFieldsDOMState(/*username=*/std::string(),
+                          /*username_autofilled=*/false,
+                          /*password=*/std::string(),
+                          /*password_autofilled=*/false);
+  SetElementReadOnly(username_element_, /*read_only=*/false);
 
-    // Try Filling with a suggestion with password different from the one that
-    // was initially sent to the renderer.
-    password_autofill_agent_->FillPasswordSuggestion(kBobUsername16,
-                                                     kCarolPassword16);
-    CheckTextFieldsDOMState(kBobUsername, true, kCarolPassword, true);
-    username_length = strlen(kBobUsername);
-    CheckUsernameSelection(username_length, username_length);
+  // If the password field is not autocompletable, only username will be filled.
+  SetElementReadOnly(password_element_, /*read_only=*/true);
+  password_autofill_agent_->FillPasswordSuggestion(kAliceUsername16,
+                                                   kAlicePassword16);
+  CheckTextFieldsDOMState(kAliceUsername, /*username_autofilled=*/true,
+                          /*password=*/std::string(),
+                          /*password_autofilled=*/false);
+  size_t username_length = strlen(kAliceUsername);
+  CheckUsernameSelection(username_length, username_length);
+  SetElementReadOnly(password_element_, /*read_only=*/false);
+  ResetFieldState(&username_element_);
 
-    ClearUsernameAndPasswordFieldValues();
-  }
+  // After filling with the suggestion, both fields should be autocompleted.
+  password_autofill_agent_->FillPasswordSuggestion(kAliceUsername16,
+                                                   kAlicePassword16);
+  CheckTextFieldsDOMState(kAliceUsername, /*username_autofilled=*/true,
+                          kAlicePassword, /*password_autofilled=*/true);
+  username_length = strlen(kAliceUsername);
+  CheckUsernameSelection(username_length, username_length);
+
+  // Try filling with a suggestion with password different from the one that
+  // was initially sent to the renderer.
+  password_autofill_agent_->FillPasswordSuggestion(kBobUsername16,
+                                                   kCarolPassword16);
+  CheckTextFieldsDOMState(kBobUsername, /*username_autofilled=*/true,
+                          kCarolPassword, /*password_autofilled=*/true);
+  username_length = strlen(kBobUsername);
+  CheckUsernameSelection(username_length, username_length);
+}
+
+// Avoid filling suggestion on username if the password field is disabled and
+// there is no <form> tag.
+TEST_F(PasswordAutofillAgentTest,
+       NoFillSuggestionOnNoFormTagAndPasswordDisabled) {
+  LoadHTML(kUnownedFieldsWithPasswordDisabled);
+  UpdateUsernameAndPasswordElements();
+
+  // Simulate the browser sending the login info, but set `wait_for_username`
+  // to prevent the form from being immediately filled.
+  fill_data_.wait_for_username = true;
+  SimulateOnFillPasswordForm(fill_data_);
+
+  SimulateElementClick(username_element_);
+
+  password_autofill_agent_->FillPasswordSuggestion(kAliceUsername16,
+                                                   kAlicePassword16);
+  // Neither field should be autocompleted.
+  CheckTextFieldsDOMState(
+      /*username=*/std::string(), /*username_autofilled=*/false,
+      /*password=*/std::string(), /*password_autofilled=*/false);
+}
+
+// Tests that `FillSuggestion` properly fills the username and password on
+// focused `password_element_`.
+TEST_F(PasswordAutofillAgentTest, FillSuggestionOnPasswordField) {
+  // Simulate the browser sending the login info, but set `wait_for_username`
+  // to prevent the form from being immediately filled.
+  fill_data_.wait_for_username = true;
+  SimulateOnFillPasswordForm(fill_data_);
+
+  SimulateElementClick(password_element_);
+
+  // Neither field should be autocompleted.
+  CheckTextFieldsDOMState(
+      /*username=*/std::string(), /*username_autofilled=*/false,
+      /*password=*/std::string(), /*password_autofilled=*/false);
+
+  // If the password field is not autocompletable, no filling will be made.
+  SetElementReadOnly(password_element_, /*read_only=*/true);
+  password_autofill_agent_->FillPasswordSuggestion(kAliceUsername16,
+                                                   kAlicePassword16);
+  CheckTextFieldsDOMState(
+      /*username=*/std::string(), /*username_autofilled=*/false,
+      /*password=*/std::string(), /*password_autofilled=*/false);
+  SetElementReadOnly(password_element_, /*read_only=*/false);
+
+  // If the username field is not autocompletable, only password field will be
+  // filled.
+  SetElementReadOnly(username_element_, /*read_only=*/true);
+  password_autofill_agent_->FillPasswordSuggestion(kAliceUsername16,
+                                                   kAlicePassword16);
+  CheckTextFieldsDOMState(/*username=*/std::string(),
+                          /*username_autofilled=*/false, kAlicePassword,
+                          /*password_autofilled=*/true);
+  SetElementReadOnly(username_element_, /*read_only=*/false);
+  ResetFieldState(&username_element_);
+
+  // After filling with the suggestion, both fields should be autocompleted.
+  password_autofill_agent_->FillPasswordSuggestion(kAliceUsername16,
+                                                   kAlicePassword16);
+  CheckTextFieldsDOMState(kAliceUsername, /*username_autofilled=*/true,
+                          kAlicePassword, /*password_autofilled=*/true);
+  size_t username_length = strlen(kAliceUsername);
+  CheckUsernameSelection(username_length, username_length);
+
+  // Try filling with a suggestion with password different from the one that
+  // was initially sent to the renderer.
+  password_autofill_agent_->FillPasswordSuggestion(kBobUsername16,
+                                                   kCarolPassword16);
+  CheckTextFieldsDOMState(kBobUsername, /*username_autofilled=*/true,
+                          kCarolPassword, /*password_autofilled=*/true);
+  username_length = strlen(kBobUsername);
+  CheckUsernameSelection(username_length, username_length);
 }
 
 // Tests that `FillSuggestion` properly fills the username and password when the
@@ -1711,69 +1808,114 @@
   EXPECT_EQ(2, fake_driver_.called_inform_about_user_input_count());
 }
 
-// Tests that `FillSuggestion` properly fills the password if the username field
-// is read-only.
-TEST_F(PasswordAutofillAgentTest, FillSuggestionIfUsernameReadOnly) {
-  // Simulate the browser sending the login info.
-  SetElementReadOnly(username_element_, true);
-  SimulateOnFillPasswordForm(fill_data_);
-
-  SimulateElementClick(password_element_);
-  // Neither field should be autocompleted.
-  CheckTextFieldsDOMState(std::string(), false, std::string(), false);
-
-  // Username field is not autocompletable, it should not be affected.
-  password_autofill_agent_->FillPasswordSuggestion(kAliceUsername16,
-                                                   kAlicePassword16);
-  CheckTextFieldsDOMState(std::string(), false, kAlicePassword, true);
-
-  // Try Filling with a suggestion with password different from the one that
-  // was initially sent to the renderer.
-  password_autofill_agent_->FillPasswordSuggestion(kBobUsername16,
-                                                   kCarolPassword16);
-  CheckTextFieldsDOMState(std::string(), false, kCarolPassword, true);
-
-  ClearUsernameAndPasswordFieldValues();
-}
-
-// Tests that `PreviewSuggestion` properly previews the username and password.
-TEST_F(PasswordAutofillAgentTest, PreviewSuggestion) {
+// Tests that `PreviewSuggestion` properly previews the username and password on
+// `username_element_` focus.
+TEST_F(PasswordAutofillAgentTest, PreviewSuggestionOnUsernameField) {
   // Simulate the browser sending the login info, but set `wait_for_username` to
   // prevent the form from being immediately filled.
   fill_data_.wait_for_username = true;
   SimulateOnFillPasswordForm(fill_data_);
 
-  for (const auto& selected_element : {username_element_, password_element_}) {
-    // Neither field should be autocompleted.
-    CheckTextFieldsDOMState(std::string(), false, std::string(), false);
+  // Neither field should be autocompleted.
+  CheckTextFieldsDOMState(
+      /*username=*/std::string(), /*username_autofilled=*/false,
+      /*password=*/std::string(), /*password_autofilled=*/false);
 
-    // If the password field is not autocompletable, it should not be affected.
-    SetElementReadOnly(password_element_, true);
-    password_autofill_agent_->PreviewSuggestion(
-        selected_element, kAliceUsername16, kAlicePassword16);
-    CheckTextFieldsSuggestedState(std::string(), false, std::string(), false);
-    SetElementReadOnly(password_element_, false);
+  // If the password field is not autocompletable, the preview must be available
+  // only on username.
+  SetElementReadOnly(password_element_, /*read_only=*/true);
+  password_autofill_agent_->PreviewSuggestion(
+      username_element_, kAliceUsername16, kAlicePassword16);
+  CheckTextFieldsSuggestedState(kAliceUsername, /*username_autofilled=*/true,
+                                /*password=*/std::string(),
+                                /*password_autofilled=*/false);
+  SetElementReadOnly(password_element_, /*read_only=*/false);
+  password_autofill_agent_->ClearPreviewedForm();
 
-    // After selecting the suggestion, both fields should be previewed with
-    // suggested values.
-    password_autofill_agent_->PreviewSuggestion(
-        selected_element, kAliceUsername16, kAlicePassword16);
-    CheckTextFieldsSuggestedState(kAliceUsername, true, kAlicePassword, true);
-    // Since the suggestion is previewed as a placeholder, there should be no
-    // selected text.
-    CheckUsernameSelection(0, 0);
+  // If the username field is not autocompletable, the preview must not be shown
+  // on any field.
+  SetElementReadOnly(username_element_, /*read_only=*/true);
+  password_autofill_agent_->PreviewSuggestion(
+      username_element_, kAliceUsername16, kAlicePassword16);
+  CheckTextFieldsSuggestedState(
+      /*username=*/std::string(), /*username_autofilled=*/false,
+      /*password=*/std::string(), /*password_autofilled=*/false);
+  SetElementReadOnly(username_element_, /*read_only=*/false);
 
-    // Try previewing with a password different from the one that was initially
-    // sent to the renderer.
-    password_autofill_agent_->PreviewSuggestion(
-        selected_element, kBobUsername16, kCarolPassword16);
-    CheckTextFieldsSuggestedState(kBobUsername, true, kCarolPassword, true);
-    // Since the suggestion is previewed as a placeholder, there should be no
-    // selected text.
-    CheckUsernameSelection(0, 0);
+  // After selecting the preview, both fields should be previewed with
+  // suggested values.
+  password_autofill_agent_->PreviewSuggestion(
+      username_element_, kAliceUsername16, kAlicePassword16);
+  CheckTextFieldsSuggestedState(kAliceUsername, /*username_autofilled=*/true,
+                                kAlicePassword, /*password_autofilled=*/true);
+  // Since the suggestion is previewed as a placeholder, there should be no
+  // selected text.
+  CheckUsernameSelection(/*start=*/0, /*end=*/0);
 
-    ClearUsernameAndPasswordFieldValues();
-  }
+  // Try previewing with a password different from the one that was initially
+  // sent to the renderer.
+  password_autofill_agent_->PreviewSuggestion(username_element_, kBobUsername16,
+                                              kCarolPassword16);
+  CheckTextFieldsSuggestedState(kBobUsername, /*username_autofilled=*/true,
+                                kCarolPassword, /*password_autofilled=*/true);
+  // Since the suggestion is previewed as a placeholder, there should be no
+  // selected text.
+  CheckUsernameSelection(/*start=*/0, /*end=*/0);
+}
+
+// Tests that `PreviewSuggestion` properly previews the username and password on
+// `password_element_` focus.
+TEST_F(PasswordAutofillAgentTest, PreviewSuggestionOnPasswordField) {
+  // Simulate the browser sending the login info, but set `wait_for_username` to
+  // prevent the form from being immediately filled.
+  fill_data_.wait_for_username = true;
+  SimulateOnFillPasswordForm(fill_data_);
+
+  // Neither field should be autocompleted.
+  CheckTextFieldsDOMState(/*username=*/std::string(),
+                          /*username_autofilled=*/false,
+                          /*password=*/std::string(),
+                          /*password_autofilled=*/false);
+
+  // If the password field is not autocompletable, there must be no preview
+  // available.
+  SetElementReadOnly(password_element_, /*read_only=*/true);
+  password_autofill_agent_->PreviewSuggestion(
+      password_element_, kAliceUsername16, kAlicePassword16);
+  CheckTextFieldsSuggestedState(
+      /*username=*/std::string(), /*username_autofilled=*/false,
+      /*password=*/std::string(), /*password_autofilled=*/false);
+  SetElementReadOnly(password_element_, /*read_only=*/false);
+
+  // If the username field is not autocompletable, the preview must be shown
+  // only on the password field.
+  SetElementReadOnly(username_element_, /*read_only=*/true);
+  password_autofill_agent_->PreviewSuggestion(
+      password_element_, kAliceUsername16, kAlicePassword16);
+  CheckTextFieldsSuggestedState(/*username=*/std::string(),
+                                /*username_autofilled=*/false, kAlicePassword,
+                                /*password_autofilled=*/true);
+  SetElementReadOnly(username_element_, /*read_only=*/false);
+
+  // After previewing the suggestion, both fields should be previewed with
+  // suggested values.
+  password_autofill_agent_->PreviewSuggestion(
+      password_element_, kAliceUsername16, kAlicePassword16);
+  CheckTextFieldsSuggestedState(kAliceUsername, /*username_autofilled=*/true,
+                                kAlicePassword, /*password_autofilled=*/true);
+  // Since the suggestion is previewed as a placeholder, there should be no
+  // selected text.
+  CheckUsernameSelection(/*start=*/0, /*end=*/0);
+
+  // Try previewing with a password different from the one that was initially
+  // sent to the renderer.
+  password_autofill_agent_->PreviewSuggestion(password_element_, kBobUsername16,
+                                              kCarolPassword16);
+  CheckTextFieldsSuggestedState(kBobUsername, /*username_autofilled=*/true,
+                                kCarolPassword, /*password_autofilled=*/true);
+  // Since the suggestion is previewed as a placeholder, there should be no
+  // selected text.
+  CheckUsernameSelection(/*start=*/0, /*end=*/0);
 }
 
 // Tests that `PreviewSuggestion` doesn't change non-empty non-autofilled
@@ -1802,33 +1944,6 @@
   CheckTextFieldsDOMState("user1", false, std::string(), true);
 }
 
-// Tests that `PreviewSuggestion` properly previews the password if username is
-// read-only.
-TEST_F(PasswordAutofillAgentTest, PreviewSuggestionIfUsernameReadOnly) {
-  // Simulate the browser sending the login info.
-  SetElementReadOnly(username_element_, true);
-  SimulateOnFillPasswordForm(fill_data_);
-
-  for (const auto& selected_element : {username_element_, password_element_}) {
-    // Neither field should be autocompleted.
-    CheckTextFieldsDOMState(std::string(), false, std::string(), false);
-
-    // Username field is not autocompletable, it should not be affected.
-    password_autofill_agent_->PreviewSuggestion(
-        selected_element, kAliceUsername16, kAlicePassword16);
-    // Password field must be autofilled.
-    CheckTextFieldsSuggestedState(std::string(), false, kAlicePassword, true);
-
-    // Try previewing with a password different from the one that was initially
-    // sent to the renderer.
-    password_autofill_agent_->PreviewSuggestion(
-        selected_element, kBobUsername16, kCarolPassword16);
-    CheckTextFieldsSuggestedState(std::string(), false, kCarolPassword, true);
-
-    ClearUsernameAndPasswordFieldValues();
-  }
-}
-
 // Tests that `PreviewSuggestion` properly sets the username selection range.
 TEST_F(PasswordAutofillAgentTest, PreviewSuggestionSelectionRange) {
   // Simulate the browser sending the login info, but set `wait_for_username`
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index f4e0850..17e905b 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -5610,6 +5610,11 @@
       sources += [ "../browser/webauthn/enclave_authenticator_browsertest.cc" ]
       deps += [ "//components/os_crypt/sync:test_support" ]
     }
+
+    if (is_chromeos) {
+      sources += [ "../browser/webauthn/chromeos/passkey_browsertest.cc" ]
+      deps += [ "//chrome/browser/webauthn:test_support" ]
+    }
   }
 }
 
@@ -7154,6 +7159,7 @@
     "//components/vector_icons",
     "//components/version_info:generate_version_info",
     "//components/visited_url_ranking/public",
+    "//components/visited_url_ranking/public:test_support",
     "//components/webapps/common",
     "//components/webauthn/core/browser",
     "//components/webauthn/core/browser:passkey_model",
@@ -8742,6 +8748,7 @@
       "../browser/ui/ash/wallpaper_controller_client_impl_unittest.cc",
       "../browser/ui/ash/window_pin_util_ash_unittest.cc",
       "../browser/ui/chromeos/magic_boost/magic_boost_controller_unittest.cc",
+      "../browser/ui/chromeos/magic_boost/magic_boost_opt_in_card_unittest.cc",
       "../browser/ui/chromeos/read_write_cards/read_write_cards_ui_controller_unittest.cc",
       "../browser/ui/quick_answers/quick_answers_controller_unittest.cc",
       "../browser/ui/quick_answers/quick_answers_state_ash_unittest.cc",
diff --git a/chrome/test/data/extensions/api_test/login_screen_apis/in_session_extension.crx b/chrome/test/data/extensions/api_test/login_screen_apis/in_session_extension.crx
index 1c0b2e0..07034f6 100644
--- a/chrome/test/data/extensions/api_test/login_screen_apis/in_session_extension.crx
+++ b/chrome/test/data/extensions/api_test/login_screen_apis/in_session_extension.crx
Binary files differ
diff --git a/chrome/test/data/extensions/api_test/login_screen_apis/in_session_extension/background.js b/chrome/test/data/extensions/api_test/login_screen_apis/in_session_extension/background.js
index 659f9f8..b2b4352 100644
--- a/chrome/test/data/extensions/api_test/login_screen_apis/in_session_extension/background.js
+++ b/chrome/test/data/extensions/api_test/login_screen_apis/in_session_extension/background.js
@@ -20,11 +20,9 @@
     'The extension does not have permission to unlock this session';
 
 const tests = {
-  'InSessionLoginLockManagedGuestSession': () => {
-    chrome.login.lockManagedGuestSession(() => {
-      chrome.test.assertNoLastError();
-      chrome.test.succeed();
-    });
+  'InSessionLoginLockManagedGuestSession': async () => {
+    await chrome.login.lockManagedGuestSession();
+    chrome.test.succeed();
   },
   'InSessionLoginLockManagedGuestSessionNoPermission': () => {
     chrome.login.lockManagedGuestSession(() => {
@@ -40,7 +38,6 @@
   },
   'InSessionLoginNotifyExternalLogoutDone': () => {
     chrome.login.notifyExternalLogoutDone();
-    chrome.test.assertNoLastError();
     chrome.test.succeed();
   },
   'InSessionLoginOnRequestExternalLogout': () => {
@@ -50,38 +47,30 @@
     });
     chrome.test.sendMessage('onRequestExternalLogoutInSessionMessage');
   },
-  'InSessionLoginScreenStorageStorePersistentData': () => {
-    chrome.loginScreenStorage.storePersistentData(
-        [extensionId1, extensionId2], data, () => {
-          chrome.test.assertNoLastError();
-          chrome.test.succeed();
-        });
+  'InSessionLoginScreenStorageStorePersistentData': async () => {
+    await chrome.loginScreenStorage.storePersistentData(
+        [extensionId1, extensionId2], data);
+    chrome.test.succeed();
   },
-  'InSessionLoginScreenStorageRetrievePersistentData': () => {
-    chrome.loginScreenStorage.retrievePersistentData(extensionId, data => {
-      chrome.test.assertNoLastError();
-      chrome.test.assertEq(loginScreenStorageResult, data);
-      chrome.test.succeed();
-    });
+  'InSessionLoginScreenStorageRetrievePersistentData': async () => {
+    const data =
+        await chrome.loginScreenStorage.retrievePersistentData(extensionId);
+    chrome.test.assertEq(loginScreenStorageResult, data);
+    chrome.test.succeed();
   },
-  'InSessionLoginScreenStorageStoreCredentials': () => {
-    chrome.loginScreenStorage.storeCredentials(
-        extensionId, credentials, () => {
-          chrome.test.assertNoLastError();
-          chrome.test.succeed();
-        });
+  'InSessionLoginScreenStorageStoreCredentials': async () => {
+    await chrome.loginScreenStorage.storeCredentials(extensionId, credentials);
+    chrome.test.succeed();
   },
-  'InSessionLoginScreenStorageRetrieveCredentials': () => {
-    chrome.loginScreenStorage.retrieveCredentials(credentials => {
-      chrome.test.assertNoLastError();
-      chrome.test.assertEq(loginScreenStorageResult, credentials);
-      chrome.test.succeed();
-    });
+  'InSessionLoginScreenStorageRetrieveCredentials': async () => {
+    const credentials = await chrome.loginScreenStorage.retrieveCredentials();
+    chrome.test.assertEq(loginScreenStorageResult, credentials);
+    chrome.test.succeed();
   }
-}
+};
 
-// |waitForTestName()| waits for the browser test to reply with a test name and
-// runs the specified test. The browser test logic can be found at
+// |waitForTestName()| waits for the browser test to reply with a test name
+// and runs the specified test. The browser test logic can be found at
 // chrome/browser/chromeos/extensions/login_screen/login_screen_apitest_base.cc
 function waitForTestName(testName) {
   if (!tests.hasOwnProperty(testName) ||
diff --git a/chrome/test/data/extensions/api_test/login_screen_apis/in_session_extension/manifest.json b/chrome/test/data/extensions/api_test/login_screen_apis/in_session_extension/manifest.json
index d0a83910..81d858e 100644
--- a/chrome/test/data/extensions/api_test/login_screen_apis/in_session_extension/manifest.json
+++ b/chrome/test/data/extensions/api_test/login_screen_apis/in_session_extension/manifest.json
@@ -1,10 +1,10 @@
 {
   "name": "Login screen APIs in-session test extension",
-  "version": "0.7",
-  "manifest_version": 2,
+  "version": "0.8",
+  "manifest_version": 3,
   "description": "In session extension for testing the chrome.login extension API",
   "background": {
-    "scripts": ["background.js"]
+    "service_worker": "background.js"
   },
   "permissions": [
     "login",
diff --git a/chrome/test/data/extensions/api_test/login_screen_apis/login_state/get_profile_type/manifest.json b/chrome/test/data/extensions/api_test/login_screen_apis/login_state/get_profile_type/manifest.json
index bdd9ebb..fd0b1fb 100644
--- a/chrome/test/data/extensions/api_test/login_screen_apis/login_state/get_profile_type/manifest.json
+++ b/chrome/test/data/extensions/api_test/login_screen_apis/login_state/get_profile_type/manifest.json
@@ -1,10 +1,10 @@
 {
   "name": "Get Profile Type",
-  "version": "0.1",
-  "manifest_version": 2,
+  "version": "2.0",
+  "manifest_version": 3,
   "description": "Browser test for chrome.loginState.getProfileType()",
   "background": {
-    "scripts": ["test.js"]
+    "service_worker": "test.js"
   },
   "permissions": [
     "loginState"
diff --git a/chrome/test/data/extensions/api_test/login_screen_apis/login_state/get_session_state/manifest.json b/chrome/test/data/extensions/api_test/login_screen_apis/login_state/get_session_state/manifest.json
index 870f100..93350cce 100644
--- a/chrome/test/data/extensions/api_test/login_screen_apis/login_state/get_session_state/manifest.json
+++ b/chrome/test/data/extensions/api_test/login_screen_apis/login_state/get_session_state/manifest.json
@@ -1,10 +1,10 @@
 {
   "name": "Get Session State",
-  "version": "1.0",
-  "manifest_version": 2,
+  "version": "2.0",
+  "manifest_version": 3,
   "description": "Browser test for chrome.loginState.getSessionState()",
   "background": {
-    "scripts": ["test.js"]
+    "service_worker": "test.js"
   },
   "permissions": [
     "loginState"
diff --git a/chrome/test/data/extensions/api_test/login_screen_apis/login_state/on_session_state_changed/manifest.json b/chrome/test/data/extensions/api_test/login_screen_apis/login_state/on_session_state_changed/manifest.json
index f299e1a9..b8ebe600 100644
--- a/chrome/test/data/extensions/api_test/login_screen_apis/login_state/on_session_state_changed/manifest.json
+++ b/chrome/test/data/extensions/api_test/login_screen_apis/login_state/on_session_state_changed/manifest.json
@@ -1,10 +1,10 @@
 {
   "name": "On Session State Changed",
-  "version": "0.1",
-  "manifest_version": 2,
+  "version": "2.0",
+  "manifest_version": 3,
   "description": "Browser test for chrome.loginState.onSessionStateChanged()",
   "background": {
-    "scripts": ["test.js"]
+    "service_worker": "test.js"
   },
   "permissions": [
     "loginState"
diff --git a/chrome/test/data/extensions/api_test/login_screen_apis/update_manifest.xml b/chrome/test/data/extensions/api_test/login_screen_apis/update_manifest.xml
index 8d97bb98..a4279c0 100644
--- a/chrome/test/data/extensions/api_test/login_screen_apis/update_manifest.xml
+++ b/chrome/test/data/extensions/api_test/login_screen_apis/update_manifest.xml
@@ -13,6 +13,6 @@
   <app appid='ofcpkomnogjenhfajfjadjmjppbegnad'>
     <updatecheck
         codebase='http://mock.http/extensions/api_test/login_screen_apis/in_session_extension.crx'
-        version='0.7' />
+        version='0.8' />
   </app>
 </gupdate>
diff --git a/chrome/test/data/webui/commerce/product_specifications/app_test.ts b/chrome/test/data/webui/commerce/product_specifications/app_test.ts
index 0d856d6..177c918 100644
--- a/chrome/test/data/webui/commerce/product_specifications/app_test.ts
+++ b/chrome/test/data/webui/commerce/product_specifications/app_test.ts
@@ -387,6 +387,32 @@
         uuid, shoppingServiceApi.getArgs('deleteProductSpecificationsSet')[1]);
   });
 
+  test('renames product specification set', async () => {
+    const urlsParam = ['https://example.com/'];
+    const promiseValues = createAppPromiseValues(
+        {urlsParam: urlsParam, specsSet: createSpecsSet()});
+    await createAppElementWithPromiseValues(promiseValues);
+
+    const uuid =
+        shoppingServiceApi.getArgs('addProductSpecificationsSet')[0][2];
+    const header =
+        appElement.shadowRoot!.querySelector('product-specifications-header');
+    assertTrue(!!header);
+    const newName = 'new name';
+    header.dispatchEvent(
+        new CustomEvent('name-change', {detail: {name: newName}}));
+
+    assertEquals(
+        1,
+        shoppingServiceApi.getCallCount('setNameForProductSpecificationsSet'));
+    assertEquals(
+        uuid,
+        shoppingServiceApi.getArgs('setNameForProductSpecificationsSet')[1]);
+    assertEquals(
+        newName,
+        shoppingServiceApi.getArgs('setNameForProductSpecificationsSet')[0][1]);
+  });
+
   suite('Header', () => {
     test('displays correct subtitle for retrieved sets', async () => {
       const specsSet = createSpecsSet({
diff --git a/chrome/test/data/webui/extensions/mv2_deprecation_panel_test.ts b/chrome/test/data/webui/extensions/mv2_deprecation_panel_test.ts
index 0c06958..b1a332f 100644
--- a/chrome/test/data/webui/extensions/mv2_deprecation_panel_test.ts
+++ b/chrome/test/data/webui/extensions/mv2_deprecation_panel_test.ts
@@ -35,6 +35,20 @@
     return flushTasks();
   });
 
+  /**
+   * Returns the only extension in the panel. Will fail if there are no
+   * extensions, or more than one.
+   */
+  function getExtension(): Element {
+    const extensionRows =
+        panelElement.shadowRoot!.querySelectorAll('.panel-extension-row');
+    assertEquals(1, extensionRows.length);
+    const extension = extensionRows[0];
+    assertTrue(!!extension);
+    return extension;
+  }
+
+
   test('header content is always visible', function() {
     assertTrue(isVisible(
         panelElement.shadowRoot!.querySelector('.panel-header-text')));
@@ -46,12 +60,11 @@
     // Verify there is one extension row for the extension added at setup.
     let extensionRows =
         panelElement.shadowRoot!.querySelectorAll('.panel-extension-row');
-    assertEquals(extensionRows.length, 1);
-    assertEquals(
-        extensionRows[0]
-            ?.querySelector('.panel-extension-info')
-            ?.textContent?.trim(),
-        'Extension A');
+    assertEquals(1, extensionRows.length);
+    let infoA =
+        extensionRows[0]!.querySelector<HTMLElement>('.panel-extension-info');
+    assertTrue(!!infoA);
+    assertEquals('Extension A', infoA.textContent!.trim());
 
     // Add a new extension to the panel.
     panelElement.push('extensions', createExtensionInfo({
@@ -64,16 +77,14 @@
     extensionRows =
         panelElement.shadowRoot!.querySelectorAll('.panel-extension-row');
     assertEquals(extensionRows.length, 2);
-    assertEquals(
-        extensionRows[0]
-            ?.querySelector('.panel-extension-info')
-            ?.textContent?.trim(),
-        'Extension A');
-    assertEquals(
-        extensionRows[1]
-            ?.querySelector('.panel-extension-info')
-            ?.textContent?.trim(),
-        'Extension B');
+    infoA =
+        extensionRows[0]!.querySelector<HTMLElement>('.panel-extension-info');
+    assertTrue(!!infoA);
+    assertEquals('Extension A', infoA.textContent!.trim());
+    const infoB =
+        extensionRows[1]!.querySelector<HTMLElement>('.panel-extension-info');
+    assertTrue(!!infoB);
+    assertEquals('Extension B', infoB.textContent!.trim());
   });
 
   test(
@@ -94,14 +105,9 @@
       'find alternative button is visible if extension has recommendations' +
           'url, and opens url when clicked',
       async function() {
-        let extension =
-            panelElement.shadowRoot!
-                .querySelectorAll<HTMLElement>('.panel-extension-row')
-                ?.[0];
-        assertTrue(!!extension);
-
         // Find alternative button is hidden when the extension doesn't have a
         // recommendations url.
+        let extension = getExtension();
         let findAlternativeButton = extension.querySelector<CrButtonElement>(
             '.find-alternative-button');
         assertFalse(isVisible(findAlternativeButton));
@@ -119,13 +125,9 @@
                          }));
         await flushTasks();
 
-        extension = panelElement.shadowRoot!
-                        .querySelectorAll<HTMLElement>('.panel-extension-row')
-                        ?.[0];
-        assertTrue(!!extension);
-
         // Find alternative button is visible when the extension has a
         // recommendations url.
+        extension = getExtension();
         findAlternativeButton = extension.querySelector<CrButtonElement>(
             '.find-alternative-button');
         assertTrue(isVisible(findAlternativeButton));
@@ -142,13 +144,8 @@
       'remove action is visible if extension can be removed, and triggers' +
           'the extension removal when clicked',
       async function() {
-        let extension =
-            panelElement.shadowRoot!
-                .querySelectorAll<HTMLElement>('.panel-extension-row')
-                ?.[0];
-        assertTrue(!!extension);
-
         // Open the extension's action menu button.
+        let extension = getExtension();
         let actionButton =
             extension.querySelector<CrIconButtonElement>('cr-icon-button');
         assertTrue(!!actionButton);
@@ -180,10 +177,7 @@
 
         // Open the extension's action menu button again, since clicking on the
         // action closed the menu.
-        extension = panelElement.shadowRoot!
-                        .querySelectorAll<HTMLElement>('.panel-extension-row')
-                        ?.[0];
-        assertTrue(!!extension);
+        extension = getExtension();
         actionButton =
             extension.querySelector<CrIconButtonElement>('cr-icon-button');
         assertTrue(!!actionButton);
@@ -199,13 +193,8 @@
       'keep action menu button triggers a warning dismissal for the extension' +
           'when clicked',
       async function() {
-        const extension =
-            panelElement.shadowRoot!
-                .querySelectorAll<HTMLElement>('.panel-extension-row')
-                ?.[0];
-        assertTrue(!!extension);
-
         // Open the extension's action menu button.
+        const extension = getExtension();
         const actionButton =
             extension.querySelector<CrIconButtonElement>('cr-icon-button');
         assertTrue(!!actionButton);
diff --git a/chrome/test/data/webui/new_tab_page/logo_test.ts b/chrome/test/data/webui/new_tab_page/logo_test.ts
index 230f449..32f5ded 100644
--- a/chrome/test/data/webui/new_tab_page/logo_test.ts
+++ b/chrome/test/data/webui/new_tab_page/logo_test.ts
@@ -46,7 +46,6 @@
         animationUrl: null,
         animationImpressionLogUrl: null,
         imageUrl: {url: createImageDataUrl(width, height, 'red')},
-        shareButton: null,
         width,
         height,
         backgroundColor: {value: 0xffffffff},
@@ -56,7 +55,6 @@
         animationUrl: null,
         animationImpressionLogUrl: null,
         imageUrl: {url: createImageDataUrl(width, height, 'blue')},
-        shareButton: null,
         width,
         height,
         backgroundColor: {value: 0x000000ff},
diff --git a/chrome/test/data/webui/settings/clear_browsing_data_test.ts b/chrome/test/data/webui/settings/clear_browsing_data_test.ts
index de024093..0166793d 100644
--- a/chrome/test/data/webui/settings/clear_browsing_data_test.ts
+++ b/chrome/test/data/webui/settings/clear_browsing_data_test.ts
@@ -131,10 +131,7 @@
   assertTrue(!!cookiesCheckbox);
   cookiesCheckbox.$.checkbox.click();
   await microtasksFinished();
-  const actionButton =
-      element.shadowRoot!.querySelector<CrButtonElement>('.action-button');
-  assertTrue(!!actionButton);
-  assertFalse(actionButton!.disabled);
+  assertFalse(element.$.clearButton.disabled);
 
   // The user selects a time range value.
   const dropdownMenu =
@@ -147,7 +144,7 @@
   await microtasksFinished();
 
   // The correct time range value is dual written to the other pref.
-  actionButton.click();
+  element.$.clearButton.click();
   await microtasksFinished();
   assertEquals(expectedDualWrittenPrefValue, element.getPref(prefName).value);
 }
@@ -452,10 +449,7 @@
     assertTrue(!!cookiesCheckbox);
     cookiesCheckbox.$.checkbox.click();
     await microtasksFinished();
-    const actionButton =
-        element.shadowRoot!.querySelector<CrButtonElement>('.action-button');
-    assertTrue(!!actionButton);
-    assertFalse(actionButton.disabled);
+    assertFalse(element.$.clearButton.disabled);
 
     // Before trying data clearing without a time range selection, the dropdown
     // is not in the dropdown-error state.
@@ -463,7 +457,7 @@
 
     // Once the user tries to clear data without having made a time range
     // selection the dropdown goes into the dropdown-error state.
-    actionButton.click();
+    element.$.clearButton.click();
     await microtasksFinished();
     assertTrue(dropdownMenu.classList.contains('dropdown-error'));
 
@@ -632,10 +626,7 @@
     await microtasksFinished();
     // Confirming the deletion persists the dropdown selection to the pref and
     // sends the time range for clearing.
-    const actionButton =
-        element.shadowRoot!.querySelector<CrButtonElement>('.action-button');
-    assertTrue(!!actionButton);
-    actionButton.click();
+    element.$.clearButton.click();
     await microtasksFinished();
     assertEquals(TimePeriod.LAST_WEEK, element.getPref(prefName).value);
     const args = await testBrowserProxy.whenCalled('clearBrowsingData');
@@ -696,10 +687,7 @@
     await microtasksFinished();
 
     // Confirming the deletion persists the tab selection to the pref.
-    const actionButton =
-        element.shadowRoot!.querySelector<CrButtonElement>('.action-button');
-    assertTrue(!!actionButton);
-    actionButton.click();
+    element.$.clearButton.click();
     await microtasksFinished();
     assertEquals(
         1, element.getPref('browser.last_clear_browsing_data_tab').value);
@@ -711,9 +699,6 @@
     const cancelButton =
         element.shadowRoot!.querySelector<CrButtonElement>('.cancel-button');
     assertTrue(!!cancelButton);
-    const actionButton =
-        element.shadowRoot!.querySelector<CrButtonElement>('.action-button');
-    assertTrue(!!actionButton);
     const spinner = element.shadowRoot!.querySelector('paper-spinner-lite');
     assertTrue(!!spinner);
 
@@ -730,12 +715,12 @@
     await microtasksFinished();
 
     assertFalse(cancelButton.disabled);
-    assertFalse(actionButton.disabled);
+    assertFalse(element.$.clearButton.disabled);
     assertFalse(spinner.active);
 
     const promiseResolver = new PromiseResolver<ClearBrowsingDataResult>();
     testBrowserProxy.setClearBrowsingDataPromise(promiseResolver.promise);
-    actionButton.click();
+    element.$.clearButton.click();
 
     const args = await testBrowserProxy.whenCalled('clearBrowsingData');
     const dataTypes = args[0];
@@ -743,7 +728,7 @@
     assertEquals('browser.clear_data.cookies_basic', dataTypes[0]);
     assertTrue(element.$.clearBrowsingDataDialog.open);
     assertTrue(cancelButton.disabled);
-    assertTrue(actionButton.disabled);
+    assertTrue(element.$.clearButton.disabled);
     assertTrue(spinner.active);
 
     // Simulate signal from browser indicating that clearing has
@@ -758,7 +743,7 @@
 
     assertFalse(element.$.clearBrowsingDataDialog.open);
     assertFalse(cancelButton.disabled);
-    assertFalse(actionButton.disabled);
+    assertFalse(element.$.clearButton.disabled);
     assertFalse(spinner.active);
     assertFalse(!!element.shadowRoot!.querySelector('#historyNotice'));
     assertFalse(!!element.shadowRoot!.querySelector('#passwordsNotice'));
@@ -767,31 +752,25 @@
   test('ClearBrowsingDataClearButton', async function() {
     assertTrue(element.$.clearBrowsingDataDialog.open);
 
-    const actionButton =
-        element.shadowRoot!.querySelector<CrButtonElement>('.action-button');
-    assertTrue(!!actionButton);
     // Initially the button is disabled because all checkboxes are off.
-    assertTrue(actionButton.disabled);
+    assertTrue(element.$.clearButton.disabled);
     // The button gets enabled if any checkbox is selected.
     element.$.cookiesCheckboxBasic.$.checkbox.click();
     await microtasksFinished();
     assertTrue(element.$.cookiesCheckboxBasic.checked);
-    assertFalse(actionButton.disabled);
+    assertFalse(element.$.clearButton.disabled);
     // Switching to advanced disables the button.
     element.$.tabs.selected = 1;
     await microtasksFinished();
-    assertTrue(actionButton.disabled);
+    assertTrue(element.$.clearButton.disabled);
     // Switching back enables it again.
     element.$.tabs.selected = 0;
     await microtasksFinished();
-    assertFalse(actionButton.disabled);
+    assertFalse(element.$.clearButton.disabled);
   });
 
   test('showHistoryDeletionDialog', async function() {
     assertTrue(element.$.clearBrowsingDataDialog.open);
-    const actionButton =
-        element.shadowRoot!.querySelector<CrButtonElement>('.action-button');
-    assertTrue(!!actionButton);
 
     // Select a datatype for deletion to enable the clear button.
     element.$.cookiesCheckboxBasic.$.checkbox.click();
@@ -804,11 +783,11 @@
         'browser.clear_data.time_period_v2_basic',
         TimePeriodExperiment.LAST_DAY);
     await microtasksFinished();
-    assertFalse(actionButton.disabled);
+    assertFalse(element.$.clearButton.disabled);
 
     const promiseResolver = new PromiseResolver<ClearBrowsingDataResult>();
     testBrowserProxy.setClearBrowsingDataPromise(promiseResolver.promise);
-    actionButton.click();
+    element.$.clearButton.click();
 
     await testBrowserProxy.whenCalled('clearBrowsingData');
     // Passing showHistoryNotice = true should trigger the notice about
@@ -849,9 +828,6 @@
 
   test('showPasswordsDeletionDialog', async function() {
     assertTrue(element.$.clearBrowsingDataDialog.open);
-    const actionButton =
-        element.shadowRoot!.querySelector<CrButtonElement>('.action-button');
-    assertTrue(!!actionButton);
 
     // Select a datatype for deletion to enable the clear button.
     const cookieCheckbox = element.$.cookiesCheckboxBasic;
@@ -866,11 +842,11 @@
         'browser.clear_data.time_period_v2_basic',
         TimePeriodExperiment.LAST_DAY);
     await microtasksFinished();
-    assertFalse(actionButton.disabled);
+    assertFalse(element.$.clearButton.disabled);
 
     const promiseResolver = new PromiseResolver<ClearBrowsingDataResult>();
     testBrowserProxy.setClearBrowsingDataPromise(promiseResolver.promise);
-    actionButton.click();
+    element.$.clearButton.click();
 
     await testBrowserProxy.whenCalled('clearBrowsingData');
     // Passing showPasswordsNotice = true should trigger the notice about
@@ -908,9 +884,6 @@
 
   test('showBothHistoryAndPasswordsDeletionDialog', async function() {
     assertTrue(element.$.clearBrowsingDataDialog.open);
-    const actionButton =
-        element.shadowRoot!.querySelector<CrButtonElement>('.action-button');
-    assertTrue(!!actionButton);
 
     // Select a datatype for deletion to enable the clear button.
     const cookieCheckbox = element.$.cookiesCheckboxBasic;
@@ -925,11 +898,11 @@
         'browser.clear_data.time_period_v2_basic',
         TimePeriodExperiment.LAST_DAY);
     await microtasksFinished();
-    assertFalse(actionButton.disabled);
+    assertFalse(element.$.clearButton.disabled);
 
     const promiseResolver = new PromiseResolver<ClearBrowsingDataResult>();
     testBrowserProxy.setClearBrowsingDataPromise(promiseResolver.promise);
-    actionButton.click();
+    element.$.clearButton.click();
 
     await testBrowserProxy.whenCalled('clearBrowsingData');
     // Passing showHistoryNotice = true and showPasswordsNotice = true
diff --git a/chrome/test/data/webui/settings/cookies_page_test.ts b/chrome/test/data/webui/settings/cookies_page_test.ts
index 8e12b7d7..e51b3f5 100644
--- a/chrome/test/data/webui/settings/cookies_page_test.ts
+++ b/chrome/test/data/webui/settings/cookies_page_test.ts
@@ -84,10 +84,13 @@
     assertTrue(isChildVisible(page, '#generalControls'));
     assertTrue(isChildVisible(page, '#advancedHeader'));
     assertTrue(isChildVisible(page, '#exceptionHeader3pcd'));
-    assertTrue(isChildVisible(page, '#allowExceptionsList'));
+    assertTrue(isChildVisible(page, '#allow3pcExceptionsList'));
     // To be removed with old UI.
     assertFalse(isChildVisible(page, '#exceptionHeader'));
     assertFalse(isChildVisible(page, '#exceptionHeaderSubLabel'));
+    // Will only be shown in the TP rollout.
+    assertFalse(isChildVisible(page, '#exceptionHeaderTrackingProtection'));
+    assertFalse(isChildVisible(page, '#trackingProtectionExceptionsList'));
 
     // Settings
     assertTrue(isChildVisible(page, '#doNotTrack'));
@@ -425,7 +428,7 @@
     assertFalse(isChildVisible(page, '#exceptionHeader'));
     assertFalse(isChildVisible(page, '#exceptionHeaderSubLabel'));
     assertTrue(isChildVisible(page, '#exceptionHeader3pcd'));
-    assertTrue(isChildVisible(page, '#allowExceptionsList'));
+    assertTrue(isChildVisible(page, '#allow3pcExceptionsList'));
   });
 
   test('BlockAll3pcToggle', async function() {
@@ -549,6 +552,36 @@
   });
 });
 
+suite('TrackingProtectionRolloutUx', function() {
+  let page: SettingsCookiesPageElement;
+  let settingsPrefs: SettingsPrefsElement;
+
+  suiteSetup(function() {
+    loadTimeData.overrideValues({
+      enableTrackingProtectionRolloutUx: true,
+    });
+    settingsPrefs = document.createElement('settings-prefs');
+    return CrSettingsPrefs.initialized;
+  });
+
+  setup(function() {
+    document.body.innerHTML = window.trustedTypes!.emptyHTML;
+    page = document.createElement('settings-cookies-page');
+    page.prefs = settingsPrefs.prefs!;
+    document.body.appendChild(page);
+    flush();
+  });
+
+  test('TrackingProtectionExceptionsListDisplayed', function() {
+    // Tracking Protection elements are shown
+    assertTrue(isChildVisible(page, '#exceptionHeaderTrackingProtection'));
+    assertTrue(isChildVisible(page, '#trackingProtectionExceptionsList'));
+    // 3PC elements are hidden
+    assertFalse(isChildVisible(page, '#exceptionHeader3pcd'));
+    assertFalse(isChildVisible(page, '#allow3pcExceptionsList'));
+  });
+});
+
 suite('TrackingProtectionSettingsRollbackNotice', function() {
   let page: SettingsCookiesPageElement;
   let settingsPrefs: SettingsPrefsElement;
diff --git a/chrome/test/data/webui/settings/performance_page_test.ts b/chrome/test/data/webui/settings/performance_page_test.ts
index db28754f..5ec66e65 100644
--- a/chrome/test/data/webui/settings/performance_page_test.ts
+++ b/chrome/test/data/webui/settings/performance_page_test.ts
@@ -23,7 +23,7 @@
     },
     aggressiveness: {
       type: chrome.settingsPrivate.PrefType.NUMBER,
-      value: 1,
+      value: MemorySaverModeAggressiveness.MEDIUM,
     },
   },
 };
@@ -226,6 +226,38 @@
         MemorySaverModeAggressiveness.MEDIUM);
   });
 
+  suite('EnterprisePolicy', function() {
+    function assertMemorySaverModeAggressivenessPolicyIndicatorExists(
+        mode: MemorySaverModeAggressiveness, el: HTMLElement) {
+      performancePage.setPrefValue(MEMORY_SAVER_MODE_AGGRESSIVENESS_PREF, mode);
+      flush();
+      assertTrue(!!el.shadowRoot!.querySelector('cr-policy-pref-indicator'));
+    }
+
+    setup(function() {
+      performancePage.set(`prefs.${MEMORY_SAVER_MODE_AGGRESSIVENESS_PREF}`, {
+        enforcement: chrome.settingsPrivate.Enforcement.ENFORCED,
+        controlledBy: chrome.settingsPrivate.ControlledBy.USER_POLICY,
+        type: chrome.settingsPrivate.PrefType.NUMBER,
+        value: MemorySaverModeAggressiveness.MEDIUM,
+      });
+    });
+
+    test('testMemorySaverModeAggressiveness', function() {
+      performancePage.setPrefValue(
+          MEMORY_SAVER_MODE_PREF, MemorySaverModeState.ENABLED);
+
+      assertMemorySaverModeAggressivenessPolicyIndicatorExists(
+          MemorySaverModeAggressiveness.CONSERVATIVE, conservativeButton);
+
+      assertMemorySaverModeAggressivenessPolicyIndicatorExists(
+          MemorySaverModeAggressiveness.MEDIUM, mediumButton);
+
+      assertMemorySaverModeAggressivenessPolicyIndicatorExists(
+          MemorySaverModeAggressiveness.AGGRESSIVE, aggressiveButton);
+    });
+  });
+
   test('testDiscardTingTreatmentChangeState', async function() {
     performancePage.setPrefValue(DISCARD_RING_PREF, false);
 
diff --git a/chrome/test/data/webui/settings/route_test.ts b/chrome/test/data/webui/settings/route_test.ts
index b5efbaa..997c8eb0 100644
--- a/chrome/test/data/webui/settings/route_test.ts
+++ b/chrome/test/data/webui/settings/route_test.ts
@@ -257,7 +257,7 @@
 
     // <if expr="chromeos_ash">
     // Regression test for b/265453606.
-    assertFalse(!!routes.SIGN_OUT);
+    assertFalse('SIGN_OUT' in routes);
     // </if>
 
     // <if expr="not chromeos_ash">
diff --git a/chrome/test/data/webui/settings/settings_browsertest.cc b/chrome/test/data/webui/settings/settings_browsertest.cc
index 9bcf232..8710c090 100644
--- a/chrome/test/data/webui/settings/settings_browsertest.cc
+++ b/chrome/test/data/webui/settings/settings_browsertest.cc
@@ -628,6 +628,12 @@
 }
 
 IN_PROC_BROWSER_TEST_F(SettingsCookiesPageTest,
+                       TrackingProtectionRolloutUx) {
+  RunTest("settings/cookies_page_test.js",
+          "runMochaSuite('TrackingProtectionRolloutUx')");
+}
+
+IN_PROC_BROWSER_TEST_F(SettingsCookiesPageTest,
                        TrackingProtectionSettingsRollbackNotice) {
   RunTest("settings/cookies_page_test.js",
           "runMochaSuite('TrackingProtectionSettingsRollbackNotice')");
diff --git a/chrome/test/data/webui/settings/sync_account_control_test.ts b/chrome/test/data/webui/settings/sync_account_control_test.ts
index 0b73b8860..2ac92af 100644
--- a/chrome/test/data/webui/settings/sync_account_control_test.ts
+++ b/chrome/test/data/webui/settings/sync_account_control_test.ts
@@ -2,24 +2,18 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// clang-format off
-import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-// <if expr="not chromeos_ash">
-import type {CrActionMenuElement, StoredAccount} from 'chrome://settings/settings.js';
-import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
-import {assertNotEquals} from 'chrome://webui-test/chai_assert.js';
-// </if>
+import 'chrome://settings/settings.js';
 
-import type {SettingsSyncAccountControlElement} from 'chrome://settings/settings.js';
+import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
+import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import type {CrActionMenuElement, SettingsSyncAccountControlElement, StoredAccount} from 'chrome://settings/settings.js';
 import {MAX_SIGNIN_PROMO_IMPRESSION, Router, SignedInState, StatusAction, SyncBrowserProxyImpl} from 'chrome://settings/settings.js';
-import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
+import {assertEquals, assertFalse, assertNotEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {isChildVisible, isVisible} from 'chrome://webui-test/test_util.js';
 
 import {simulateStoredAccounts} from './sync_test_util.js';
 import {TestSyncBrowserProxy} from './test_sync_browser_proxy.js';
 
-// clang-format on
-
 
 suite('SyncAccountControl', function() {
   let browserProxy: TestSyncBrowserProxy;
@@ -121,7 +115,7 @@
     assertTrue(isChildVisible(testElement, '#promo-header'));
   });
 
-  test('not signed in and no stored accounts', async function() {
+  test('not signed in and no stored accounts', function() {
     testElement.syncStatus = {
       signedInState: SignedInState.SIGNED_OUT,
       signedInUsername: '',
@@ -132,26 +126,16 @@
     assertTrue(isChildVisible(testElement, '#promo-header'));
     assertFalse(isChildVisible(testElement, '#avatar-row'));
 
-    // <if expr="not chromeos_ash">
     // Chrome OS does not use the account switch menu.
     assertFalse(isChildVisible(testElement, '#menu'));
-    // </if>
 
     assertTrue(isChildVisible(testElement, '#signIn'));
 
     testElement.$.signIn.click();
 
-    // <if expr="chromeos_ash">
-    await browserProxy.whenCalled('turnOnSync');
-    // </if>
-
-    // <if expr="not chromeos_ash">
-    await browserProxy.whenCalled('startSignIn');
-    // </if>
+    return browserProxy.whenCalled('startSignIn');
   });
 
-  // <if expr="not chromeos_ash">
-  // Chrome OS users are always signed in.
   test('not signed in but has stored accounts', async function() {
     loadTimeData.overrideValues({isSecondaryUser: true});
     testElement.syncStatus = {
@@ -281,7 +265,6 @@
     assertFalse(!!testElement.shadowRoot!.querySelector('#menu'));
     assertFalse(isChildVisible(testElement, '#dropdown-arrow'));
   });
-  // </if>
 
   // <if expr="chromeos_lacros">
   test('main profile not signed in but has stored accounts', function() {
@@ -335,12 +318,9 @@
         testElement.shadowRoot!
             .querySelector<HTMLElement>('#sync-icon-container')!.hidden);
 
-    // <if expr="not chromeos_ash">
-    // Chrome OS does not use the account switch menu.
     assertFalse(isChildVisible(testElement, 'cr-icon-button'));
     assertFalse(!!testElement.shadowRoot!.querySelector('#menu'));
     assertFalse(isChildVisible(testElement, '#dropdown-arrow'));
-    // </if>
 
     const userInfo =
         testElement.shadowRoot!.querySelector<HTMLElement>('#user-info')!;
@@ -624,7 +604,6 @@
     assertTrue(testElement.$.signIn.disabled);
   });
 
-  // <if expr="not chromeos_ash">
   test('signinPaused effects', function() {
     // <if expr="chromeos_lacros">
     // For Lacros, force the page to be loaded as if it was a secondary user so
@@ -699,5 +678,4 @@
 
     assertFalse(isChildVisible(testElement, '#avatar-row'));
   });
-  // </if>
 });
diff --git a/chrome/test/data/webui/side_panel/read_anything/fake_speech_synthesis.ts b/chrome/test/data/webui/side_panel/read_anything/fake_speech_synthesis.ts
index 7749f76..3f7ce5a 100644
--- a/chrome/test/data/webui/side_panel/read_anything/fake_speech_synthesis.ts
+++ b/chrome/test/data/webui/side_panel/read_anything/fake_speech_synthesis.ts
@@ -10,19 +10,29 @@
   spokenUtterances: SpeechSynthesisUtterance[];
   errorEvent: SpeechSynthesisErrorCode|undefined;
   triggerUtteranceStartedEventNext: boolean = false;
+  shouldUseLocalVoices: boolean = false;
+  canceledUtterances: SpeechSynthesisUtterance[];
+  currentUtterance: SpeechSynthesisUtterance|undefined;
 
 
   constructor() {
     this.spokenUtterances = [];
+    this.canceledUtterances = [];
   }
 
   clearSpokenUtterances() {
     this.spokenUtterances = [];
+    this.canceledUtterances = [];
   }
 
   cancel() {
     this.canceled = true;
     this.speaking = false;
+
+    if (this.currentUtterance) {
+      this.canceledUtterances.push(this.currentUtterance);
+      this.currentUtterance = undefined;
+    }
   }
 
   pause() {
@@ -41,28 +51,37 @@
       {lang: 'en', name: 'Kristi'} as SpeechSynthesisVoice,
       {lang: 'en', name: 'Shari'} as SpeechSynthesisVoice,
       {lang: 'en', name: 'Yu'} as SpeechSynthesisVoice,
-      {lang: 'en', name: 'Xiang'} as SpeechSynthesisVoice,
+      {lang: 'en', name: 'Xiang', localService: this.shouldUseLocalVoices} as
+          SpeechSynthesisVoice,
     ];
   }
 
   speak(utterance: SpeechSynthesisUtterance) {
+    this.currentUtterance = utterance;
     this.paused = false;
     this.speaking = true;
     this.spokenUtterances.push(utterance);
-    if (utterance.onend) {
-      utterance.onend(new SpeechSynthesisEvent('end', {utterance}));
-    }
 
     if (utterance.onerror && this.errorEvent !== undefined) {
-      utterance.onerror(new SpeechSynthesisErrorEvent(
-          'type', {utterance: utterance, error: this.errorEvent}));
+      // Copy this.errorEvent to a temporary variable so that this.errorEvent
+      // can be cleared before calling onerror. Otherwise, we can create an
+      // infinite loop where this.errorEvent never clears based on how
+      // synthesis is handled in the main app.
+      const errorCode = this.errorEvent;
       this.errorEvent = undefined;
+      utterance.onerror(new SpeechSynthesisErrorEvent(
+          'type', {utterance: utterance, error: errorCode}));
     }
 
     if (this.triggerUtteranceStartedEventNext && utterance.onstart) {
       utterance.onstart(new SpeechSynthesisEvent('start', {utterance}));
       this.triggerUtteranceStartedEventNext = false;
     }
+
+    if (utterance.onend) {
+      this.currentUtterance = undefined;
+      utterance.onend(new SpeechSynthesisEvent('end', {utterance}));
+    }
   }
 
   // On the next #speak, trigger an error of the given error code.
@@ -74,6 +93,10 @@
     this.triggerUtteranceStartedEventNext = true;
   }
 
+  useLocalVoices() {
+    this.shouldUseLocalVoices = true;
+  }
+
   // These are currently unused in tests but need to be defined in order to be
   // used a SpeechSynthesis object. Can be implemented as needed.
   onvoiceschanged = () => {};
diff --git a/chrome/test/data/webui/side_panel/read_anything/speech_test.ts b/chrome/test/data/webui/side_panel/read_anything/speech_test.ts
index 0e4f50f..1be011e4 100644
--- a/chrome/test/data/webui/side_panel/read_anything/speech_test.ts
+++ b/chrome/test/data/webui/side_panel/read_anything/speech_test.ts
@@ -227,10 +227,11 @@
     assertEquals(speechSynthesis.spokenUtterances[0]!.text, paragraph2.at(-1)!);
   });
 
-  test('very long text uses max speech length', () => {
+
+  suite('very long text', () => {
     const longSentences =
         'A kingdom of isolation, and it looks like I am the queen and the ' +
-        'wind is howling like this swirling storm inside, Couldn\t keep it ' +
+        'wind is howling like this swirling storm inside, Couldn\'t keep it ' +
         'in, heaven knows I tried, but don\'t let them in, don\'t let them ' +
         'see, be the good girl you always have to be, and conceal, don\'t ' +
         'feel, don\'t let them know.' +
@@ -244,34 +245,78 @@
         'for me- I\'m free- let it go let it go I am one with the wind and ' +
         'sky let it go let it go you\'ll never see me cry- here I stand and ' +
         'here I stay- let the storm rage on';
-    const leafs = [2];
-    const longTree = {
-      rootId: 1,
-      nodes: [
-        {
-          id: 1,
-          role: 'rootWebArea',
-          htmlTag: '#document',
-          childIds: [2],
-        },
-        {
-          id: 2,
-          role: 'staticText',
-          name: longSentences,
-        },
-      ],
-    };
-    const expectedNumSegments =
-        Math.ceil(longSentences.length / app.maxSpeechLength);
+    setup(() => {
+      const leafs = [2];
 
-    chrome.readingMode.setContentForTesting(longTree, leafs);
-    app.playSpeech();
+      const longTree = {
+        rootId: 1,
+        nodes: [
+          {
+            id: 1,
+            role: 'rootWebArea',
+            htmlTag: '#document',
+            childIds: [2],
+          },
+          {
+            id: 2,
+            role: 'staticText',
+            name: longSentences,
+          },
+        ],
+      };
 
-    assertEquals(speechSynthesis.spokenUtterances.length, expectedNumSegments);
-    const spoken =
-        speechSynthesis.spokenUtterances.map(utterance => utterance.text)
-            .join('');
-    assertEquals(spoken, longSentences);
+      chrome.readingMode.setContentForTesting(longTree, leafs);
+    });
+
+    test('uses max speech length', () => {
+      const expectedNumSegments =
+          Math.ceil(longSentences.length / app.maxSpeechLength);
+
+      app.playSpeech();
+
+      assertEquals(
+          speechSynthesis.spokenUtterances.length, expectedNumSegments);
+      const spoken =
+          speechSynthesis.spokenUtterances.map(utterance => utterance.text)
+              .join('');
+      assertEquals(spoken, longSentences);
+    });
+
+    test('on text-too-long error smaller text segment plays', () => {
+      // Remote voices already reduce the size of a speech segment to avoid
+      // the bug where speech stops without an error callback.
+      speechSynthesis.useLocalVoices();
+      chrome.readingMode.onVoiceChange = () => {};
+      emitEvent(
+          app, 'select-voice',
+          {detail: {selectedVoice: speechSynthesis.getVoices()[5]}});
+
+      speechSynthesis.triggerErrorEventOnNextSpeak('text-too-long');
+      app.playSpeech();
+
+      assertFalse(speechSynthesis.speaking);
+      assertTrue(speechSynthesis.canceled);
+      assertFalse(speechSynthesis.paused);
+      assertEquals(speechSynthesis.spokenUtterances.length, 3);
+
+      // The first utterance should contain the entire text, but it should
+      // be canceled. The second utterance should be the smaller text
+      // segment after receiving the text-too-long error. The third utterance
+      // should be the remaining text, as there is no longer a text-too-long
+      // error triggered.
+      const accessibleTextLength = app.getAccessibleTextLength(longSentences);
+      assertEquals(speechSynthesis.spokenUtterances[0]!.text, longSentences);
+      assertEquals(
+          speechSynthesis.spokenUtterances[1]!.text,
+          longSentences.substring(0, accessibleTextLength));
+      assertEquals(
+          speechSynthesis.spokenUtterances[2]!.text,
+          longSentences.substring(accessibleTextLength));
+      assertEquals(speechSynthesis.canceledUtterances.length, 1);
+      assertEquals(
+          speechSynthesis.canceledUtterances[0]!,
+          speechSynthesis.spokenUtterances[0]!);
+    });
   });
 
   suite('while playing', () => {
diff --git a/chrome/test/fuzzing/page_load_in_process_fuzzer.cc b/chrome/test/fuzzing/page_load_in_process_fuzzer.cc
index 4748c41..229e2890 100644
--- a/chrome/test/fuzzing/page_load_in_process_fuzzer.cc
+++ b/chrome/test/fuzzing/page_load_in_process_fuzzer.cc
@@ -137,8 +137,7 @@
             << request.relative_url;
   for (const auto& network_resource : fuzz_case_.network_resource()) {
     if (network_resource.which_server() == which_server &&
-        (network_resource.path() == request.relative_url ||
-         (request.relative_url == "/" && network_resource.path().empty()))) {
+        request.relative_url.substr(1) == network_resource.path()) {
       std::unique_ptr<net::test_server::BasicHttpResponse> response =
           std::make_unique<net::test_server::BasicHttpResponse>();
       response->set_code(
diff --git a/chromeos/ash/components/nearby/common/connections_manager/nearby_connections_manager_impl.cc b/chromeos/ash/components/nearby/common/connections_manager/nearby_connections_manager_impl.cc
index 11bdf0d..b01a8bb7 100644
--- a/chromeos/ash/components/nearby/common/connections_manager/nearby_connections_manager_impl.cc
+++ b/chromeos/ash/components/nearby/common/connections_manager/nearby_connections_manager_impl.cc
@@ -1011,15 +1011,14 @@
 void NearbyConnectionsManagerImpl::OnConnectionResultV3(
     const std::string& endpoint_id,
     Status status) {
-  CD_LOG(INFO, Feature::NEARBY_INFRA)
-      << __func__ << ": OnConnectionResult result=" << status;
+  CD_LOG(INFO, Feature::NEARBY_INFRA) << __func__ << ": result=" << status;
 
   auto it = pending_outgoing_connections_.find(endpoint_id);
   if (it == pending_outgoing_connections_.end()) {
-    connection_listener_v3s_.ReportBadMessage(
-        base::StringPrintf("OnConnectionResult() received endpoint_id=%s which "
-                           "does not exist in connections V3",
-                           endpoint_id.c_str()));
+    connection_listener_v3s_.ReportBadMessage(base::StringPrintf(
+        "OnConnectionResultV3() received endpoint_id=%s which "
+        "does not exist in connections V3",
+        endpoint_id.c_str()));
     return;
   }
 
@@ -1034,6 +1033,8 @@
     std::move(it->second).Run(/*nearby_connection=*/nullptr);
   }
 
+  base::UmaHistogramEnumeration("Nearby.Connections.V3.Connection.Result",
+                                status);
   pending_outgoing_connections_.erase(it);
   connect_timeout_timers_v3_.erase(endpoint_id);
 }
diff --git a/chromeos/ash/components/nearby/common/connections_manager/nearby_connections_manager_impl_unittest.cc b/chromeos/ash/components/nearby/common/connections_manager/nearby_connections_manager_impl_unittest.cc
index c16081f..197aedb 100644
--- a/chromeos/ash/components/nearby/common/connections_manager/nearby_connections_manager_impl_unittest.cc
+++ b/chromeos/ash/components/nearby/common/connections_manager/nearby_connections_manager_impl_unittest.cc
@@ -190,6 +190,7 @@
         std::make_unique<NearbyConnectionsManagerImpl>(&nearby_process_manager_,
                                                        kServiceId);
     scoped_feature_list_.InitAndEnableFeature(features::kNearbySharingWebRtc);
+    histogram_tester_ = std::make_unique<base::HistogramTester>();
 
     EXPECT_CALL(nearby_process_manager_, GetNearbyProcessReference)
         .WillRepeatedly([&](ash::nearby::NearbyProcessManager::
@@ -537,7 +538,10 @@
     accept_or_reject_run_loop.Run();
   }
 
+  base::HistogramTester* histogram_tester() { return histogram_tester_.get(); }
+
   base::test::ScopedFeatureList scoped_feature_list_;
+  std::unique_ptr<base::HistogramTester> histogram_tester_;
   content::BrowserTaskEnvironment task_environment_{
       base::test::TaskEnvironment::TimeSource::MOCK_TIME};
   bool should_use_web_rtc_ = true;
@@ -2088,6 +2092,11 @@
                 /*is_incoming_connection=*/false,
                 nearby::connections::mojom::AuthenticationStatus::kSuccess),
             /*on_connection_result_status=*/Status::kSuccess);
+
+  histogram_tester()->ExpectBucketCount(
+      "Nearby.Connections.V3.Connection.Result", Status::kSuccess, 1);
+  histogram_tester()->ExpectTotalCount(
+      "Nearby.Connections.V3.Connection.Result", 1);
 }
 
 TEST_F(NearbyConnectionsManagerImplTest, RequestConnectionV3Reject) {
@@ -2118,6 +2127,11 @@
                 /*is_incoming_connection=*/false,
                 nearby::connections::mojom::AuthenticationStatus::kFailure),
             /*on_connection_result_status=*/Status::kError);
+
+  histogram_tester()->ExpectBucketCount(
+      "Nearby.Connections.V3.Connection.Result", Status::kError, 1);
+  histogram_tester()->ExpectTotalCount(
+      "Nearby.Connections.V3.Connection.Result", 1);
 }
 
 TEST_F(NearbyConnectionsManagerImplTest, DisconnectV3) {
@@ -2176,7 +2190,6 @@
 }
 
 TEST_F(NearbyConnectionsManagerImplTest, OnBandwidthChangedV3) {
-  base::HistogramTester histogram_tester;
   mojo::Remote<ConnectionListenerV3> connection_listener_v3_remote;
   mojo::Remote<PayloadListenerV3> payload_listener_v3_remote;
 
@@ -2229,14 +2242,14 @@
       presence_device.GetEndpointId(),
       nearby::connections::mojom::BandwidthInfo::New(BandwidthQuality::kMedium,
                                                      Medium::kBluetooth));
-  histogram_tester.ExpectTotalCount(
+  histogram_tester()->ExpectTotalCount(
       "Nearby.Connections.V3.Medium.ChangedToMedium", 0);
   connection_listener_v3_remote->OnBandwidthChangedV3(
       presence_device.GetEndpointId(),
       nearby::connections::mojom::BandwidthInfo::New(BandwidthQuality::kHigh,
                                                      Medium::kWebRtc));
   bandwidth_run_loop.Run();
-  histogram_tester.ExpectBucketCount(
+  histogram_tester()->ExpectBucketCount(
       "Nearby.Connections.V3.Medium.ChangedToMedium", Medium::kWebRtc, 1);
 }
 
diff --git a/clank b/clank
index 2cffdf4..fe29512 160000
--- a/clank
+++ b/clank
@@ -1 +1 @@
-Subproject commit 2cffdf40d0973f4fad8cbe455e916c46f1ec64f5
+Subproject commit fe29512018ec9f92b49e92702baf0f087656fa32
diff --git a/components/autofill/content/renderer/password_autofill_agent.cc b/components/autofill/content/renderer/password_autofill_agent.cc
index 6c75765..9061cc36 100644
--- a/components/autofill/content/renderer/password_autofill_agent.cc
+++ b/components/autofill/content/renderer/password_autofill_agent.cc
@@ -42,6 +42,7 @@
 #include "components/autofill/core/common/autofill_features.h"
 #include "components/autofill/core/common/autofill_regexes.h"
 #include "components/autofill/core/common/autofill_util.h"
+#include "components/autofill/core/common/form_data.h"
 #include "components/autofill/core/common/form_field_data.h"
 #include "components/autofill/core/common/mojom/autofill_types.mojom-shared.h"
 #include "components/autofill/core/common/mojom/autofill_types.mojom.h"
@@ -785,7 +786,7 @@
                          AutofillSuggestionTriggerSource::kTextFieldDidChange);
 }
 
-// LINT.IfChange()
+// LINT.IfChange
 
 void PasswordAutofillAgent::NotifyPasswordManagerAboutFieldModification(
     const WebInputElement& element) {
@@ -853,7 +854,7 @@
     const std::u16string& username,
     const std::u16string& password) {
   auto element = focused_element().DynamicTo<WebInputElement>();
-  if (element.IsNull() || focused_element().IsReadOnly()) {
+  if (element.IsNull() || !IsElementEditable(element)) {
     return;
   }
 
@@ -861,10 +862,8 @@
   WebInputElement password_element;
   PasswordInfo* password_info = nullptr;
 
-  if (!FindPasswordInfoForElement(element, UseFallbackData(true),
-                                  &username_element, &password_element,
-                                  &password_info) ||
-      (!password_element.IsNull() && !IsElementEditable(password_element))) {
+  if (!HasElementsToFill(element, UseFallbackData(true), &username_element,
+                         &password_element, &password_info)) {
     return;
   }
 
@@ -889,7 +888,7 @@
     DoFillField(username_element, username);
   }
 
-  if (!password_element.IsNull()) {
+  if (!password_element.IsNull() && IsElementEditable(password_element)) {
     FillPasswordFieldAndSave(password_element, password);
 
     // TODO(crbug.com/40223173): As Touch-To-Fill and auto-submission don't
@@ -989,7 +988,7 @@
     const std::u16string& password) {
   // The element in context of the suggestion popup.
   const WebInputElement element = control_element.DynamicTo<WebInputElement>();
-  if (element.IsNull()) {
+  if (element.IsNull() || !IsElementEditable(element)) {
     return;
   }
 
@@ -997,10 +996,8 @@
   WebInputElement password_element;
   PasswordInfo* password_info;
 
-  if (!FindPasswordInfoForElement(element, UseFallbackData(true),
-                                  &username_element, &password_element,
-                                  &password_info) ||
-      (!password_element.IsNull() && !IsElementEditable(password_element))) {
+  if (!HasElementsToFill(element, UseFallbackData(true), &username_element,
+                         &password_element, &password_info)) {
     return;
   }
 
@@ -1011,7 +1008,7 @@
 
     DoPreviewField(username_element, username, /*is_password=*/false);
   }
-  if (!password_element.IsNull()) {
+  if (!password_element.IsNull() && IsElementEditable(password_element)) {
     DoPreviewField(password_element, password, /*is_password=*/true);
   }
 }
@@ -1100,6 +1097,40 @@
   return true;
 }
 
+bool PasswordAutofillAgent::IsUsernameOrPasswordFillable(
+    const WebInputElement& username_element,
+    const WebInputElement& password_element,
+    PasswordInfo* password_info) {
+  if (!username_element.IsNull() && IsElementEditable(username_element) &&
+      password_element.IsNull()) {
+    return true;
+  }
+  if (!password_element.IsNull() && IsElementEditable(password_element)) {
+    return true;
+  }
+  // Password field might be disabled before entering valid username.
+  // For such cases, check that the password field is under <form> tag.
+  // Otherwise, username and password element might not be related.
+  return !username_element.IsNull() && IsElementEditable(username_element) &&
+         (password_info != nullptr &&
+          !password_info->fill_data.form_renderer_id.is_null());
+}
+
+bool PasswordAutofillAgent::HasElementsToFill(
+    const WebInputElement& trigger_element,
+    UseFallbackData use_fallback_data,
+    WebInputElement* username_element,
+    WebInputElement* password_element,
+    PasswordInfo** password_info) {
+  if (!FindPasswordInfoForElement(trigger_element, use_fallback_data,
+                                  username_element, password_element,
+                                  password_info)) {
+    return false;
+  }
+  return IsUsernameOrPasswordFillable(*username_element, *password_element,
+                                      *password_info);
+}
+
 void PasswordAutofillAgent::MaybeCheckSafeBrowsingReputation(
     const WebInputElement& element) {
   // Enabled on desktop and Android
@@ -1649,9 +1680,12 @@
     }
   }
 
-  // Check that all fillable elements are editable.
-  if (!element.IsTextField() || !IsElementEditable(element) ||
-      (!password_element.IsNull() && !IsElementEditable(password_element))) {
+  if (!element.IsTextField() || !IsElementEditable(element)) {
+    return false;
+  }
+  // Check that at least one fillable element is editable.
+  if (!IsUsernameOrPasswordFillable(username_element, password_element,
+                                    password_info)) {
     return false;
   }
 
@@ -1757,14 +1791,21 @@
   FindPasswordInfoForElement(user_input, UseFallbackData(false),
                              &username_element, &password_element,
                              &password_info);
+  size_t username_element_index = GetIndexOfElement(form, username_element);
+  if (username_element.IsNull() || !IsElementEditable(username_element)) {
+    username_element_index = form.fields.size();
+  }
+  size_t password_element_index = GetIndexOfElement(form, password_element);
+  if (password_element.IsNull() || !IsElementEditable(password_element)) {
+    password_element_index = form.fields.size();
+  }
 
   const bool show_webauthn_credentials =
       field.parsed_autocomplete() && field.parsed_autocomplete()->webauthn;
   GetPasswordManagerDriver().ShowPasswordSuggestions(PasswordSuggestionRequest(
-      field.renderer_id(), form, trigger_source,
-      GetIndexOfElement(form, username_element),
-      GetIndexOfElement(form, password_element), field.text_direction(),
-      typed_username, show_webauthn_credentials,
+      field.renderer_id(), form, trigger_source, username_element_index,
+      password_element_index, field.text_direction(), typed_username,
+      show_webauthn_credentials,
       render_frame()->ElementBoundsInWindow(user_input)));
 }
 
diff --git a/components/autofill/content/renderer/password_autofill_agent.h b/components/autofill/content/renderer/password_autofill_agent.h
index bbf97bd..9063d69 100644
--- a/components/autofill/content/renderer/password_autofill_agent.h
+++ b/components/autofill/content/renderer/password_autofill_agent.h
@@ -380,6 +380,7 @@
   // be filled. If the username exists, it should be passed as `user_input`. If
   // there is no username, pass the password field in `user_input`. In the
   // latter case, no username value will be shown in the pop-up.
+  // Suggestion will be shown only on editable fields.
   void ShowSuggestionPopup(const std::u16string& typed_username,
                            const blink::WebInputElement& user_input,
                            AutofillSuggestionTriggerSource trigger_source);
@@ -399,6 +400,22 @@
                                   blink::WebInputElement* password_element,
                                   PasswordInfo** password_info);
 
+  // Returns true if at least one of the found username or password field is
+  // fillable.
+  bool IsUsernameOrPasswordFillable(
+      const blink::WebInputElement& username_element,
+      const blink::WebInputElement& password_element,
+      PasswordInfo* password_info);
+
+  // Finds the PasswordInfo, username and password fields corresponding to the
+  // passed in `element` and returns true if there is at least one field that
+  // can be filled by a password suggestion.
+  bool HasElementsToFill(const blink::WebInputElement& trigger_element,
+                         UseFallbackData use_fallback_data,
+                         blink::WebInputElement* username_element,
+                         blink::WebInputElement* password_element,
+                         PasswordInfo** password_info);
+
   // Cleans up the state when document is shut down, e.g. when committing a new
   // document or closing the frame.
   void CleanupOnDocumentShutdown();
diff --git a/components/autofill/core/browser/autofill_client.cc b/components/autofill/core/browser/autofill_client.cc
index ceef309..b77d1cb 100644
--- a/components/autofill/core/browser/autofill_client.cc
+++ b/components/autofill/core/browser/autofill_client.cc
@@ -134,9 +134,6 @@
 void AutofillClient::HideVirtualCardEnrollBubbleAndIconIfVisible() {
 }
 
-void AutofillClient::UpdateWebauthnOfferDialogWithError() {
-}
-
 bool AutofillClient::CloseWebauthnDialog() {
   return false;
 }
diff --git a/components/autofill/core/browser/autofill_client.h b/components/autofill/core/browser/autofill_client.h
index 9c76fc04..fe25260 100644
--- a/components/autofill/core/browser/autofill_client.h
+++ b/components/autofill/core/browser/autofill_client.h
@@ -510,10 +510,6 @@
   // Hides the virtual card enroll bubble and icon if it is visible.
   virtual void HideVirtualCardEnrollBubbleAndIconIfVisible();
 
-  // Will update the WebAuthn dialog content when there is an error fetching the
-  // challenge.
-  virtual void UpdateWebauthnOfferDialogWithError();
-
   // Will close the current visible WebAuthn dialog. Returns true if dialog was
   // visible and has been closed.
   virtual bool CloseWebauthnDialog();
diff --git a/components/autofill/core/browser/autofill_manager.cc b/components/autofill/core/browser/autofill_manager.cc
index a46bcdf..fe20bd3 100644
--- a/components/autofill/core/browser/autofill_manager.cc
+++ b/components/autofill/core/browser/autofill_manager.cc
@@ -153,9 +153,9 @@
 
 AutofillManager::AutofillManager(AutofillDriver* driver)
     : driver_(CHECK_DEREF(driver)),
-      log_manager_(unsafe_client().GetLogManager()),
+      log_manager_(client().GetLogManager()),
       form_interactions_ukm_logger_(CreateFormInteractionsUkmLogger()) {
-  if (auto* translate_driver = unsafe_client().GetTranslateDriver()) {
+  if (auto* translate_driver = client().GetTranslateDriver()) {
     translate_observation_.Observe(translate_driver);
   }
 }
@@ -513,7 +513,7 @@
 std::unique_ptr<AutofillMetrics::FormInteractionsUkmLogger>
 AutofillManager::CreateFormInteractionsUkmLogger() {
   return std::make_unique<AutofillMetrics::FormInteractionsUkmLogger>(
-      &unsafe_client(), unsafe_client().GetUkmRecorder());
+      &client(), client().GetUkmRecorder());
 }
 
 size_t AutofillManager::FindCachedFormsBySignature(
diff --git a/components/autofill/core/browser/autofill_manager.h b/components/autofill/core/browser/autofill_manager.h
index 9587dda..cb93705 100644
--- a/components/autofill/core/browser/autofill_manager.h
+++ b/components/autofill/core/browser/autofill_manager.h
@@ -21,7 +21,6 @@
 #include "base/task/sequenced_task_runner.h"
 #include "base/task/thread_pool.h"
 #include "base/time/time.h"
-#include "base/types/pass_key.h"
 #include "base/types/strong_alias.h"
 #include "build/build_config.h"
 #include "components/autofill/core/browser/autofill_driver.h"
@@ -46,7 +45,6 @@
 class FormFieldData;
 class FormStructure;
 class LogManager;
-class TouchToFillDelegateAndroidImpl;
 
 // This class defines the interface should be implemented by autofill
 // implementation in browser side to interact with AutofillDriver.
@@ -196,20 +194,8 @@
 
   ~AutofillManager() override;
 
-  // The following will fail a DCHECK if called for a prerendered main frame.
-  AutofillClient& client() {
-    DCHECK(!driver().IsPrerendering());
-    return unsafe_client();
-  }
-
-  const AutofillClient& client() const {
-    return const_cast<AutofillManager*>(this)->client();
-  }
-
-  AutofillClient& unsafe_client(
-      base::PassKey<TouchToFillDelegateAndroidImpl> pass_key) {
-    return AutofillManager::unsafe_client();
-  }
+  AutofillClient& client() { return driver_->GetAutofillClient(); }
+  const AutofillClient& client() const { return driver_->GetAutofillClient(); }
 
   // Returns a WeakPtr to the leaf class.
   virtual base::WeakPtr<AutofillManager> GetWeakPtr() = 0;
@@ -326,10 +312,6 @@
   // Retrieves the page language from |client_|
   LanguageCode GetCurrentPageLanguage();
 
-  // The following do not check for prerendering. These should only used while
-  // constructing or resetting the manager.
-  AutofillClient& unsafe_client() { return driver_->GetAutofillClient(); }
-
   // OnFooImpl() is called, potentially asynchronously after parsing the form,
   // by the renderer event OnFoo().
   virtual void OnFormSubmittedImpl(const FormData& form,
diff --git a/components/autofill/core/browser/browser_autofill_manager.cc b/components/autofill/core/browser/browser_autofill_manager.cc
index 87aef20..e5d9902 100644
--- a/components/autofill/core/browser/browser_autofill_manager.cc
+++ b/components/autofill/core/browser/browser_autofill_manager.cc
@@ -650,24 +650,24 @@
       external_delegate_(std::make_unique<AutofillExternalDelegate>(this)),
       app_locale_(app_locale),
       suggestion_generator_(
-          std::make_unique<AutofillSuggestionGenerator>(unsafe_client())),
+          std::make_unique<AutofillSuggestionGenerator>(client())),
       form_filler_(
           std::make_unique<FormFiller>(*this, log_manager(), app_locale)) {
   address_form_event_logger_ =
       std::make_unique<autofill_metrics::AddressFormEventLogger>(
           driver->IsInAnyMainFrame(), form_interactions_ukm_logger(),
-          &unsafe_client());
+          &client());
   credit_card_form_event_logger_ =
       std::make_unique<autofill_metrics::CreditCardFormEventLogger>(
           driver->IsInAnyMainFrame(), form_interactions_ukm_logger(),
-          unsafe_client().GetPersonalDataManager(), &unsafe_client());
+          client().GetPersonalDataManager(), &client());
   autocomplete_unrecognized_fallback_logger_ = std::make_unique<
       autofill_metrics::AutocompleteUnrecognizedFallbackEventLogger>();
   manual_fallback_logger_ =
       std::make_unique<autofill_metrics::ManualFallbackEventLogger>();
 
   credit_card_access_manager_ = std::make_unique<CreditCardAccessManager>(
-      driver, &unsafe_client(), unsafe_client().GetPersonalDataManager(),
+      driver, &client(), client().GetPersonalDataManager(),
       credit_card_form_event_logger_.get());
 }
 
@@ -2264,13 +2264,13 @@
   address_form_event_logger_ =
       std::make_unique<autofill_metrics::AddressFormEventLogger>(
           driver().IsInAnyMainFrame(), form_interactions_ukm_logger(),
-          &unsafe_client());
+          &client());
   credit_card_form_event_logger_ =
       std::make_unique<autofill_metrics::CreditCardFormEventLogger>(
           driver().IsInAnyMainFrame(), form_interactions_ukm_logger(),
-          unsafe_client().GetPersonalDataManager(), &unsafe_client());
+          client().GetPersonalDataManager(), &client());
   credit_card_access_manager_ = std::make_unique<CreditCardAccessManager>(
-      &driver(), &unsafe_client(), unsafe_client().GetPersonalDataManager(),
+      &driver(), &client(), client().GetPersonalDataManager(),
       credit_card_form_event_logger_.get());
   autocomplete_unrecognized_fallback_logger_ = std::make_unique<
       autofill_metrics::AutocompleteUnrecognizedFallbackEventLogger>();
diff --git a/components/autofill/core/browser/browser_autofill_manager.h b/components/autofill/core/browser/browser_autofill_manager.h
index a6c10803..4e48eb1b 100644
--- a/components/autofill/core/browser/browser_autofill_manager.h
+++ b/components/autofill/core/browser/browser_autofill_manager.h
@@ -619,9 +619,9 @@
   // Autocomplete and merchant promo codes.
   std::unique_ptr<SingleFieldFormFillRouter> single_field_form_fill_router_ =
       std::make_unique<SingleFieldFormFillRouter>(
-          unsafe_client().GetAutocompleteHistoryManager(),
-          unsafe_client().GetIbanManager(),
-          unsafe_client().GetMerchantPromoCodeManager());
+          client().GetAutocompleteHistoryManager(),
+          client().GetIbanManager(),
+          client().GetMerchantPromoCodeManager());
 
   // Utilities for logging form events. The loggers emit metrics during their
   // destruction, effectively when the BrowserAutofillManager is reset or
diff --git a/components/autofill/core/browser/form_parsing/form_field_parser.cc b/components/autofill/core/browser/form_parsing/form_field_parser.cc
index cd0c16d0..81e2e6e 100644
--- a/components/autofill/core/browser/form_parsing/form_field_parser.cc
+++ b/components/autofill/core/browser/form_parsing/form_field_parser.cc
@@ -333,6 +333,15 @@
     FieldCandidatesMap& field_candidates) {
   std::vector<raw_ptr<AutofillField, VectorExperimental>> processed_fields =
       RemoveCheckableFields(fields);
+  // Do not ignore fields with autocomplete attributes attempting to disable
+  // autocomplete. Disabling autocomplete is a common practice on fields where
+  // we don't want to offer email filling even if our heuristics match (e.g.
+  // search input fields).
+  std::erase_if(processed_fields, [](const AutofillField* field) {
+    return field->autocomplete_attribute() == "off" ||
+           field->autocomplete_attribute() == "false";
+  });
+
   ParseFormFieldsPass(EmailFieldParser::Parse, context, processed_fields,
                       field_candidates);
 }
diff --git a/components/autofill/core/browser/form_structure_unittest.cc b/components/autofill/core/browser/form_structure_unittest.cc
index 6e88ba0..19eef9a 100644
--- a/components/autofill/core/browser/form_structure_unittest.cc
+++ b/components/autofill/core/browser/form_structure_unittest.cc
@@ -2497,4 +2497,34 @@
   }
 }
 
+// Verifies that with single field email heuristics, fields with
+// autocomplete=off are not parsed as email fields.
+TEST_F(FormStructureTestImpl,
+       SingleFieldEmailHeuristicsEnabledAutocompleteOff) {
+  base::test::ScopedFeatureList enabled{
+      features::kAutofillEnableEmailHeuristicOnlyAddressForms};
+
+  FormData form = test::GetFormData(
+      {.fields = {{.role = EMAIL_ADDRESS, .autocomplete_attribute = "off"},
+                  {.role = EMAIL_ADDRESS, .autocomplete_attribute = "false"}}});
+
+  // The form has too few fields; it should not run heuristics, falling back to
+  // the single field parsing.
+  EXPECT_FALSE(FormShouldRunHeuristics(form));
+  EXPECT_TRUE(FormShouldRunHeuristicsForSingleFieldForms(form));
+
+  {
+    FormStructure form_structure(form);
+    form_structure.DetermineHeuristicTypes(GeoIpCountryCode(""), nullptr,
+                                           nullptr);
+    ASSERT_EQ(2U, form_structure.field_count());
+    // However, because the email field has autocomplete=off, it should not run
+    // heuristics.
+    ASSERT_EQ(0U, form_structure.autofill_count());
+    EXPECT_EQ(UNKNOWN_TYPE, form_structure.field(0)->heuristic_type());
+    EXPECT_EQ(UNKNOWN_TYPE, form_structure.field(1)->heuristic_type());
+    EXPECT_FALSE(form_structure.IsAutofillable());
+  }
+}
+
 }  // namespace autofill
diff --git a/components/autofill/core/browser/payments/credit_card_fido_authenticator.cc b/components/autofill/core/browser/payments/credit_card_fido_authenticator.cc
index 27d7584..449d8de 100644
--- a/components/autofill/core/browser/payments/credit_card_fido_authenticator.cc
+++ b/components/autofill/core/browser/payments/credit_card_fido_authenticator.cc
@@ -465,8 +465,10 @@
   // End the flow if the server responded with an error.
   if (result != AutofillClient::PaymentsRpcResult::kSuccess) {
 #if !BUILDFLAG(IS_ANDROID)
-    if (current_flow_ == OPT_IN_FETCH_CHALLENGE_FLOW)
-      autofill_client_->UpdateWebauthnOfferDialogWithError();
+    if (current_flow_ == OPT_IN_FETCH_CHALLENGE_FLOW) {
+      autofill_client_->GetPaymentsAutofillClient()
+          ->UpdateWebauthnOfferDialogWithError();
+    }
 #endif
     current_flow_ = NONE_FLOW;
     return;
diff --git a/components/autofill/core/browser/payments/payments_autofill_client.cc b/components/autofill/core/browser/payments/payments_autofill_client.cc
index 6c7556c..16667fa1 100644
--- a/components/autofill/core/browser/payments/payments_autofill_client.cc
+++ b/components/autofill/core/browser/payments/payments_autofill_client.cc
@@ -45,6 +45,8 @@
 
 void PaymentsAutofillClient::ShowWebauthnVerifyPendingDialog(
     WebauthnDialogCallback verify_pending_dialog_callback) {}
+
+void PaymentsAutofillClient::UpdateWebauthnOfferDialogWithError() {}
 #endif  // BUILDFLAG(IS_ANDROID)
 
 void PaymentsAutofillClient::CreditCardUploadCompleted(
diff --git a/components/autofill/core/browser/payments/payments_autofill_client.h b/components/autofill/core/browser/payments/payments_autofill_client.h
index ac05be7..5347890 100644
--- a/components/autofill/core/browser/payments/payments_autofill_client.h
+++ b/components/autofill/core/browser/payments/payments_autofill_client.h
@@ -134,6 +134,10 @@
   // shown after verification starts only if the WebAuthn is enabled.
   virtual void ShowWebauthnVerifyPendingDialog(
       WebauthnDialogCallback verify_pending_dialog_callback);
+
+  // Will update the WebAuthn dialog content when there is an error fetching the
+  // challenge.
+  virtual void UpdateWebauthnOfferDialogWithError();
 #endif  // BUILDFLAG(IS_ANDROID)
 
   // Shows upload result to users. Called after credit card upload is finished.
diff --git a/components/browser_ui/bottomsheet/android/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetObserverTest.java b/components/browser_ui/bottomsheet/android/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetObserverTest.java
index 416fccc..09bc88b 100644
--- a/components/browser_ui/bottomsheet/android/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetObserverTest.java
+++ b/components/browser_ui/bottomsheet/android/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetObserverTest.java
@@ -30,20 +30,17 @@
 import org.chromium.base.test.BaseJUnit4ClassRunner;
 import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.CallbackHelper;
-import org.chromium.base.test.util.Restriction;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController.StateChangeReason;
 import org.chromium.components.browser_ui.widget.scrim.ScrimCoordinator;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 import org.chromium.ui.KeyboardVisibilityDelegate;
 import org.chromium.ui.test.util.BlankUiTestActivity;
-import org.chromium.ui.test.util.UiRestriction;
 
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeoutException;
 
 /** This class tests the functionality of the {@link BottomSheetObserver}. */
 @RunWith(BaseJUnit4ClassRunner.class)
-@Restriction(UiRestriction.RESTRICTION_TYPE_PHONE) // Bottom sheet is only used on phones.
 @Batch(Batch.PER_CLASS)
 public class BottomSheetObserverTest {
     /** An observer used to record events that occur with respect to the bottom sheet. */
diff --git a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SingleCategorySettings.java b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SingleCategorySettings.java
index cd36af7..cce77fa 100644
--- a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SingleCategorySettings.java
+++ b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SingleCategorySettings.java
@@ -77,8 +77,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.ContentFeatureMap;
 import org.chromium.ui.modaldialog.DialogDismissalCause;
 import org.chromium.ui.modaldialog.ModalDialogManager;
 import org.chromium.ui.modaldialog.ModalDialogManager.ModalDialogType;
@@ -1262,9 +1260,7 @@
         }
 
         // Configure/hide the desktop site window setting, as needed.
-        if (mCategory.getType() == SiteSettingsCategory.Type.REQUEST_DESKTOP_SITE
-                && ContentFeatureMap.isEnabled(
-                        ContentFeatureList.REQUEST_DESKTOP_SITE_WINDOW_SETTING)) {
+        if (mCategory.getType() == SiteSettingsCategory.Type.REQUEST_DESKTOP_SITE) {
             mDesktopSiteWindowPref.setOnPreferenceChangeListener(this);
             updateDesktopSiteWindowSetting();
         } else {
@@ -1459,10 +1455,6 @@
     // TODO(crbug.com/40852484): Looking at a different class setup for SingleCategorySettings that
     // allows category specific logic to live in separate files.
     private void updateDesktopSiteWindowSetting() {
-        if (!ContentFeatureMap.isEnabled(ContentFeatureList.REQUEST_DESKTOP_SITE_WINDOW_SETTING)) {
-            return;
-        }
-
         BrowserContextHandle browserContextHandle =
                 getSiteSettingsDelegate().getBrowserContextHandle();
         Boolean categoryEnabled =
diff --git a/components/commerce/core/compare/cluster_manager_unittest.cc b/components/commerce/core/compare/cluster_manager_unittest.cc
index faf1da6..0d67469 100644
--- a/components/commerce/core/compare/cluster_manager_unittest.cc
+++ b/components/commerce/core/compare/cluster_manager_unittest.cc
@@ -10,6 +10,7 @@
 #include <vector>
 
 #include "base/functional/callback.h"
+#include "base/functional/callback_helpers.h"
 #include "base/run_loop.h"
 #include "base/test/task_environment.h"
 #include "components/commerce/core/commerce_types.h"
@@ -41,7 +42,9 @@
  public:
   explicit MockProductSpecificationsService(
       std::unique_ptr<ProductSpecificationsSyncBridge> bridge)
-      : ProductSpecificationsService(std::move(bridge)) {}
+      : ProductSpecificationsService(
+            base::DoNothing(),
+            std::make_unique<syncer::MockModelTypeChangeProcessor>()) {}
   ~MockProductSpecificationsService() override = default;
 
   MOCK_METHOD(const std::vector<ProductSpecificationsSet>,
@@ -70,7 +73,7 @@
             std::make_unique<ProductSpecificationsSyncBridge>(
                 syncer::ModelTypeStoreTestUtil::FactoryForForwardingStore(
                     store_.get()),
-                processor_.CreateForwardingProcessor()));
+                processor_.CreateForwardingProcessor(), base::DoNothing()));
     EXPECT_CALL(*product_specification_service_, GetAllProductSpecifications())
         .Times(1);
     cluster_manager_ = std::make_unique<ClusterManager>(
diff --git a/components/commerce/core/product_specifications/product_specifications_service.cc b/components/commerce/core/product_specifications/product_specifications_service.cc
index ba51b60f..3bdebe7 100644
--- a/components/commerce/core/product_specifications/product_specifications_service.cc
+++ b/components/commerce/core/product_specifications/product_specifications_service.cc
@@ -4,8 +4,10 @@
 
 #include "components/commerce/core/product_specifications/product_specifications_service.h"
 
+#include <memory>
 #include <optional>
 
+#include "base/functional/bind.h"
 #include "components/commerce/core/product_specifications/product_specifications_set.h"
 #include "components/sync/model/proxy_model_type_controller_delegate.h"
 #include "components/sync/protocol/compare_specifics.pb.h"
@@ -13,8 +15,13 @@
 namespace commerce {
 
 ProductSpecificationsService::ProductSpecificationsService(
-    std::unique_ptr<ProductSpecificationsSyncBridge> bridge)
-    : bridge_(std::move(bridge)) {}
+    syncer::OnceModelTypeStoreFactory create_store_callback,
+    std::unique_ptr<syncer::ModelTypeChangeProcessor> change_processor)
+    : bridge_(std::make_unique<ProductSpecificationsSyncBridge>(
+          std::move(create_store_callback),
+          std::move(change_processor),
+          base::BindOnce(&ProductSpecificationsService::OnInit,
+                         base::Unretained(this)))) {}
 
 ProductSpecificationsService::~ProductSpecificationsService() = default;
 
@@ -40,6 +47,28 @@
   return product_specifications;
 }
 
+void ProductSpecificationsService::GetAllProductSpecifications(
+    GetAllCallback callback) {
+  if (is_initialized_) {
+    base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
+        FROM_HERE,
+        base::BindOnce(std::move(callback), GetAllProductSpecifications()));
+  } else {
+    deferred_operations_.push_back(base::BindOnce(
+        [](base::WeakPtr<ProductSpecificationsService>
+               product_specifications_service,
+           GetAllCallback callback) {
+          if (product_specifications_service) {
+            std::move(callback).Run(product_specifications_service.get()
+                                        ->GetAllProductSpecifications());
+          } else {
+            std::move(callback).Run({});
+          }
+        },
+        weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+  }
+}
+
 const std::optional<ProductSpecificationsSet>
 ProductSpecificationsService::GetSetByUuid(const base::Uuid& uuid) {
   // TODO(b:337263623): Consider centralizing ID logic for product
@@ -121,4 +150,13 @@
   bridge_->RemoveObserver(observer);
 }
 
+void ProductSpecificationsService::OnInit() {
+  is_initialized_ = true;
+  for (base::OnceCallback<void(void)>& deferred_operation :
+       deferred_operations_) {
+    std::move(deferred_operation).Run();
+  }
+  deferred_operations_.clear();
+}
+
 }  // namespace commerce
diff --git a/components/commerce/core/product_specifications/product_specifications_service.h b/components/commerce/core/product_specifications/product_specifications_service.h
index c0b870d..f903b055 100644
--- a/components/commerce/core/product_specifications/product_specifications_service.h
+++ b/components/commerce/core/product_specifications/product_specifications_service.h
@@ -5,19 +5,24 @@
 #ifndef COMPONENTS_COMMERCE_CORE_PRODUCT_SPECIFICATIONS_PRODUCT_SPECIFICATIONS_SERVICE_H_
 #define COMPONENTS_COMMERCE_CORE_PRODUCT_SPECIFICATIONS_PRODUCT_SPECIFICATIONS_SERVICE_H_
 
+#include "base/functional/callback_forward.h"
 #include "base/task/sequenced_task_runner.h"
 #include "components/commerce/core/product_specifications/product_specifications_sync_bridge.h"
 #include "components/keyed_service/core/keyed_service.h"
 
 namespace commerce {
 
+class ProductSpecificationsServiceTest;
 class ProductSpecificationsSet;
 
 // Acquires synced data about product specifications.
 class ProductSpecificationsService : public KeyedService {
  public:
-  explicit ProductSpecificationsService(
-      std::unique_ptr<ProductSpecificationsSyncBridge> bridge);
+  using GetAllCallback =
+      base::OnceCallback<void(const std::vector<ProductSpecificationsSet>)>;
+  ProductSpecificationsService(
+      syncer::OnceModelTypeStoreFactory create_store_callback,
+      std::unique_ptr<syncer::ModelTypeChangeProcessor> change_processor);
   ProductSpecificationsService(const ProductSpecificationsService&) = delete;
   ProductSpecificationsService& operator=(const ProductSpecificationsService&) =
       delete;
@@ -29,6 +34,8 @@
   virtual const std::vector<ProductSpecificationsSet>
   GetAllProductSpecifications();
 
+  virtual void GetAllProductSpecifications(GetAllCallback callback);
+
   virtual const std::optional<ProductSpecificationsSet> GetSetByUuid(
       const base::Uuid& uuid);
 
@@ -62,8 +69,14 @@
   void RemoveObserver(commerce::ProductSpecificationsSet::Observer* observer);
 
  private:
+  friend class commerce::ProductSpecificationsServiceTest;
   std::unique_ptr<ProductSpecificationsSyncBridge> bridge_;
   scoped_refptr<base::SequencedTaskRunner> backend_task_runner_;
+  std::vector<base::OnceCallback<void()>> deferred_operations_;
+  bool is_initialized_;
+
+  void OnInit();
+  base::WeakPtrFactory<ProductSpecificationsService> weak_ptr_factory_{this};
 };
 
 }  // namespace commerce
diff --git a/components/commerce/core/product_specifications/product_specifications_service_unittest.cc b/components/commerce/core/product_specifications/product_specifications_service_unittest.cc
index c657039..3a07813 100644
--- a/components/commerce/core/product_specifications/product_specifications_service_unittest.cc
+++ b/components/commerce/core/product_specifications/product_specifications_service_unittest.cc
@@ -7,6 +7,7 @@
 #include <optional>
 #include <vector>
 
+#include "base/functional/bind.h"
 #include "base/run_loop.h"
 #include "base/strings/stringprintf.h"
 #include "base/task/task_traits.h"
@@ -49,6 +50,25 @@
   return specifics;
 }
 
+void CheckSpecsAgainstSpecifics(
+    const commerce::ProductSpecificationsSet& specifications,
+    const sync_pb::CompareSpecifics& specifics) {
+  EXPECT_EQ(base::Uuid::ParseLowercase(specifics.uuid()),
+            specifications.uuid());
+  EXPECT_EQ(base::Time::FromMillisecondsSinceUnixEpoch(
+                specifics.creation_time_unix_epoch_micros()),
+            specifications.creation_time());
+  EXPECT_EQ(base::Time::FromMillisecondsSinceUnixEpoch(
+                specifics.update_time_unix_epoch_micros()),
+            specifications.update_time());
+  EXPECT_EQ(specifics.name(), specifications.name());
+  std::vector<GURL> urls;
+  for (const sync_pb::ComparisonData& data : specifics.data()) {
+    urls.emplace_back(data.url());
+  }
+  EXPECT_EQ(urls, specifications.urls());
+}
+
 const sync_pb::CompareSpecifics kCompareSpecifics[] = {
     BuildCompareSpecifics("abe18411-bd7e-4819-b9b5-11e66e0ad8b4",
                           1710953277,
@@ -108,52 +128,6 @@
 
 namespace commerce {
 
-class MockProductSpecificationsSyncBridge
-    : public ProductSpecificationsSyncBridge {
- public:
-  MockProductSpecificationsSyncBridge(
-      syncer::OnceModelTypeStoreFactory create_store_callback,
-      std::unique_ptr<syncer::ModelTypeChangeProcessor> change_processor)
-      : ProductSpecificationsSyncBridge(std::move(create_store_callback),
-                                        std::move(change_processor)) {}
-  ~MockProductSpecificationsSyncBridge() override = default;
-
-  MOCK_METHOD(std::unique_ptr<syncer::MetadataChangeList>,
-              CreateMetadataChangeList,
-              (),
-              (override));
-
-  MOCK_METHOD(std::optional<syncer::ModelError>,
-              MergeFullSyncData,
-              (std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
-               syncer::EntityChangeList entity_changes),
-              (override));
-
-  MOCK_METHOD(std::string,
-              GetStorageKey,
-              (const syncer::EntityData& entity_data),
-              (override));
-
-  MOCK_METHOD(std::string,
-              GetClientTag,
-              (const syncer::EntityData& entity_data),
-              (override));
-
-  MOCK_METHOD(void,
-              GetData,
-              (StorageKeyList storage_keys, DataCallback callback),
-              (override));
-
-  MOCK_METHOD(void,
-              GetAllDataForDebugging,
-              (DataCallback callback),
-              (override));
-
-  void AddCompareSpecifics(const sync_pb::CompareSpecifics& compare_specifics) {
-    entries_.emplace(compare_specifics.uuid(), compare_specifics);
-  }
-};
-
 class MockProductSpecificationsSetObserver
     : public ProductSpecificationsSet::Observer {
  public:
@@ -180,20 +154,16 @@
     store_ = syncer::ModelTypeStoreTestUtil::CreateInMemoryStoreForTest();
     ON_CALL(processor_, IsTrackingMetadata())
         .WillByDefault(testing::Return(true));
-    std::unique_ptr<MockProductSpecificationsSyncBridge> bridge =
-        std::make_unique<MockProductSpecificationsSyncBridge>(
-            syncer::ModelTypeStoreTestUtil::FactoryForForwardingStore(store()),
-            change_processor().CreateForwardingProcessor());
-    bridge_ = bridge.get();
-    service_ =
-        std::make_unique<ProductSpecificationsService>(std::move(bridge));
+    service_ = std::make_unique<ProductSpecificationsService>(
+        syncer::ModelTypeStoreTestUtil::FactoryForForwardingStore(store()),
+        change_processor().CreateForwardingProcessor());
     service_->AddObserver(&observer_);
     base::RunLoop().RunUntilIdle();
   }
 
   void TearDown() override { service_->RemoveObserver(&observer_); }
 
-  MockProductSpecificationsSyncBridge* bridge() { return bridge_; }
+  ProductSpecificationsSyncBridge* bridge() { return service_->bridge_.get(); }
 
   ProductSpecificationsService* service() { return service_.get(); }
 
@@ -201,23 +171,19 @@
     return &observer_;
   }
 
-  void CheckSpecsAgainstSpecifics(
-      const ProductSpecificationsSet& specifications,
-      const sync_pb::CompareSpecifics& specifics) const {
-    EXPECT_EQ(base::Uuid::ParseLowercase(specifics.uuid()),
-              specifications.uuid());
-    EXPECT_EQ(base::Time::FromMillisecondsSinceUnixEpoch(
-                  specifics.creation_time_unix_epoch_micros()),
-              specifications.creation_time());
-    EXPECT_EQ(base::Time::FromMillisecondsSinceUnixEpoch(
-                  specifics.update_time_unix_epoch_micros()),
-              specifications.update_time());
-    EXPECT_EQ(specifics.name(), specifications.name());
-    std::vector<GURL> urls;
-    for (const sync_pb::ComparisonData& data : specifics.data()) {
-      urls.emplace_back(data.url());
-    }
-    EXPECT_EQ(urls, specifications.urls());
+  void AddCompareSpecificsForTesting(
+      sync_pb::CompareSpecifics compare_specifics) {
+    bridge()->AddCompareSpecificsForTesting(compare_specifics);
+  }
+
+  void SetIsInitialized(bool is_initialized) {
+    service()->is_initialized_ = is_initialized;
+  }
+
+  void OnInit() { service()->OnInit(); }
+
+  uint64_t GetDeferredOperationsSize() {
+    return service()->deferred_operations_.size();
   }
 
  private:
@@ -237,7 +203,7 @@
 
 TEST_F(ProductSpecificationsServiceTest, TestGetProductSpecifications) {
   for (const sync_pb::CompareSpecifics& specifics : kCompareSpecifics) {
-    bridge()->AddCompareSpecifics(specifics);
+    AddCompareSpecificsForTesting(specifics);
   }
   const std::vector<ProductSpecificationsSet> specifications =
       service()->GetAllProductSpecifications();
@@ -247,6 +213,30 @@
   }
 }
 
+TEST_F(ProductSpecificationsServiceTest, TestGetProductSpecificationsAsync) {
+  SetIsInitialized(false);
+  for (const sync_pb::CompareSpecifics& specifics : kCompareSpecifics) {
+    AddCompareSpecificsForTesting(specifics);
+  }
+  base::RunLoop run_loop;
+
+  service()->GetAllProductSpecifications(base::BindOnce(
+      [](base::OnceClosure done,
+         const std::vector<ProductSpecificationsSet> specifications) {
+        EXPECT_EQ(2u, specifications.size());
+        for (uint64_t i = 0; i < specifications.size(); i++) {
+          CheckSpecsAgainstSpecifics(specifications[i], kCompareSpecifics[i]);
+        }
+
+        std::move(done).Run();
+      },
+      run_loop.QuitClosure()));
+
+  EXPECT_EQ(1u, GetDeferredOperationsSize());
+  OnInit();
+  run_loop.Run();
+}
+
 TEST_F(ProductSpecificationsServiceTest, TestAddProductSpecificationsSuccess) {
   std::vector<GURL> expected_product_urls{GURL(kProductOneUrl),
                                           GURL(kProductTwoUrl)};
@@ -288,7 +278,7 @@
 
 TEST_F(ProductSpecificationsServiceTest, TestSetUrls) {
   for (const sync_pb::CompareSpecifics& specifics : kCompareSpecifics) {
-    bridge()->AddCompareSpecifics(specifics);
+    AddCompareSpecificsForTesting(specifics);
   }
 
   const std::vector<ProductSpecificationsSet> specifications =
@@ -316,7 +306,7 @@
 
 TEST_F(ProductSpecificationsServiceTest, TestSetName) {
   for (const sync_pb::CompareSpecifics& specifics : kCompareSpecifics) {
-    bridge()->AddCompareSpecifics(specifics);
+    AddCompareSpecificsForTesting(specifics);
   }
 
   const std::vector<ProductSpecificationsSet> specifications =
@@ -344,7 +334,7 @@
 
 TEST_F(ProductSpecificationsServiceTest, TestSetNameAndUrls_BadId) {
   for (const sync_pb::CompareSpecifics& specifics : kCompareSpecifics) {
-    bridge()->AddCompareSpecifics(specifics);
+    AddCompareSpecificsForTesting(specifics);
   }
 
   const std::vector<ProductSpecificationsSet> specifications =
diff --git a/components/commerce/core/product_specifications/product_specifications_sync_bridge.cc b/components/commerce/core/product_specifications/product_specifications_sync_bridge.cc
index 659c4e3..df6d4539f 100644
--- a/components/commerce/core/product_specifications/product_specifications_sync_bridge.cc
+++ b/components/commerce/core/product_specifications/product_specifications_sync_bridge.cc
@@ -35,8 +35,10 @@
 
 ProductSpecificationsSyncBridge::ProductSpecificationsSyncBridge(
     syncer::OnceModelTypeStoreFactory create_store_callback,
-    std::unique_ptr<syncer::ModelTypeChangeProcessor> change_processor)
-    : syncer::ModelTypeSyncBridge(std::move(change_processor)) {
+    std::unique_ptr<syncer::ModelTypeChangeProcessor> change_processor,
+    base::OnceCallback<void(void)> init_callback)
+    : syncer::ModelTypeSyncBridge(std::move(change_processor)),
+      init_callback_(std::move(init_callback)) {
   std::move(create_store_callback)
       .Run(syncer::COMPARE,
            base::BindOnce(&ProductSpecificationsSyncBridge::OnStoreCreated,
diff --git a/components/commerce/core/product_specifications/product_specifications_sync_bridge.h b/components/commerce/core/product_specifications/product_specifications_sync_bridge.h
index 2b112ac..4ac5e4d 100644
--- a/components/commerce/core/product_specifications/product_specifications_sync_bridge.h
+++ b/components/commerce/core/product_specifications/product_specifications_sync_bridge.h
@@ -5,6 +5,7 @@
 #ifndef COMPONENTS_COMMERCE_CORE_PRODUCT_SPECIFICATIONS_PRODUCT_SPECIFICATIONS_SYNC_BRIDGE_H_
 #define COMPONENTS_COMMERCE_CORE_PRODUCT_SPECIFICATIONS_PRODUCT_SPECIFICATIONS_SYNC_BRIDGE_H_
 
+#include "base/functional/callback_forward.h"
 #include "base/memory/weak_ptr.h"
 #include "base/observer_list.h"
 #include "components/commerce/core/product_specifications/product_specifications_set.h"
@@ -24,6 +25,7 @@
 
 class MockProductSpecificationsSyncBridge;
 class ProductSpecificationsService;
+class ProductSpecificationsServiceTest;
 class ProductSpecificationsSyncBridgeTest;
 
 // Integration point between sync and ProductSpecificationService.
@@ -31,7 +33,8 @@
  public:
   ProductSpecificationsSyncBridge(
       syncer::OnceModelTypeStoreFactory create_store_callback,
-      std::unique_ptr<syncer::ModelTypeChangeProcessor> change_processor);
+      std::unique_ptr<syncer::ModelTypeChangeProcessor> change_processor,
+      base::OnceCallback<void(void)> init_callback);
   ~ProductSpecificationsSyncBridge() override;
 
   // syncer::ModelTypeSyncBridge:
@@ -51,12 +54,18 @@
  private:
   friend class commerce::MockProductSpecificationsSyncBridge;
   friend class commerce::ProductSpecificationsService;
+  friend class commerce::ProductSpecificationsServiceTest;
   friend class commerce::ProductSpecificationsSyncBridgeTest;
   using CompareSpecificsEntries =
       std::map<std::string, sync_pb::CompareSpecifics>;
 
   const CompareSpecificsEntries& entries() { return entries_; }
 
+  void AddCompareSpecificsForTesting(
+      const sync_pb::CompareSpecifics& compare_specifics) {
+    entries_.emplace(compare_specifics.uuid(), compare_specifics);
+  }
+
   virtual sync_pb::CompareSpecifics AddProductSpecifications(
       const std::string& name,
       const std::vector<GURL>& urls);
@@ -95,6 +104,8 @@
 
   base::ObserverList<commerce::ProductSpecificationsSet::Observer> observers_;
 
+  base::OnceCallback<void(void)> init_callback_;
+
   base::WeakPtrFactory<ProductSpecificationsSyncBridge> weak_ptr_factory_{this};
 };
 
diff --git a/components/commerce/core/product_specifications/product_specifications_sync_bridge_unittest.cc b/components/commerce/core/product_specifications/product_specifications_sync_bridge_unittest.cc
index 5bf9922..ce7d66f 100644
--- a/components/commerce/core/product_specifications/product_specifications_sync_bridge_unittest.cc
+++ b/components/commerce/core/product_specifications/product_specifications_sync_bridge_unittest.cc
@@ -6,6 +6,7 @@
 
 #include <vector>
 
+#include "base/functional/callback_helpers.h"
 #include "base/run_loop.h"
 #include "base/strings/stringprintf.h"
 #include "base/test/bind.h"
@@ -152,7 +153,7 @@
         .WillByDefault(testing::Return(true));
     bridge_ = std::make_unique<ProductSpecificationsSyncBridge>(
         syncer::ModelTypeStoreTestUtil::FactoryForForwardingStore(store_.get()),
-        processor_.CreateForwardingProcessor());
+        processor_.CreateForwardingProcessor(), base::DoNothing());
     base::RunLoop().RunUntilIdle();
     initial_store_ = GetAllStoreData();
     initial_entries_ = entries();
diff --git a/components/commerce/core/webui/shopping_service_handler_unittest.cc b/components/commerce/core/webui/shopping_service_handler_unittest.cc
index 32d76f2..089e1756 100644
--- a/components/commerce/core/webui/shopping_service_handler_unittest.cc
+++ b/components/commerce/core/webui/shopping_service_handler_unittest.cc
@@ -7,6 +7,7 @@
 #include <memory>
 
 #include "base/functional/callback.h"
+#include "base/functional/callback_helpers.h"
 #include "base/memory/raw_ptr.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
@@ -27,6 +28,7 @@
 #include "components/power_bookmarks/core/proto/power_bookmark_meta.pb.h"
 #include "components/power_bookmarks/core/proto/shopping_specifics.pb.h"
 #include "components/prefs/testing_pref_service.h"
+#include "components/sync/test/mock_model_type_change_processor.h"
 #include "components/ukm/test_ukm_recorder.h"
 #include "services/metrics/public/cpp/ukm_builders.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -110,7 +112,10 @@
 
 class MockProductSpecificationsService : public ProductSpecificationsService {
  public:
-  MockProductSpecificationsService() : ProductSpecificationsService(nullptr) {}
+  MockProductSpecificationsService()
+      : ProductSpecificationsService(
+            base::DoNothing(),
+            std::make_unique<syncer::MockModelTypeChangeProcessor>()) {}
   ~MockProductSpecificationsService() override = default;
   MOCK_METHOD(const std::vector<ProductSpecificationsSet>,
               GetAllProductSpecifications,
diff --git a/components/component_updater/installer_policies/tpcd_metadata_component_installer_policy_unittest.cc b/components/component_updater/installer_policies/tpcd_metadata_component_installer_policy_unittest.cc
index b34f883..be5dfee 100644
--- a/components/component_updater/installer_policies/tpcd_metadata_component_installer_policy_unittest.cc
+++ b/components/component_updater/installer_policies/tpcd_metadata_component_installer_policy_unittest.cc
@@ -146,8 +146,10 @@
   const std::string secondary_pattern_spec = "[*.]foo.com";
 
   tpcd::metadata::Metadata metadata;
-  tpcd::metadata::helpers::AddEntryToMetadata(metadata, primary_pattern_spec,
-                                              secondary_pattern_spec);
+  auto* me = metadata.add_metadata_entries();
+  me->set_primary_pattern_spec(primary_pattern_spec);
+  me->set_secondary_pattern_spec(secondary_pattern_spec);
+  me->set_source(Parser::kSourceTest);
   ASSERT_EQ(metadata.metadata_entries_size(), 1);
 
   ExecFakeComponentInstallation(metadata.SerializeAsString());
@@ -170,8 +172,10 @@
   const std::string secondary_pattern_spec = "[*]foo.com";
 
   tpcd::metadata::Metadata metadata;
-  tpcd::metadata::helpers::AddEntryToMetadata(metadata, primary_pattern_spec,
-                                              secondary_pattern_spec);
+  auto* me = metadata.add_metadata_entries();
+  me->set_primary_pattern_spec(primary_pattern_spec);
+  me->set_secondary_pattern_spec(secondary_pattern_spec);
+  me->set_source(Parser::kSourceTest);
   ASSERT_EQ(metadata.metadata_entries_size(), 1);
 
   ExecFakeComponentInstallation(metadata.SerializeAsString());
@@ -184,56 +188,6 @@
       tpcd::metadata::InstallationResult::kErroneousSpec, 1);
 }
 
-TEST_P(TpcdMetadataComponentInstallerPolicyTest,
-       ComponentReady_ErroneousDtrp_Source1pDt) {
-  if (!IsTpcdMetadataGrantsEnabled() || !IsTpcdMetadataStagingEnabled()) {
-    GTEST_SKIP() << "Reason: Test parameters instance N/A";
-  }
-
-  const std::string primary_pattern_spec = "[*.]bar.com";
-  const std::string secondary_pattern_spec = "[*.]foo.com";
-
-  tpcd::metadata::Metadata metadata;
-  tpcd::metadata::helpers::AddEntryToMetadata(
-      metadata, primary_pattern_spec, secondary_pattern_spec,
-      tpcd::metadata::Parser::kSource1pDt);
-  ASSERT_EQ(metadata.metadata_entries_size(), 1);
-
-  ExecFakeComponentInstallation(metadata.SerializeAsString());
-
-  base::HistogramTester histogram_tester;
-  ASSERT_FALSE(
-      policy()->VerifyInstallation(base::Value::Dict(), install_dir()));
-  histogram_tester.ExpectBucketCount(
-      kTpcdMetadataInstallationResult,
-      tpcd::metadata::InstallationResult::kErroneousDtrp, 1);
-}
-
-TEST_P(TpcdMetadataComponentInstallerPolicyTest,
-       ComponentReady_ErroneousDtrp_Source3pDt) {
-  if (!IsTpcdMetadataGrantsEnabled() || !IsTpcdMetadataStagingEnabled()) {
-    GTEST_SKIP() << "Reason: Test parameters instance N/A";
-  }
-
-  const std::string primary_pattern_spec = "[*.]bar.com";
-  const std::string secondary_pattern_spec = "[*.]foo.com";
-
-  tpcd::metadata::Metadata metadata;
-  tpcd::metadata::helpers::AddEntryToMetadata(
-      metadata, primary_pattern_spec, secondary_pattern_spec,
-      tpcd::metadata::Parser::kSource3pDt);
-  ASSERT_EQ(metadata.metadata_entries_size(), 1);
-
-  ExecFakeComponentInstallation(metadata.SerializeAsString());
-
-  base::HistogramTester histogram_tester;
-  ASSERT_FALSE(
-      policy()->VerifyInstallation(base::Value::Dict(), install_dir()));
-  histogram_tester.ExpectBucketCount(
-      kTpcdMetadataInstallationResult,
-      tpcd::metadata::InstallationResult::kErroneousDtrp, 1);
-}
-
 TEST_P(TpcdMetadataComponentInstallerPolicyTest, ComponentReady_ErroneousDtrp) {
   if (!IsTpcdMetadataGrantsEnabled() || !IsTpcdMetadataStagingEnabled()) {
     GTEST_SKIP() << "Reason: Test parameters instance N/A";
@@ -243,45 +197,52 @@
   const std::string secondary_pattern_spec = "[*.]foo.com";
 
   tpcd::metadata::Metadata metadata;
-  tpcd::metadata::helpers::AddEntryToMetadata(
-      metadata, primary_pattern_spec, secondary_pattern_spec,
-      tpcd::metadata::Parser::kSource1pDt,
-      tpcd::metadata::Parser::kMaxDtrp + 1);
+  auto* me = tpcd::metadata::helpers::AddEntryToMetadata(
+      metadata, primary_pattern_spec, secondary_pattern_spec);
   ASSERT_EQ(metadata.metadata_entries_size(), 1);
 
-  ExecFakeComponentInstallation(metadata.SerializeAsString());
+  // Set a valid DTRP override only.
+  {
+    me->set_dtrp_override(tpcd::metadata::Parser::kMaxDtrp);
 
-  base::HistogramTester histogram_tester;
-  ASSERT_FALSE(
-      policy()->VerifyInstallation(base::Value::Dict(), install_dir()));
-  histogram_tester.ExpectBucketCount(
-      kTpcdMetadataInstallationResult,
-      tpcd::metadata::InstallationResult::kErroneousDtrp, 1);
-}
+    ExecFakeComponentInstallation(metadata.SerializeAsString());
 
-TEST_P(TpcdMetadataComponentInstallerPolicyTest, ComponentReady_kIllicitDtrp) {
-  if (!IsTpcdMetadataGrantsEnabled() || !IsTpcdMetadataStagingEnabled()) {
-    GTEST_SKIP() << "Reason: Test parameters instance N/A";
+    base::HistogramTester histogram_tester;
+    EXPECT_FALSE(
+        policy()->VerifyInstallation(base::Value::Dict(), install_dir()));
+    histogram_tester.ExpectBucketCount(
+        kTpcdMetadataInstallationResult,
+        tpcd::metadata::InstallationResult::kErroneousDtrp, 1);
   }
 
-  const std::string primary_pattern_spec = "[*.]bar.com";
-  const std::string secondary_pattern_spec = "[*.]foo.com";
+  // Set an erroneous DTRP.
+  {
+    me->set_dtrp(tpcd::metadata::Parser::kMaxDtrp + 1);
 
-  tpcd::metadata::Metadata metadata;
-  tpcd::metadata::helpers::AddEntryToMetadata(
-      metadata, primary_pattern_spec, secondary_pattern_spec,
-      tpcd::metadata::Parser::kSourceCriticalSector, std::nullopt,
-      tpcd::metadata::Parser::kMaxDtrp);
-  ASSERT_EQ(metadata.metadata_entries_size(), 1);
+    ExecFakeComponentInstallation(metadata.SerializeAsString());
 
-  ExecFakeComponentInstallation(metadata.SerializeAsString());
+    base::HistogramTester histogram_tester;
+    EXPECT_FALSE(
+        policy()->VerifyInstallation(base::Value::Dict(), install_dir()));
+    histogram_tester.ExpectBucketCount(
+        kTpcdMetadataInstallationResult,
+        tpcd::metadata::InstallationResult::kErroneousDtrp, 1);
+  }
 
-  base::HistogramTester histogram_tester;
-  ASSERT_FALSE(
-      policy()->VerifyInstallation(base::Value::Dict(), install_dir()));
-  histogram_tester.ExpectBucketCount(
-      kTpcdMetadataInstallationResult,
-      tpcd::metadata::InstallationResult::kIllicitDtrp, 1);
+  // Set an erroneous DTRP override with a valid DTRP.
+  {
+    me->set_dtrp(tpcd::metadata::Parser::kMaxDtrp);
+    me->set_dtrp_override(tpcd::metadata::Parser::kMaxDtrp + 1);
+
+    ExecFakeComponentInstallation(metadata.SerializeAsString());
+
+    base::HistogramTester histogram_tester;
+    EXPECT_FALSE(
+        policy()->VerifyInstallation(base::Value::Dict(), install_dir()));
+    histogram_tester.ExpectBucketCount(
+        kTpcdMetadataInstallationResult,
+        tpcd::metadata::InstallationResult::kErroneousDtrp, 1);
+  }
 }
 
 TEST_P(TpcdMetadataComponentInstallerPolicyTest, ComponentReady_FiresCallback) {
diff --git a/components/facilitated_payments/content/browser/content_facilitated_payments_driver_factory.cc b/components/facilitated_payments/content/browser/content_facilitated_payments_driver_factory.cc
index 3e8c469..2146cd7b 100644
--- a/components/facilitated_payments/content/browser/content_facilitated_payments_driver_factory.cc
+++ b/components/facilitated_payments/content/browser/content_facilitated_payments_driver_factory.cc
@@ -4,6 +4,7 @@
 
 #include "components/facilitated_payments/content/browser/content_facilitated_payments_driver_factory.h"
 
+#include "base/check_deref.h"
 #include "components/facilitated_payments/core/browser/facilitated_payments_client.h"
 #include "components/facilitated_payments/core/features/features.h"
 #include "content/public/browser/navigation_handle.h"
@@ -17,7 +18,7 @@
         optimization_guide::OptimizationGuideDecider*
             optimization_guide_decider)
     : content::WebContentsObserver(web_contents),
-      client_(*client),
+      client_(CHECK_DEREF(client)),
       optimization_guide_decider_(optimization_guide_decider) {}
 
 ContentFacilitatedPaymentsDriverFactory::
diff --git a/components/facilitated_payments/content/browser/content_facilitated_payments_driver_factory.h b/components/facilitated_payments/content/browser/content_facilitated_payments_driver_factory.h
index 4b7c572..e0efff0 100644
--- a/components/facilitated_payments/content/browser/content_facilitated_payments_driver_factory.h
+++ b/components/facilitated_payments/content/browser/content_facilitated_payments_driver_factory.h
@@ -63,7 +63,7 @@
       driver_map_;
 
   // Owner.
-  raw_ref<FacilitatedPaymentsClient> client_;
+  const raw_ref<FacilitatedPaymentsClient> client_;
 
   // The optimization guide decider to help determine whether the current main
   // frame URL is eligible for facilitated payments.
diff --git a/components/facilitated_payments/core/browser/facilitated_payments_manager.cc b/components/facilitated_payments/core/browser/facilitated_payments_manager.cc
index 25a7eac..6e20b1b 100644
--- a/components/facilitated_payments/core/browser/facilitated_payments_manager.cc
+++ b/components/facilitated_payments/core/browser/facilitated_payments_manager.cc
@@ -8,6 +8,7 @@
 #include <utility>
 
 #include "base/check.h"
+#include "base/check_deref.h"
 #include "base/functional/callback_helpers.h"
 #include "components/autofill/core/browser/data_model/bank_account.h"
 #include "components/autofill/core/browser/payments/payments_util.h"
@@ -25,8 +26,8 @@
     FacilitatedPaymentsClient* client,
     std::unique_ptr<FacilitatedPaymentsApiClient> api_client,
     optimization_guide::OptimizationGuideDecider* optimization_guide_decider)
-    : driver_(*driver),
-      client_(*client),
+    : driver_(CHECK_DEREF(driver)),
+      client_(CHECK_DEREF(client)),
       api_client_(std::move(api_client)),
       optimization_guide_decider_(optimization_guide_decider),
       initiate_payment_request_details_(
diff --git a/components/facilitated_payments/core/browser/facilitated_payments_manager.h b/components/facilitated_payments/core/browser/facilitated_payments_manager.h
index 864e4e1..ca00be80 100644
--- a/components/facilitated_payments/core/browser/facilitated_payments_manager.h
+++ b/components/facilitated_payments/core/browser/facilitated_payments_manager.h
@@ -258,10 +258,10 @@
   void ResetForTesting();
 
   // Owner.
-  raw_ref<FacilitatedPaymentsDriver> driver_;
+  const raw_ref<FacilitatedPaymentsDriver> driver_;
 
   // Indirect owner.
-  raw_ref<FacilitatedPaymentsClient> client_;
+  const raw_ref<FacilitatedPaymentsClient> client_;
 
   // The client for the facilitated payment API.
   std::unique_ptr<FacilitatedPaymentsApiClient> api_client_;
diff --git a/components/history/core/browser/visit_database.cc b/components/history/core/browser/visit_database.cc
index 651c9d4..6b48113 100644
--- a/components/history/core/browser/visit_database.cc
+++ b/components/history/core/browser/visit_database.cc
@@ -299,7 +299,7 @@
 bool VisitDatabase::FillVisitVectorWithOptions(sql::Statement& statement,
                                                const QueryOptions& options,
                                                VisitVector* visits) {
-  std::set<URLID> found_urls;
+  std::map<URLID, VisitRow> found_urls;
 
   // Keeps track of the day that `found_urls` is holding the URLs for, in order
   // to handle removing per-day duplicates.
@@ -320,9 +320,23 @@
         found_urls_midnight = visit.visit_time.LocalMidnight();
       }
       // Make sure the URL this visit corresponds to is unique.
-      if (found_urls.find(visit.url_id) != found_urls.end())
+      auto it = found_urls.find(visit.url_id);
+      if (it != found_urls.end()) {
+#if defined(ANDROID)
+        // The visit with app ID is preferred. Replace the already added visit
+        // with a new one if it doesn't have an app ID but the new one does.
+        VisitRow& ov = it->second;
+        if (!ov.app_id && visit.app_id) {
+          auto is_matched = [ov](VisitRow v) { return ov.url_id == v.url_id; };
+          auto pos = std::find_if(visits->begin(), visits->end(), is_matched);
+          DCHECK(pos != visits->end());
+          *pos = visit;
+          found_urls[visit.url_id] = visit;
+        }
+#endif
         continue;
-      found_urls.insert(visit.url_id);
+      }
+      found_urls[visit.url_id] = visit;
     }
 
     if (static_cast<int>(visits->size()) >= options.EffectiveMaxCount())
diff --git a/components/history/core/browser/visit_database_unittest.cc b/components/history/core/browser/visit_database_unittest.cc
index 3958bfaa..3355682 100644
--- a/components/history/core/browser/visit_database_unittest.cc
+++ b/components/history/core/browser/visit_database_unittest.cc
@@ -395,13 +395,20 @@
     EXPECT_TRUE(AddVisit(&test_visit_rows[i], SOURCE_BROWSED));
   }
 
-  // Query the visits for all time.  We should not get the first or the second
-  // visit (duplicates of the sixth) or the redirect or subframe visits.
+  // Query the visits for all time.
   VisitVector results;
   QueryOptions options;
   GetVisibleVisitsInRange(options, &results);
   ASSERT_EQ(2U, results.size());
+#if !defined(ANDROID)
+  // We should not get the first or the second visit (duplicates of the sixth)
+  // or the redirect or subframe visits.
   EXPECT_TRUE(IsVisitInfoEqual(results[0], test_visit_rows[5]));
+#else
+  // On Android, the one with app_id is chosen among the duplicates.
+  EXPECT_TRUE(IsVisitInfoEqual(results[0], test_visit_rows[1]));
+#endif
+
   EXPECT_TRUE(IsVisitInfoEqual(results[1], test_visit_rows[3]));
 
   // Query the visits with app_id. Only those with the matching app_id will be
@@ -545,15 +552,20 @@
     EXPECT_TRUE(AddVisit(&test_visit_rows[i], SOURCE_BROWSED));
   }
 
-  // Query the visits for the first url id.  We should not get the first, the
-  // second or the sixth (duplicates of the seventh) or any other urls,
-  // redirects or subframe visits.
+  // Query the visits for the first url id.
   VisitVector results;
   QueryOptions options;
   int url_id = test_visit_rows[0].url_id;
   GetVisibleVisitsForURL(url_id, options, &results);
   ASSERT_EQ(1U, results.size());
+#if !defined(ANDROID)
+  // We should not get the first, the second or the sixth (duplicates of the
+  // seventh) or any other urls, redirects or subframe visits.
   EXPECT_TRUE(IsVisitInfoEqual(results[0], test_visit_rows[6]));
+#else
+  // On Android, the one with app_id is chosen among the duplicates.
+  EXPECT_TRUE(IsVisitInfoEqual(results[0], test_visit_rows[5]));
+#endif
 
   // Query the visits with app_id. Only those with the matching both url id
   // (1,2,6,7) and app id(2,3,4,6) will be returned(2, 6) -> 6 (deduped).
diff --git a/components/live_caption/views/caption_bubble.cc b/components/live_caption/views/caption_bubble.cc
index 8a306abe..d3c0991 100644
--- a/components/live_caption/views/caption_bubble.cc
+++ b/components/live_caption/views/caption_bubble.cc
@@ -338,15 +338,16 @@
   METADATA_HEADER(CaptionBubbleLabel, views::Label)
 
  public:
-#if defined(NEED_FOCUS_FOR_ACCESSIBILITY)
   CaptionBubbleLabel() {
+    SetAccessibleRole(ax::mojom::Role::kDocument);
+    SetAccessibleName(std::u16string(),
+                      ax::mojom::NameFrom::kAttributeExplicitlyEmpty);
+#if defined(NEED_FOCUS_FOR_ACCESSIBILITY)
     ax_mode_observer_ =
         std::make_unique<CaptionBubbleLabelAXModeObserver>(this);
     SetFocusBehaviorForAccessibility();
-  }
-#else
-  CaptionBubbleLabel() = default;
 #endif
+  }
   ~CaptionBubbleLabel() override = default;
   CaptionBubbleLabel(const CaptionBubbleLabel&) = delete;
   CaptionBubbleLabel& operator=(const CaptionBubbleLabel&) = delete;
diff --git a/components/media_message_center/media_notification_view_impl_unittest.cc b/components/media_message_center/media_notification_view_impl_unittest.cc
index 5dc0bf5..6c48300 100644
--- a/components/media_message_center/media_notification_view_impl_unittest.cc
+++ b/components/media_message_center/media_notification_view_impl_unittest.cc
@@ -162,9 +162,7 @@
     return GetHeaderRow(view());
   }
 
-  const std::u16string& accessible_name() const {
-    return view()->GetAccessibleName();
-  }
+  std::u16string accessible_name() const { return view()->GetAccessibleName(); }
 
   views::View* button_row() const { return view()->button_row_; }
 
diff --git a/components/omnibox/browser/omnibox_field_trial.h b/components/omnibox/browser/omnibox_field_trial.h
index df302c0..a37aa2e 100644
--- a/components/omnibox/browser/omnibox_field_trial.h
+++ b/components/omnibox/browser/omnibox_field_trial.h
@@ -720,11 +720,6 @@
     false);
 // <- Actions In Suggest
 // ---------------------------------------------------------
-// Android UI Revamp ->
-extern const base::FeatureParam<bool>
-    kOmniboxModernizeVisualUpdateMergeClipboardOnNTP;
-// <- Android UI Revamp
-// ---------------------------------------------------------
 // Touch Down Trigger For Prefetch ->
 extern const base::FeatureParam<int>
     kTouchDownTriggerForPrefetchMaxPrefetchesPerOmniboxSession;
diff --git a/components/optimization_guide/core/model_execution/on_device_model_component.cc b/components/optimization_guide/core/model_execution/on_device_model_component.cc
index 0f979f8..f1ea499 100644
--- a/components/optimization_guide/core/model_execution/on_device_model_component.cc
+++ b/components/optimization_guide/core/model_execution/on_device_model_component.cc
@@ -137,7 +137,6 @@
 void OnDeviceModelComponentStateManager::UninstallComplete() {
   local_state_->ClearPref(
       prefs::localstate::kLastTimeEligibleForOnDeviceModelDownload);
-  UpdateOnDeviceBaseModelSpecCache();
   component_installer_registered_ = false;
 }
 
@@ -180,25 +179,6 @@
   BeginUpdateRegistration();
 }
 
-void OnDeviceModelComponentStateManager::UpdateOnDeviceBaseModelSpecCache() {
-  if (state_ && state_->GetBaseModelSpec()) {
-    local_state_->SetString(prefs::localstate::kOnDeviceBaseModelVersion,
-                            state_->GetBaseModelSpec().value().model_version);
-    local_state_->SetString(prefs::localstate::kOnDeviceBaseModelName,
-                            state_->GetBaseModelSpec().value().model_name);
-  } else {
-    local_state_->ClearPref(prefs::localstate::kOnDeviceBaseModelVersion);
-    local_state_->ClearPref(prefs::localstate::kOnDeviceBaseModelName);
-  }
-}
-
-const std::optional<OnDeviceBaseModelSpec>
-OnDeviceModelComponentStateManager::GetCachedBaseModelSpec() {
-  return OnDeviceBaseModelSpec{
-      local_state_->GetString(prefs::localstate::kOnDeviceBaseModelName),
-      local_state_->GetString(prefs::localstate::kOnDeviceBaseModelVersion)};
-}
-
 void OnDeviceModelComponentStateManager::OnStartup() {
   BeginUpdateRegistration();
 }
@@ -358,7 +338,6 @@
   // Populate the model version and name from the manifest into the Chrome
   // pref cache. If not present, clears the state and cache.
   state_->model_spec_ = ReadBaseModelSpecFromManifest(manifest);
-  UpdateOnDeviceBaseModelSpecCache();
   if (is_model_allowed_) {
     NotifyStateChanged();
   }
diff --git a/components/optimization_guide/core/model_execution/on_device_model_component.h b/components/optimization_guide/core/model_execution/on_device_model_component.h
index 2697e4e4..de15372f 100644
--- a/components/optimization_guide/core/model_execution/on_device_model_component.h
+++ b/components/optimization_guide/core/model_execution/on_device_model_component.h
@@ -97,9 +97,6 @@
   // Returns the current state. Null if the component is not available.
   const OnDeviceModelComponentState* GetState();
 
-  // Returns the last cached BaseModelSpec.
-  const std::optional<OnDeviceBaseModelSpec> GetCachedBaseModelSpec();
-
   void AddObserver(Observer* observer);
   void RemoveObserver(Observer* observer);
 
@@ -155,9 +152,6 @@
 
   void NotifyStateChanged();
 
-  // Should be called whenever the BaseModelSpec changes.
-  void UpdateOnDeviceBaseModelSpecCache();
-
   raw_ptr<PrefService> local_state_;
   std::unique_ptr<Delegate> delegate_;
   base::ObserverList<Observer> observers_;
diff --git a/components/optimization_guide/core/model_execution/on_device_model_component_unittest.cc b/components/optimization_guide/core/model_execution/on_device_model_component_unittest.cc
index 7fc27d7..563576e 100644
--- a/components/optimization_guide/core/model_execution/on_device_model_component_unittest.cc
+++ b/components/optimization_guide/core/model_execution/on_device_model_component_unittest.cc
@@ -503,27 +503,11 @@
   manager()->SetReady(base::Version("0.1.1"),
                       base::FilePath(FILE_PATH_LITERAL("/some/path")),
                       manifest);  // manifest is populated with test data.
-  EXPECT_EQ(manager()->GetCachedBaseModelSpec().value().model_name, "TestXS");
-  EXPECT_EQ(manager()->GetCachedBaseModelSpec().value().model_version,
+  EXPECT_EQ(manager()->GetState()->GetBaseModelSpec().value().model_name,
+            "TestXS");
+  EXPECT_EQ(manager()->GetState()->GetBaseModelSpec().value().model_version,
             "0.0.0.0");
 }
 
-TEST_F(OnDeviceModelComponentTest, UninstallClearsCachedBaseModelSpec) {
-  // This pref records that the model was eligible for download previously,
-  // and hasn't been cleaned up yet.
-  local_state_.SetTime(
-      prefs::localstate::kLastTimeEligibleForOnDeviceModelDownload,
-      base::Time::Now() - base::Minutes(1) -
-          features::GetOnDeviceModelRetentionTime());
-  local_state_.ClearPref(
-      prefs::localstate::kLastTimeOnDeviceEligibleFeatureWasUsed);
-  // Should uninstall on next startup.
-  manager()->OnStartup();
-  WaitForStartup();
-
-  EXPECT_TRUE(on_device_component_state_manager_.WasComponentUninstalled());
-  EXPECT_EQ(manager()->GetCachedBaseModelSpec().value().model_name, "");
-  EXPECT_EQ(manager()->GetCachedBaseModelSpec().value().model_version, "");
-}
 }  // namespace
 }  // namespace optimization_guide
diff --git a/components/optimization_guide/core/model_quality/model_quality_log_entry.cc b/components/optimization_guide/core/model_quality/model_quality_log_entry.cc
index 7a0c87d..e4233de 100644
--- a/components/optimization_guide/core/model_quality/model_quality_log_entry.cc
+++ b/components/optimization_guide/core/model_quality/model_quality_log_entry.cc
@@ -8,14 +8,14 @@
 #include "base/metrics/histogram_functions.h"
 #include "components/optimization_guide/core/model_execution/feature_keys.h"
 #include "components/optimization_guide/core/model_quality/model_quality_util.h"
+
 namespace optimization_guide {
 
 ModelQualityLogEntry::ModelQualityLogEntry(
     std::unique_ptr<proto::LogAiDataRequest> log_ai_data_request,
-    base::WeakPtr<ModelQualityLogsUploaderService>
-        model_quality_uploader_service)
+    base::WeakPtr<ModelQualityLogsUploaderService> uploader)
     : log_ai_data_request_(std::move(log_ai_data_request)),
-      model_quality_uploader_service_(model_quality_uploader_service) {}
+      uploader_(uploader) {}
 
 ModelQualityLogEntry::~ModelQualityLogEntry() {
   // Upload logs if we have reference to the uploader service and the
@@ -23,19 +23,18 @@
   // uploaded in the feature which owns the ModelQualityLogEntry for e.g when
   // chrome is closed.
   bool uploaded_on_destruction = false;
-  if (model_quality_uploader_service_ && log_ai_data_request_) {
+  if (uploader_ && log_ai_data_request_) {
     auto key = GetModelExecutionFeature(log_ai_data_request_->feature_case());
 
-    if (key && model_quality_uploader_service_->CanUploadLogs(*key)) {
+    if (key && uploader_->CanUploadLogs(*key)) {
       // Set the system profile proto before upload. We do that here as we need
       // to access the API on //chrome.
-      model_quality_uploader_service_->SetSystemProfileProto(
-          logging_metadata());
+      uploader_->SetSystemProfileProto(
+          log_ai_data_request_->mutable_logging_metadata());
 
       // We pass the ownership of the LogAiDataRequest to avoid re-uploading the
       // logs.
-      model_quality_uploader_service_->UploadModelQualityLogs(
-          std::move(log_ai_data_request_));
+      uploader_->UploadModelQualityLogs(std::move(log_ai_data_request_));
       uploaded_on_destruction = true;
     }
   }
@@ -44,4 +43,16 @@
       uploaded_on_destruction);
 }
 
+// static
+void ModelQualityLogEntry::Upload(std::unique_ptr<ModelQualityLogEntry> entry) {
+  // Destroying the log entry triggers an upload.
+  entry.reset();
+}
+
+// static
+void ModelQualityLogEntry::Drop(std::unique_ptr<ModelQualityLogEntry> entry) {
+  // Clearing the data results in dropping the log.
+  entry->log_ai_data_request_.reset();
+}
+
 }  // namespace optimization_guide
diff --git a/components/optimization_guide/core/model_quality/model_quality_log_entry.h b/components/optimization_guide/core/model_quality/model_quality_log_entry.h
index b935b5b0..1019573 100644
--- a/components/optimization_guide/core/model_quality/model_quality_log_entry.h
+++ b/components/optimization_guide/core/model_quality/model_quality_log_entry.h
@@ -19,18 +19,18 @@
  public:
   explicit ModelQualityLogEntry(
       std::unique_ptr<proto::LogAiDataRequest> log_ai_data_request,
-      base::WeakPtr<ModelQualityLogsUploaderService>
-          model_quality_uploader_service);
+      base::WeakPtr<ModelQualityLogsUploaderService> uploader);
 
   virtual ~ModelQualityLogEntry();
 
-  proto::LoggingMetadata* logging_metadata() {
-    return log_ai_data_request_->mutable_logging_metadata();
-  }
+  // Uploads the log entry if uploading is allowed; otherwise drops the entry.
+  // Note that the ModelQualityLogsEntry class will upload logs upon
+  // destruction, so most clients can skip calling this method.
+  static void Upload(std::unique_ptr<ModelQualityLogEntry> entry);
 
-  int64_t client_id() const {
-    return log_ai_data_request_->mutable_logging_metadata()->client_id();
-  }
+  // Drops the passed log entry, to ensure that it is never uploaded.
+  // Useful, for example, if the content of the log entry is stale.
+  static void Drop(std::unique_ptr<ModelQualityLogEntry> entry);
 
   template <typename FeatureType>
   FeatureType::Quality* quality_data() {
@@ -61,10 +61,8 @@
   // Holds feature's model execution and quality data.
   std::unique_ptr<proto::LogAiDataRequest> log_ai_data_request_;
 
-  // Reference to ModelQualityLogsUploaderService to upload logs during
-  // destruction if not uploaded.
-  base::WeakPtr<ModelQualityLogsUploaderService>
-      model_quality_uploader_service_;
+  // The ModelQualityLogsUploaderService used to upload logs.
+  base::WeakPtr<ModelQualityLogsUploaderService> uploader_;
 };
 
 }  // namespace optimization_guide
diff --git a/components/optimization_guide/core/model_quality/model_quality_log_entry_unittest.cc b/components/optimization_guide/core/model_quality/model_quality_log_entry_unittest.cc
index aa0c3568..6ea0b652 100644
--- a/components/optimization_guide/core/model_quality/model_quality_log_entry_unittest.cc
+++ b/components/optimization_guide/core/model_quality/model_quality_log_entry_unittest.cc
@@ -2,14 +2,88 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "testing/gtest/include/gtest/gtest.h"
-
 #include "components/optimization_guide/core/model_quality/model_quality_log_entry.h"
+
+#include "base/memory/scoped_refptr.h"
+#include "base/run_loop.h"
+#include "base/test/task_environment.h"
+#include "components/optimization_guide/core/model_quality/model_quality_logs_uploader_service.h"
+#include "components/optimization_guide/core/optimization_guide_prefs.h"
 #include "components/optimization_guide/proto/model_quality_service.pb.h"
+#include "components/prefs/testing_pref_service.h"
+#include "components/variations/scoped_variations_ids_provider.h"
+#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
+#include "services/network/test/test_url_loader_factory.h"
+#include "testing/gtest/include/gtest/gtest.h"
 
 namespace optimization_guide {
 
-class ModelQualityLogEntryTest : public testing::Test {};
+namespace {
+
+std::unique_ptr<proto::LogAiDataRequest> BuildComposeLogAiDataRequest() {
+  auto log = std::make_unique<proto::LogAiDataRequest>();
+  proto::ComposeLoggingData compose_data;
+  *(log->mutable_compose()) = compose_data;
+  return log;
+}
+
+class TestModelQualityLogsUploaderService
+    : public ModelQualityLogsUploaderService {
+ public:
+  explicit TestModelQualityLogsUploaderService(
+      network::TestURLLoaderFactory* url_loader_factory)
+      : ModelQualityLogsUploaderService(
+            base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
+                url_loader_factory),
+            &prefs_) {
+    prefs::RegisterLocalStatePrefs(prefs_.registry());
+  }
+
+  ~TestModelQualityLogsUploaderService() override = default;
+
+  TestModelQualityLogsUploaderService(
+      const TestModelQualityLogsUploaderService&) = delete;
+  TestModelQualityLogsUploaderService& operator=(
+      const TestModelQualityLogsUploaderService&) = delete;
+
+  bool CanUploadLogs(UserVisibleFeatureKey feature) override { return true; }
+
+ private:
+  TestingPrefServiceSimple prefs_;
+};
+
+}  // namespace
+
+class ModelQualityLogEntryTest : public testing::Test {
+ public:
+  ModelQualityLogEntryTest()
+      : task_environment_(base::test::TaskEnvironment::MainThreadType::UI,
+                          base::test::TaskEnvironment::TimeSource::MOCK_TIME),
+        uploader_(&url_loader_factory_) {}
+  ~ModelQualityLogEntryTest() override = default;
+
+  base::WeakPtr<ModelQualityLogsUploaderService> uploader() {
+    return uploader_.GetWeakPtr();
+  }
+
+  int NumPendingUploads() {
+    RunUntilIdle();
+    return url_loader_factory_.NumPending();
+  }
+
+ private:
+  void RunUntilIdle() {
+    task_environment_.RunUntilIdle();
+    base::RunLoop().RunUntilIdle();
+  }
+
+  base::test::TaskEnvironment task_environment_;
+  variations::ScopedVariationsIdsProvider scoped_variations_ids_provider_{
+      variations::VariationsIdsProvider::Mode::kUseSignedInState};
+
+  network::TestURLLoaderFactory url_loader_factory_;
+  TestModelQualityLogsUploaderService uploader_;
+};
 
 // Test ModelQualityLogEntry initialization and logging_metadata().
 TEST_F(ModelQualityLogEntryTest, Initialize) {
@@ -20,7 +94,8 @@
 
   ModelQualityLogEntry log_entry(std::move(log_ai_data_request), nullptr);
 
-  EXPECT_EQ(log_entry.logging_metadata(), logging_metadata);
+  EXPECT_EQ(log_entry.log_ai_data_request()->mutable_logging_metadata(),
+            logging_metadata);
 }
 
 // Test client id is correctly set.
@@ -32,7 +107,49 @@
 
   ModelQualityLogEntry log_entry(std::move(log_ai_data_request), nullptr);
 
-  EXPECT_EQ(log_entry.client_id(), client_id);
+  EXPECT_EQ(
+      log_entry.log_ai_data_request()->mutable_logging_metadata()->client_id(),
+      client_id);
+}
+
+TEST_F(ModelQualityLogEntryTest, UploadOnDestruction) {
+  {
+    auto data = BuildComposeLogAiDataRequest();
+    auto log =
+        std::make_unique<ModelQualityLogEntry>(std::move(data), uploader());
+  }  // ModelQualityLogEntry destroyed, this should trigger an upload.
+
+  EXPECT_EQ(1, NumPendingUploads());
+}
+
+TEST_F(ModelQualityLogEntryTest, Upload_WithNonEmptyLog_SchedulesAnUpload) {
+  auto data = BuildComposeLogAiDataRequest();
+  auto log =
+      std::make_unique<ModelQualityLogEntry>(std::move(data), uploader());
+
+  ModelQualityLogEntry::Upload(std::move(log));
+
+  EXPECT_EQ(1, NumPendingUploads());
+}
+
+TEST_F(ModelQualityLogEntryTest, Upload_WithEmptyLog_DoesNotScheduleAnUpload) {
+  std::unique_ptr<proto::LogAiDataRequest> data;
+  auto log =
+      std::make_unique<ModelQualityLogEntry>(std::move(data), uploader());
+
+  ModelQualityLogEntry::Upload(std::move(log));
+
+  EXPECT_EQ(0, NumPendingUploads());
+}
+
+TEST_F(ModelQualityLogEntryTest, Drop_WithNonEmptyLog_DoesNotScheduleAnUpload) {
+  auto data = BuildComposeLogAiDataRequest();
+  auto log =
+      std::make_unique<ModelQualityLogEntry>(std::move(data), uploader());
+
+  ModelQualityLogEntry::Drop(std::move(log));
+
+  EXPECT_EQ(0, NumPendingUploads());
 }
 
 }  // namespace optimization_guide
diff --git a/components/optimization_guide/core/optimization_guide_prefs.cc b/components/optimization_guide/core/optimization_guide_prefs.cc
index 960cf46..8e65c74 100644
--- a/components/optimization_guide/core/optimization_guide_prefs.cc
+++ b/components/optimization_guide/core/optimization_guide_prefs.cc
@@ -121,14 +121,6 @@
 const char kOnDevicePerformanceClass[] =
     "optimization_guide.on_device.performance_class";
 
-// Stores the version of the last downloaded on-device model.
-const char kOnDeviceBaseModelVersion[] =
-    "optimization_guide.on_device.base_model_version";
-
-// Stores the name of the last downloaded on-device model.
-const char kOnDeviceBaseModelName[] =
-    "optimization_guide.on_device.base_model_name";
-
 // A dictionary pref that stores the file paths that need to be deleted as keys.
 // The value will not be used.
 const char kStoreFilePathsToDelete[] =
@@ -189,10 +181,6 @@
   registry->RegisterIntegerPref(localstate::kOnDeviceModelCrashCount, 0);
   registry->RegisterIntegerPref(localstate::kOnDeviceModelTimeoutCount, 0);
   registry->RegisterIntegerPref(localstate::kOnDevicePerformanceClass, 0);
-  registry->RegisterStringPref(localstate::kOnDeviceBaseModelVersion,
-                               std::string());
-  registry->RegisterStringPref(localstate::kOnDeviceBaseModelName,
-                               std::string());
   registry->RegisterDictionaryPref(localstate::kStoreFilePathsToDelete);
   registry->RegisterTimePref(
       localstate::kLastTimeOnDeviceEligibleFeatureWasUsed, base::Time::Min());
diff --git a/components/optimization_guide/core/optimization_guide_prefs.h b/components/optimization_guide/core/optimization_guide_prefs.h
index 3ab7dcb..58b27089 100644
--- a/components/optimization_guide/core/optimization_guide_prefs.h
+++ b/components/optimization_guide/core/optimization_guide_prefs.h
@@ -75,10 +75,6 @@
 COMPONENT_EXPORT(OPTIMIZATION_GUIDE_FEATURES)
 extern const char kOnDevicePerformanceClass[];
 COMPONENT_EXPORT(OPTIMIZATION_GUIDE_FEATURES)
-extern const char kOnDeviceBaseModelVersion[];
-COMPONENT_EXPORT(OPTIMIZATION_GUIDE_FEATURES)
-extern const char kOnDeviceBaseModelName[];
-COMPONENT_EXPORT(OPTIMIZATION_GUIDE_FEATURES)
 extern const char kStoreFilePathsToDelete[];
 COMPONENT_EXPORT(OPTIMIZATION_GUIDE_FEATURES)
 extern const char kLastTimeOnDeviceEligibleFeatureWasUsed[];
diff --git a/components/password_manager/core/browser/fake_form_fetcher.cc b/components/password_manager/core/browser/fake_form_fetcher.cc
index 286eeb5..a100d580 100644
--- a/components/password_manager/core/browser/fake_form_fetcher.cc
+++ b/components/password_manager/core/browser/fake_form_fetcher.cc
@@ -39,8 +39,7 @@
   return stats_;
 }
 
-std::vector<raw_ptr<const PasswordForm, VectorExperimental>>
-FakeFormFetcher::GetInsecureCredentials() const {
+base::span<const PasswordForm> FakeFormFetcher::GetInsecureCredentials() const {
   return insecure_credentials_;
 }
 
diff --git a/components/password_manager/core/browser/fake_form_fetcher.h b/components/password_manager/core/browser/fake_form_fetcher.h
index cd478ebf..c518041 100644
--- a/components/password_manager/core/browser/fake_form_fetcher.h
+++ b/components/password_manager/core/browser/fake_form_fetcher.h
@@ -44,8 +44,7 @@
   State GetState() const override;
 
   const std::vector<InteractionsStats>& GetInteractionsStats() const override;
-  std::vector<raw_ptr<const PasswordForm, VectorExperimental>>
-  GetInsecureCredentials() const override;
+  base::span<const PasswordForm> GetInsecureCredentials() const override;
   std::vector<raw_ptr<const PasswordForm, VectorExperimental>>
   GetNonFederatedMatches() const override;
   std::vector<raw_ptr<const PasswordForm, VectorExperimental>>
@@ -79,9 +78,7 @@
     federated_ = federated;
   }
 
-  void set_insecure_credentials(
-      const std::vector<raw_ptr<const PasswordForm, VectorExperimental>>&
-          credentials) {
+  void set_insecure_credentials(const std::vector<PasswordForm>& credentials) {
     insecure_credentials_ = credentials;
   }
 
@@ -109,8 +106,7 @@
   std::vector<raw_ptr<const PasswordForm, VectorExperimental>>
       non_federated_same_scheme_;
   std::vector<PasswordForm> best_matches_;
-  std::vector<raw_ptr<const PasswordForm, VectorExperimental>>
-      insecure_credentials_;
+  std::vector<PasswordForm> insecure_credentials_;
   bool is_blocklisted_ = false;
   std::optional<PasswordStoreBackendError> profile_store_backend_error_;
   std::optional<PasswordStoreBackendError> account_store_backend_error_;
diff --git a/components/password_manager/core/browser/form_fetcher.h b/components/password_manager/core/browser/form_fetcher.h
index fa9ea18..4a63a593 100644
--- a/components/password_manager/core/browser/form_fetcher.h
+++ b/components/password_manager/core/browser/form_fetcher.h
@@ -9,6 +9,7 @@
 #include <string>
 #include <vector>
 
+#include "base/containers/span.h"
 #include "base/memory/raw_ptr.h"
 #include "base/observer_list_types.h"
 #include "components/password_manager/core/browser/password_form.h"
@@ -69,10 +70,7 @@
       const = 0;
 
   // Returns all PasswordForm entries that have insecure features.
-  // Do not store the result of this call. The pointers become invalid if `this`
-  // receives new results from a password store.
-  virtual std::vector<raw_ptr<const PasswordForm, VectorExperimental>>
-  GetInsecureCredentials() const = 0;
+  virtual base::span<const PasswordForm> GetInsecureCredentials() const = 0;
 
   // Non-federated matches obtained from the backend.
   // Do not store the result of this call. The pointers become invalid if `this`
diff --git a/components/password_manager/core/browser/form_fetcher_impl.cc b/components/password_manager/core/browser/form_fetcher_impl.cc
index 8875823..285c13a 100644
--- a/components/password_manager/core/browser/form_fetcher_impl.cc
+++ b/components/password_manager/core/browser/form_fetcher_impl.cc
@@ -85,8 +85,9 @@
 void FormFetcherImpl::AddConsumer(FormFetcher::Consumer* consumer) {
   DCHECK(consumer);
   consumers_.AddObserver(consumer);
-  if (state_ == State::NOT_WAITING)
+  if (state_ == State::NOT_WAITING) {
     consumer->OnFetchCompleted();
+  }
 }
 
 void FormFetcherImpl::RemoveConsumer(FormFetcher::Consumer* consumer) {
@@ -124,8 +125,9 @@
   PasswordStoreInterface* profile_password_store =
       client_->GetProfilePasswordStore();
   if (!profile_password_store) {
-    if (logger)
+    if (logger) {
       logger->LogMessage(Logger::STRING_NO_STORE);
+    }
 
     std::vector<std::unique_ptr<PasswordForm>> results;
     AggregatePasswordStoreResults(std::move(results));
@@ -134,14 +136,16 @@
 
   PasswordStoreInterface* account_password_store =
       client_->GetAccountPasswordStore();
-  if (account_password_store)
+  if (account_password_store) {
     wait_counter_++;
+  }
 
   profile_password_store->GetLogins(form_digest_,
                                     weak_ptr_factory_.GetWeakPtr());
-  if (account_password_store)
+  if (account_password_store) {
     account_password_store->GetLogins(form_digest_,
                                       weak_ptr_factory_.GetWeakPtr());
+  }
 
 // The statistics isn't needed on mobile, only on desktop. Let's save some
 // processor cycles.
@@ -150,9 +154,10 @@
   password_manager::SmartBubbleStatsStore* stats_store =
       profile_password_store->GetSmartBubbleStatsStore();
   // `stats_store` can be null in tests.
-  if (stats_store)
+  if (stats_store) {
     stats_store->GetSiteStats(form_digest_.url.DeprecatedGetOriginAsURL(),
                               weak_ptr_factory_.GetWeakPtr());
+  }
 #endif
 }
 
@@ -165,9 +170,8 @@
   return interactions_stats_;
 }
 
-std::vector<raw_ptr<const PasswordForm, VectorExperimental>>
-FormFetcherImpl::GetInsecureCredentials() const {
-  return MakeWeakCopies(insecure_credentials_);
+base::span<const PasswordForm> FormFetcherImpl::GetInsecureCredentials() const {
+  return insecure_credentials_;
 }
 
 std::vector<raw_ptr<const PasswordForm, VectorExperimental>>
@@ -197,18 +201,21 @@
       // Only local entries can be moved to the account store (though
       // account store matches should never have |moving_blocked_for_list|
       // entries anyway).
-      if (form->IsUsingAccountStore())
+      if (form->IsUsingAccountStore()) {
         continue;
+      }
       // Ignore non-exact matches for blocking moving. PLS, affiliated and
       // grouped matches are ignored.
       if (GetMatchType(*form) !=
           password_manager_util::GetLoginMatchType::kExact) {
         continue;
       }
-      if (form->username_value != username)
+      if (form->username_value != username) {
         continue;
-      if (base::Contains(form->moving_blocked_for_list, destination))
+      }
+      if (base::Contains(form->moving_blocked_for_list, destination)) {
         return true;
+      }
     }
   }
   return false;
@@ -250,7 +257,7 @@
       &result->non_federated_same_scheme_);
 
   result->interactions_stats_ = interactions_stats_;
-  result->insecure_credentials_ = MakeCopies(insecure_credentials_);
+  result->insecure_credentials_ = insecure_credentials_;
   result->state_ = state_;
   result->need_to_refetch_ = need_to_refetch_;
   result->profile_store_backend_error_ = profile_store_backend_error_;
@@ -282,8 +289,9 @@
       &non_federated_same_scheme_);
 
   state_ = State::NOT_WAITING;
-  for (auto& consumer : consumers_)
+  for (auto& consumer : consumers_) {
     consumer.OnFetchCompleted();
+  }
 }
 
 void FormFetcherImpl::SplitResults(
@@ -300,14 +308,16 @@
       if (password_manager_util::GetMatchType(*form) ==
               password_manager_util::GetLoginMatchType::kExact &&
           form->scheme == form_digest_.scheme) {
-        if (form->IsUsingAccountStore())
+        if (form->IsUsingAccountStore()) {
           is_blocklisted_in_account_store_ = true;
-        else
+        } else {
           is_blocklisted_in_profile_store_ = true;
+        }
       }
     } else {
-      if (!form->password_issues.empty())
-        insecure_credentials_.push_back(std::make_unique<PasswordForm>(*form));
+      if (!form->password_issues.empty()) {
+        insecure_credentials_.push_back(*form);
+      }
       if (form->IsFederatedCredential()) {
         federated_.push_back(std::move(form));
       } else {
@@ -382,12 +392,14 @@
 void FormFetcherImpl::AggregatePasswordStoreResults(
     std::vector<std::unique_ptr<PasswordForm>> results) {
   // Store the results.
-  for (auto& form : results)
+  for (auto& form : results) {
     partial_results_.push_back(std::move(form));
+  }
 
   // If we're still awaiting more results, nothing else to do.
-  if (--wait_counter_ > 0)
+  if (--wait_counter_ > 0) {
     return;
+  }
 
   if (need_to_refetch_) {
     // The received results are no longer up to date, need to re-request.
diff --git a/components/password_manager/core/browser/form_fetcher_impl.h b/components/password_manager/core/browser/form_fetcher_impl.h
index 4cc62d1..2a3f95edd 100644
--- a/components/password_manager/core/browser/form_fetcher_impl.h
+++ b/components/password_manager/core/browser/form_fetcher_impl.h
@@ -49,8 +49,7 @@
   void Fetch() override;
   State GetState() const override;
   const std::vector<InteractionsStats>& GetInteractionsStats() const override;
-  std::vector<raw_ptr<const PasswordForm, VectorExperimental>>
-  GetInsecureCredentials() const override;
+  base::span<const PasswordForm> GetInsecureCredentials() const override;
   std::vector<raw_ptr<const PasswordForm, VectorExperimental>>
   GetNonFederatedMatches() const override;
   std::vector<raw_ptr<const PasswordForm, VectorExperimental>>
@@ -105,7 +104,7 @@
   std::vector<std::unique_ptr<PasswordForm>> federated_;
 
   // List of insecure credentials for the current domain.
-  std::vector<std::unique_ptr<PasswordForm>> insecure_credentials_;
+  std::vector<PasswordForm> insecure_credentials_;
 
   // Indicates whether HTTP passwords should be migrated to HTTPS. This is
   // always false for non HTML forms.
diff --git a/components/password_manager/core/browser/form_fetcher_impl_unittest.cc b/components/password_manager/core/browser/form_fetcher_impl_unittest.cc
index 294f8514..3a43158 100644
--- a/components/password_manager/core/browser/form_fetcher_impl_unittest.cc
+++ b/components/password_manager/core/browser/form_fetcher_impl_unittest.cc
@@ -307,9 +307,8 @@
     }
   }
 
-  void DeliverPasswordStoreResults(
-      LoginsResultOrError profile_store_results,
-      LoginsResultOrError account_store_results) {
+  void DeliverPasswordStoreResults(LoginsResultOrError profile_store_results,
+                                   LoginsResultOrError account_store_results) {
     store_consumer()->OnGetPasswordStoreResultsOrErrorFrom(
         profile_mock_store_.get(), std::move(profile_store_results));
     if (account_mock_store_) {
@@ -561,9 +560,9 @@
   std::vector<PasswordForm> results = {form};
   DeliverPasswordStoreResults(/*profile_store_results=*/std::move(results),
                               /*account_store_results=*/{});
-  EXPECT_THAT(form_fetcher_->GetInsecureCredentials(),
-              UnorderedElementsAre(
-                  Pointee(CreateLeakedCredential(form, leaked_metadata))));
+  EXPECT_THAT(
+      form_fetcher_->GetInsecureCredentials(),
+      UnorderedElementsAre(CreateLeakedCredential(form, leaked_metadata)));
 }
 
 // Test that multiple calls of Fetch() are handled gracefully, and that they
@@ -947,7 +946,7 @@
       form_fetcher_->GetFederatedMatches(),
       UnorderedElementsAre(Pointee(federated), Pointee(android_federated)));
   EXPECT_THAT(form_fetcher_->GetInsecureCredentials(),
-              UnorderedElementsAre(Pointee(federated)));
+              UnorderedElementsAre(federated));
   EXPECT_FALSE(form_fetcher_->IsBlocklisted());
 
   ASSERT_TRUE(
@@ -968,8 +967,7 @@
   EXPECT_THAT(
       clone->GetFederatedMatches(),
       UnorderedElementsAre(Pointee(federated), Pointee(android_federated)));
-  EXPECT_THAT(clone->GetInsecureCredentials(),
-              UnorderedElementsAre(Pointee(federated)));
+  EXPECT_THAT(clone->GetInsecureCredentials(), UnorderedElementsAre(federated));
   MockConsumer consumer;
   EXPECT_CALL(consumer, OnFetchCompleted);
   clone->AddConsumer(&consumer);
@@ -1001,8 +999,7 @@
                               /*account_store_results=*/{});
 
   auto clone = form_fetcher_->Clone();
-  EXPECT_THAT(clone->GetInsecureCredentials(),
-              UnorderedElementsAre(Pointee(form)));
+  EXPECT_THAT(clone->GetInsecureCredentials(), UnorderedElementsAre(form));
 }
 
 // Check that removing consumers stops them from receiving store updates.
@@ -1294,10 +1291,9 @@
   DeliverPasswordStoreResults(std::move(profile_results),
                               std::move(account_results));
 
-  EXPECT_THAT(
-      form_fetcher_->GetInsecureCredentials(),
-      testing::UnorderedElementsAre(Pointee(profile_form_insecure_credential),
-                                    Pointee(account_form_insecure_credential)));
+  EXPECT_THAT(form_fetcher_->GetInsecureCredentials(),
+              testing::UnorderedElementsAre(profile_form_insecure_credential,
+                                            account_form_insecure_credential));
 }
 
 TEST_P(FormFetcherImplTest, ProfileBackendErrorResetsOnNewFetch) {
diff --git a/components/password_manager/core/browser/mock_password_form_manager_for_ui.h b/components/password_manager/core/browser/mock_password_form_manager_for_ui.h
index d1cb31da..4d1cbbb9 100644
--- a/components/password_manager/core/browser/mock_password_form_manager_for_ui.h
+++ b/components/password_manager/core/browser/mock_password_form_manager_for_ui.h
@@ -41,7 +41,7 @@
               GetInteractionsStats,
               (),
               (const override));
-  MOCK_METHOD((std::vector<raw_ptr<const PasswordForm, VectorExperimental>>),
+  MOCK_METHOD((base::span<const PasswordForm>),
               GetInsecureCredentials,
               (),
               (const override));
diff --git a/components/password_manager/core/browser/password_form_manager.cc b/components/password_manager/core/browser/password_form_manager.cc
index fe768de4..f51964c 100644
--- a/components/password_manager/core/browser/password_form_manager.cc
+++ b/components/password_manager/core/browser/password_form_manager.cc
@@ -88,8 +88,9 @@
 
 bool FormContainsFieldWithName(const FormData& form,
                                const std::u16string& element) {
-  if (element.empty())
+  if (element.empty()) {
     return false;
+  }
 
   auto equals_element_case_insensitive =
       [&element](const std::u16string& name) {
@@ -102,8 +103,9 @@
 void LogUsingPossibleUsername(PasswordManagerClient* client,
                               bool is_used,
                               const char* message) {
-  if (!password_manager_util::IsLoggingActive(client))
+  if (!password_manager_util::IsLoggingActive(client)) {
     return;
+  }
   BrowserSavePasswordProgressLogger logger(client->GetLogManager());
   logger.LogString(is_used ? Logger::STRING_POSSIBLE_USERNAME_USED
                            : Logger::STRING_POSSIBLE_USERNAME_NOT_USED,
@@ -286,8 +288,9 @@
                           form_fetcher,
                           std::move(password_save_manager),
                           nullptr /* metrics_recorder */) {
-  if (owned_form_fetcher_)
+  if (owned_form_fetcher_) {
     owned_form_fetcher_->Fetch();
+  }
 }
 
 PasswordFormManager::~PasswordFormManager() {
@@ -297,8 +300,9 @@
 bool PasswordFormManager::DoesManage(
     autofill::FormRendererId form_renderer_id,
     const PasswordManagerDriver* driver) const {
-  if (driver != driver_.get())
+  if (driver != driver_.get()) {
     return false;
+  }
   CHECK(observed_form());
   return observed_form()->renderer_id == form_renderer_id;
 }
@@ -319,10 +323,12 @@
 
 bool PasswordFormManager::IsEqualToSubmittedForm(
     const autofill::FormData& form) const {
-  if (!is_submitted_)
+  if (!is_submitted_) {
     return false;
-  if (IsHttpAuth())
+  }
+  if (IsHttpAuth()) {
     return false;
+  }
 
   if (form.action.is_valid() && !form.is_action_empty &&
       !submitted_form_.is_action_empty &&
@@ -381,8 +387,8 @@
   return base::make_span(form_fetcher_->GetInteractionsStats());
 }
 
-std::vector<raw_ptr<const PasswordForm, VectorExperimental>>
-PasswordFormManager::GetInsecureCredentials() const {
+base::span<const PasswordForm> PasswordFormManager::GetInsecureCredentials()
+    const {
   return form_fetcher_->GetInsecureCredentials();
 }
 
@@ -697,12 +703,14 @@
       mutable_observed_form()->fields, [&field_id](const FormFieldData& field) {
         return field.renderer_id() == field_id;
       });
-  if (modified_field == mutable_observed_form()->fields.end())
+  if (modified_field == mutable_observed_form()->fields.end()) {
     return;
+  }
   modified_field->set_value(field_value);
 
-  if (!HasGeneratedPassword())
+  if (!HasGeneratedPassword()) {
     return;
+  }
   // Update the presaved password form. Even if generated password was not
   // modified, the user might have modified the username.
   std::u16string generated_password =
@@ -728,8 +736,9 @@
   bool data_found = false;
   for (FormFieldData& field : mutable_observed_form()->fields) {
     FieldRendererId field_id = field.renderer_id();
-    if (!field_data_manager.HasFieldData(field_id))
+    if (!field_data_manager.HasFieldData(field_id)) {
       continue;
+    }
     field.set_user_input(field_data_manager.GetUserInput(field_id));
     field.set_properties_mask(
         field_data_manager.GetFieldPropertiesMask(field_id));
@@ -738,8 +747,9 @@
 
   // Provisionally save form and set the manager to be submitted if valid
   // data was recovered.
-  if (data_found)
+  if (data_found) {
     ProvisionallySave(*observed_form(), driver, possible_usernames);
+  }
 }
 
 bool PasswordFormManager::AreRemovedUnownedFieldsValidForSubmissionDetection(
@@ -806,8 +816,9 @@
   //       cloned FormFetcher.
   result->votes_uploader_ = votes_uploader_;
 
-  if (parser_.predictions())
+  if (parser_.predictions()) {
     result->parser_.set_predictions(*parser_.predictions());
+  }
 
   if (parsed_submitted_form_) {
     result->parsed_submitted_form_ =
@@ -891,8 +902,9 @@
                                  form_fetcher_->GetBestMatches(),
                                  form_fetcher_->IsBlocklisted());
 
-  if (is_submitted_)
+  if (is_submitted_) {
     CreatePendingCredentials();
+  }
 
   if (IsHttpAuth()) {
     // No server prediction for http auth, so no need to wait.
@@ -949,8 +961,9 @@
 
 void PasswordFormManager::CreatePendingCredentials() {
   DCHECK(is_submitted_);
-  if (!parsed_submitted_form_)
+  if (!parsed_submitted_form_) {
     return;
+  }
 
   password_save_manager_->CreatePendingCredentials(
       *parsed_submitted_form_, observed_form(), submitted_form_, IsHttpAuth(),
@@ -1028,8 +1041,9 @@
 
 bool PasswordFormManager::ProvisionallySaveHttpAuthForm(
     const PasswordForm& submitted_form) {
-  if (!IsHttpAuth())
+  if (!IsHttpAuth()) {
     return false;
+  }
   CHECK(observed_digest());
   if (*observed_digest() != PasswordFormDigest(submitted_form)) {
     return false;
@@ -1086,14 +1100,17 @@
 }
 
 void PasswordFormManager::FillNow() {
-  if (!driver_)
+  if (!driver_) {
     return;
+  }
 
-  if (form_fetcher_->GetState() == FormFetcher::State::WAITING)
+  if (form_fetcher_->GetState() == FormFetcher::State::WAITING) {
     return;
+  }
 
-  if (autofills_left_ <= 0)
+  if (autofills_left_ <= 0) {
     return;
+  }
   autofills_left_--;
 
   // There are additional signals (server-side data) and parse results in
@@ -1161,8 +1178,9 @@
   auto it = base::ranges::find(form_data.fields, generation_element_id,
                                &FormFieldData::renderer_id);
   // The parameters are coming from the renderer and can't be trusted.
-  if (it == form_data.fields.end())
+  if (it == form_data.fields.end()) {
     return;
+  }
   it->set_value(password);
   auto [parsed_form, username_detection_method] =
       ParseFormAndMakeLogging(form_data, FormDataParser::Mode::kSaving);
@@ -1257,8 +1275,9 @@
 
 void PasswordFormManager::FillHttpAuth() {
   DCHECK(IsHttpAuth());
-  if (!form_fetcher_->GetPreferredMatch())
+  if (!form_fetcher_->GetPreferredMatch()) {
     return;
+  }
   client_->AutofillHttpAuth(*form_fetcher_->GetPreferredMatch(), this);
 }
 
@@ -1308,8 +1327,9 @@
        form_fetcher_->GetNonFederatedMatches()) {
     // Saved credentials might have empty usernames which are not interesting
     // for filling assistance metric.
-    if (!saved_form->username_value.empty())
+    if (!saved_form->username_value.empty()) {
       saved_usernames.emplace(saved_form->username_value, saved_form->in_store);
+    }
     saved_passwords.emplace(saved_form->password_value, saved_form->in_store);
   }
 
@@ -1321,8 +1341,9 @@
 }
 
 void PasswordFormManager::CalculateSubmittedFormFrameMetric() {
-  if (!driver_)
+  if (!driver_) {
     return;
+  }
 
   const PasswordForm& form = *GetSubmittedForm();
   metrics_util::SubmittedFormFrame frame;
@@ -1454,8 +1475,9 @@
   FormSignature observed_form_signature =
       CalculateFormSignature(*observed_form());
   auto it = predictions.find(observed_form_signature);
-  if (it == predictions.end())
+  if (it == predictions.end()) {
     return;
+  }
 
   ReportTimeBetweenStoreAndServerUMA();
   parser_.set_predictions(it->second);
diff --git a/components/password_manager/core/browser/password_form_manager.h b/components/password_manager/core/browser/password_form_manager.h
index b05651ee..0ddc30c0 100644
--- a/components/password_manager/core/browser/password_form_manager.h
+++ b/components/password_manager/core/browser/password_form_manager.h
@@ -170,8 +170,7 @@
   metrics_util::CredentialSourceType GetCredentialSource() const override;
   PasswordFormMetricsRecorder* GetMetricsRecorder() override;
   base::span<const InteractionsStats> GetInteractionsStats() const override;
-  std::vector<raw_ptr<const PasswordForm, VectorExperimental>>
-  GetInsecureCredentials() const override;
+  base::span<const PasswordForm> GetInsecureCredentials() const override;
   bool IsBlocklisted() const override;
   bool IsMovableToAccountStore() const override;
 
diff --git a/components/password_manager/core/browser/password_form_manager_for_ui.h b/components/password_manager/core/browser/password_form_manager_for_ui.h
index 66ee9b1..df0511c 100644
--- a/components/password_manager/core/browser/password_form_manager_for_ui.h
+++ b/components/password_manager/core/browser/password_form_manager_for_ui.h
@@ -53,8 +53,7 @@
   virtual base::span<const InteractionsStats> GetInteractionsStats() const = 0;
 
   // List of insecure passwords for the current site.
-  virtual std::vector<raw_ptr<const PasswordForm, VectorExperimental>>
-  GetInsecureCredentials() const = 0;
+  virtual base::span<const PasswordForm> GetInsecureCredentials() const = 0;
 
   // Determines if the user opted to 'never remember' passwords for this form.
   virtual bool IsBlocklisted() const = 0;
diff --git a/components/password_manager/core/browser/password_generation_manager.cc b/components/password_manager/core/browser/password_generation_manager.cc
index c1e45d8..d0e2527 100644
--- a/components/password_manager/core/browser/password_generation_manager.cc
+++ b/components/password_manager/core/browser/password_generation_manager.cc
@@ -28,8 +28,9 @@
     const std::vector<raw_ptr<const PasswordForm, VectorExperimental>>& forms) {
   std::vector<PasswordForm> result;
   result.reserve(forms.size());
-  for (const PasswordForm* form : forms)
+  for (const PasswordForm* form : forms) {
     result.emplace_back(*form);
+  }
   return result;
 }
 
@@ -59,8 +60,7 @@
   metrics_util::CredentialSourceType GetCredentialSource() const override;
   PasswordFormMetricsRecorder* GetMetricsRecorder() override;
   base::span<const InteractionsStats> GetInteractionsStats() const override;
-  std::vector<raw_ptr<const PasswordForm, VectorExperimental>>
-  GetInsecureCredentials() const override;
+  base::span<const PasswordForm> GetInsecureCredentials() const override;
   bool IsBlocklisted() const override;
   bool IsMovableToAccountStore() const override;
   void Save() override;
@@ -103,8 +103,9 @@
       non_federated_matches_(DeepCopyVector(matches)),
       store_for_saving_(store_for_saving),
       bubble_interaction_cb_(std::move(bubble_interaction)) {
-  for (const PasswordForm& form : non_federated_matches_)
+  for (const PasswordForm& form : non_federated_matches_) {
     matches_.push_back(form);
+  }
 }
 
 const GURL& PasswordDataForUI::GetURL() const {
@@ -142,8 +143,8 @@
   return {};
 }
 
-std::vector<raw_ptr<const PasswordForm, VectorExperimental>>
-PasswordDataForUI::GetInsecureCredentials() const {
+base::span<const PasswordForm> PasswordDataForUI::GetInsecureCredentials()
+    const {
   return {};
 }
 
@@ -208,8 +209,9 @@
     const std::vector<raw_ptr<const PasswordForm, VectorExperimental>>&
         matches) {
   for (const password_manager::PasswordForm* form : matches) {
-    if (form->username_value == generated.username_value)
+    if (form->username_value == generated.username_value) {
       return form;
+    }
   }
   return nullptr;
 }
@@ -372,8 +374,9 @@
   CHECK(!generated.password_value.empty());
   // Clear the username value if there are already saved credentials with
   // the same username in order to prevent overwriting.
-  if (FindUsernameConflict(generated, matches))
+  if (FindUsernameConflict(generated, matches)) {
     generated.username_value.clear();
+  }
   generated.date_created = base::Time::Now();
   if (presaved_) {
     form_saver->UpdateReplace(generated, {} /* matches */,
diff --git a/components/password_manager/core/browser/password_manager.cc b/components/password_manager/core/browser/password_manager.cc
index 0ebae1c..1f01596 100644
--- a/components/password_manager/core/browser/password_manager.cc
+++ b/components/password_manager/core/browser/password_manager.cc
@@ -200,18 +200,16 @@
          HasNewPasswordVote(form_predictions);
 }
 
-bool IsMutedInsecureCredential(const PasswordForm* credential,
+bool IsMutedInsecureCredential(const PasswordForm& credential,
                                InsecureType insecure_type) {
-  auto it = credential->password_issues.find(insecure_type);
-  return it != credential->password_issues.end() && it->second.is_muted;
+  auto it = credential.password_issues.find(insecure_type);
+  return it != credential.password_issues.end() && it->second.is_muted;
 }
 
-bool HasMutedCredentials(
-    const std::vector<raw_ptr<const PasswordForm, VectorExperimental>>&
-        credentials,
-    const std::u16string& username) {
+bool HasMutedCredentials(base::span<const PasswordForm> credentials,
+                         const std::u16string& username) {
   return base::ranges::any_of(credentials, [&username](const auto& credential) {
-    return credential->username_value == username &&
+    return credential.username_value == username &&
            (IsMutedInsecureCredential(credential, InsecureType::kLeaked) ||
             IsMutedInsecureCredential(credential, InsecureType::kPhished));
   });
@@ -219,8 +217,9 @@
 
 // Returns true iff a password field is absent or hidden.
 bool IsSingleUsernameSubmission(const PasswordForm& submitted_form) {
-  if (submitted_form.IsSingleUsername())
+  if (submitted_form.IsSingleUsername()) {
     return true;
+  }
 
   for (auto const& field : submitted_form.form_data.fields) {
     if (submitted_form.password_element_renderer_id == field.renderer_id() ||
@@ -430,8 +429,9 @@
 
   PasswordFormManager* form_manager =
       GetMatchedManagerForForm(driver, form_data.renderer_id);
-  if (form_manager)
+  if (form_manager) {
     form_manager->PasswordNoLongerGenerated();
+  }
 }
 
 void PasswordManager::SetGenerationElementAndTypeForForm(
@@ -482,8 +482,9 @@
   }
 
   if (client_->IsNewTabPage()) {
-    if (logger)
+    if (logger) {
       logger->LogMessage(Logger::STRING_NAVIGATION_NTP);
+    }
     // On a successful Chrome sign-in the page navigates to the new tab page
     // (ntp). OnPasswordFormsRendered is not called on ntp. That is
     // why the standard flow for saving hash does not work. Save a password hash
@@ -503,8 +504,9 @@
 
   // Reset |possible_username_| if the navigation cannot be a result of form
   // submission.
-  if (!form_may_be_submitted)
+  if (!form_may_be_submitted) {
     possible_usernames_.Clear();
+  }
 
   if (form_may_be_submitted) {
     std::unique_ptr<PasswordFormManager> submitted_manager =
@@ -528,8 +530,9 @@
   std::vector<PasswordManagerDriver*> drivers;
   for (const auto& form_manager : password_form_cache_.GetFormManagers()) {
     fetchers.push_back(form_manager->GetFormFetcher());
-    if (form_manager->GetDriver())
+    if (form_manager->GetDriver()) {
       drivers.push_back(form_manager->GetDriver().get());
+    }
   }
 
   // Remove the duplicates.
@@ -538,8 +541,9 @@
   base::ranges::sort(drivers);
   drivers.erase(base::ranges::unique(drivers), drivers.end());
   // Refetch credentials for all the forms and update the drivers.
-  for (FormFetcher* fetcher : fetchers)
+  for (FormFetcher* fetcher : fetchers) {
     fetcher->Fetch();
+  }
 
   // The autofill manager will be repopulated again when the credentials
   // are retrieved.
@@ -589,8 +593,9 @@
   PasswordFormManager* submitted_manager = GetSubmittedManager();
   // TODO(crbug.com/40621653): Add UMA metric for how frequently
   // submitted_manager is actually null.
-  if (!submitted_manager || !submitted_manager->GetSubmittedForm())
+  if (!submitted_manager || !submitted_manager->GetSubmittedForm()) {
     return;
+  }
 
   if (
 #if BUILDFLAG(IS_IOS)
@@ -621,8 +626,9 @@
 
   submitted_manager->UpdateSubmissionIndicatorEvent(event);
 
-  if (IsAutomaticSavePromptAvailable())
+  if (IsAutomaticSavePromptAvailable()) {
     OnLoginSuccessful();
+  }
 }
 
 void PasswordManager::OnPasswordFormCleared(
@@ -668,8 +674,9 @@
 
   ProvisionallySaveForm(form_data, driver, false);
 
-  if (IsAutomaticSavePromptAvailable())
+  if (IsAutomaticSavePromptAvailable()) {
     OnLoginSuccessful();
+  }
 }
 #endif  // BUILDFLAG(IS_IOS)
 
@@ -719,8 +726,9 @@
       manager ? PasswordManagerMetricsRecorder::FormManagerAvailable::kSuccess
               : PasswordManagerMetricsRecorder::FormManagerAvailable::
                     kMissingManual;
-  if (client_->GetMetricsRecorder())
+  if (client_->GetMetricsRecorder()) {
     client_->GetMetricsRecorder()->RecordFormManagerAvailable(availability);
+  }
 
   ShowManualFallbackForSaving(manager, form_data);
 }
@@ -749,8 +757,9 @@
 void PasswordManager::OnPasswordFormsParsed(
     PasswordManagerDriver* driver,
     const std::vector<FormData>& form_data) {
-  if (NewFormsParsed(driver, form_data))
+  if (NewFormsParsed(driver, form_data)) {
     client_->RefreshPasswordManagerSettingsIfNeeded();
+  }
   CreatePendingLoginManagers(driver, form_data);
 
   PasswordGenerationFrameHelper* password_generation_manager =
@@ -783,8 +792,9 @@
   // Find new forms.
   std::vector<const FormData*> new_forms_data;
   for (const FormData& form_data : forms_data) {
-    if (!client_->IsFillingEnabled(form_data.url))
+    if (!client_->IsFillingEnabled(form_data.url)) {
       continue;
+    }
 
     PasswordFormManager* manager =
         GetMatchedManagerForForm(driver, form_data.renderer_id);
@@ -803,8 +813,9 @@
   }
 
   // Create form manager for new forms.
-  for (const FormData* new_form_data : new_forms_data)
+  for (const FormData* new_form_data : new_forms_data) {
     CreateFormManager(driver, *new_form_data);
+  }
 }
 
 PasswordFormManager* PasswordManager::CreateFormManager(
@@ -832,8 +843,9 @@
     logger->LogMessage(Logger::STRING_PROVISIONALLY_SAVE_FORM_METHOD);
   }
 
-  if (store_password_called_)
+  if (store_password_called_) {
     return nullptr;
+  }
 
   const GURL& submitted_url = submitted_form.url;
   if (ShouldBlockPasswordForSameOriginButDifferentScheme(submitted_url)) {
@@ -851,8 +863,9 @@
           ? PasswordManagerMetricsRecorder::FormManagerAvailable::kSuccess
           : PasswordManagerMetricsRecorder::FormManagerAvailable::
                 kMissingProvisionallySave;
-  if (client_->GetMetricsRecorder())
+  if (client_->GetMetricsRecorder()) {
     client_->GetMetricsRecorder()->RecordFormManagerAvailable(availability);
+  }
 
   if (!matched_manager) {
     RecordProvisionalSaveFailure(
@@ -891,8 +904,9 @@
 
   // Set all other form managers to no submission state.
   for (const auto& manager : password_form_cache_.GetFormManagers()) {
-    if (manager.get() != matched_manager)
+    if (manager.get() != matched_manager) {
       manager->set_not_submitted();
+    }
   }
 
   // Cache the committed URL. Once the post-submit navigation concludes, we
@@ -930,8 +944,9 @@
   PasswordFormManager* manager =
       form_id ? GetMatchedManagerForForm(driver, *form_id)
               : GetMatchedManagerForField(driver, field_id);
-  if (!manager)
+  if (!manager) {
     return;
+  }
 
   const autofill::FormData* observed_form = manager->observed_form();
 
@@ -1133,19 +1148,22 @@
   }
 
   // No submitted manager => no submission tracking.
-  if (!GetSubmittedManager())
+  if (!GetSubmittedManager()) {
     client_->ResetSubmissionTrackingAfterTouchToFill();
+  }
 
-  if (!IsAutomaticSavePromptAvailable())
+  if (!IsAutomaticSavePromptAvailable()) {
     return;
+  }
 
   PasswordFormManager* submitted_manager = GetSubmittedManager();
 
   // If the server throws an internal error, access denied page, page not
   // found etc. after a login attempt, we do not save the credentials.
   if (client_->WasLastNavigationHTTPError()) {
-    if (logger)
+    if (logger) {
       logger->LogMessage(Logger::STRING_DECISION_DROP);
+    }
     submitted_manager->GetMetricsRecorder()->LogSubmitFailed();
     ResetSubmittedManager();
     return;
@@ -1198,8 +1216,9 @@
       }
     }
   } else {
-    if (logger)
+    if (logger) {
       logger->LogMessage(Logger::STRING_PROVISIONALLY_SAVED_FORM_IS_NOT_HTML);
+    }
   }
   // Clear visible_forms_data_ after checking all the visible forms.
   visible_forms_data_.clear();
@@ -1238,8 +1257,9 @@
   auto submission_event =
       submitted_manager->GetSubmittedForm()->submission_event;
   metrics_util::LogPasswordSuccessfulSubmissionIndicatorEvent(submission_event);
-  if (logger)
+  if (logger) {
     logger->LogSuccessfulSubmissionIndicatorEvent(submission_event);
+  }
 
   password_manager::PasswordReuseManager* reuse_manager =
       client_->GetPasswordReuseManager();
@@ -1305,18 +1325,21 @@
   }
 
   if (ShouldPromptUserToSavePassword(*submitted_manager)) {
-    if (logger)
+    if (logger) {
       logger->LogMessage(Logger::STRING_DECISION_ASK);
+    }
     submitted_manager->SaveSuggestedUsernameValueToVotesUploader();
     bool update_password = submitted_manager->IsPasswordUpdate();
     if (client_->PromptUserToSaveOrUpdatePassword(MoveOwnedSubmittedManager(),
                                                   update_password)) {
-      if (logger)
+      if (logger) {
         logger->LogMessage(Logger::STRING_SHOW_PASSWORD_PROMPT);
+      }
     }
   } else {
-    if (logger)
+    if (logger) {
       logger->LogMessage(Logger::STRING_DECISION_SAVE);
+    }
     submitted_manager->Save();
 
     if (!submitted_manager->IsNewLogin()) {
@@ -1339,8 +1362,9 @@
                          autofill::AutofillType::ServerPrediction>&
         predictions) {
   // Don't do anything if Password store is not available.
-  if(!client_->GetProfilePasswordStore())
+  if (!client_->GetProfilePasswordStore()) {
     return;
+  }
 
   std::unique_ptr<BrowserSavePasswordProgressLogger> logger;
   if (password_manager_util::IsLoggingActive(client_)) {
@@ -1403,16 +1427,18 @@
 }
 
 PasswordFormManager* PasswordManager::GetSubmittedManager() {
-  if (owned_submitted_form_manager_)
+  if (owned_submitted_form_manager_) {
     return owned_submitted_form_manager_.get();
+  }
 
   return password_form_cache_.GetSubmittedManager();
 }
 
 std::optional<PasswordForm> PasswordManager::GetSubmittedCredentials() {
   PasswordFormManager* submitted_manager = GetSubmittedManager();
-  if (submitted_manager)
+  if (submitted_manager) {
     return submitted_manager->GetPendingCredentials();
+  }
   return std::nullopt;
 }
 
@@ -1444,8 +1470,9 @@
 
 std::unique_ptr<PasswordFormManagerForUI>
 PasswordManager::MoveOwnedSubmittedManager() {
-  if (owned_submitted_form_manager_)
+  if (owned_submitted_form_manager_) {
     return std::move(owned_submitted_form_manager_);
+  }
 
   std::unique_ptr<PasswordFormManager> manager =
       password_form_cache_.MoveOwnedSubmittedManager();
@@ -1519,8 +1546,9 @@
     return;
   }
 
-  if (!form_manager->is_submitted())
+  if (!form_manager->is_submitted()) {
     return;
+  }
 
   if (!password_manager_util::IsAbleToSavePasswords(client_) ||
       !client_->IsSavingAndFillingEnabled(form_data.url) ||
@@ -1557,8 +1585,9 @@
 
 bool PasswordManager::IsFormManagerPendingPasswordUpdate() const {
   for (const auto& form_manager : password_form_cache_.GetFormManagers()) {
-    if (form_manager->IsPasswordUpdate())
+    if (form_manager->IsPasswordUpdate()) {
       return true;
+    }
   }
   return owned_submitted_form_manager_ &&
          owned_submitted_form_manager_->IsPasswordUpdate();
diff --git a/components/password_manager/core/browser/ui/post_save_compromised_helper.cc b/components/password_manager/core/browser/ui/post_save_compromised_helper.cc
index 6ae11c2..5e8dd65 100644
--- a/components/password_manager/core/browser/ui/post_save_compromised_helper.cc
+++ b/components/password_manager/core/browser/ui/post_save_compromised_helper.cc
@@ -21,12 +21,12 @@
 constexpr auto kMaxTimeSinceLastCheck = base::Minutes(30);
 
 PostSaveCompromisedHelper::PostSaveCompromisedHelper(
-    const std::vector<raw_ptr<const PasswordForm, VectorExperimental>>&
-        compromised,
+    const base::span<const PasswordForm> compromised,
     const std::u16string& current_username) {
-  for (const PasswordForm* credential : compromised) {
-    if (credential->username_value == current_username)
-      current_leak_ = *credential;
+  for (const PasswordForm& credential : compromised) {
+    if (credential.username_value == current_username) {
+      current_leak_ = credential;
+    }
   }
 }
 
diff --git a/components/password_manager/core/browser/ui/post_save_compromised_helper.h b/components/password_manager/core/browser/ui/post_save_compromised_helper.h
index 597420b..15316e8 100644
--- a/components/password_manager/core/browser/ui/post_save_compromised_helper.h
+++ b/components/password_manager/core/browser/ui/post_save_compromised_helper.h
@@ -41,10 +41,8 @@
 
   // |compromised| contains all insecure credentials for the current site.
   // |current_username| is the username that was just saved or updated.
-  PostSaveCompromisedHelper(
-      const std::vector<raw_ptr<const PasswordForm, VectorExperimental>>&
-          compromised,
-      const std::u16string& current_username);
+  PostSaveCompromisedHelper(base::span<const PasswordForm> compromised,
+                            const std::u16string& current_username);
   ~PostSaveCompromisedHelper() override;
 
   PostSaveCompromisedHelper(const PostSaveCompromisedHelper&) = delete;
diff --git a/components/password_manager/core/browser/ui/post_save_compromised_helper_unittest.cc b/components/password_manager/core/browser/ui/post_save_compromised_helper_unittest.cc
index 7e7978de..8db7d74 100644
--- a/components/password_manager/core/browser/ui/post_save_compromised_helper_unittest.cc
+++ b/components/password_manager/core/browser/ui/post_save_compromised_helper_unittest.cc
@@ -150,7 +150,7 @@
   PasswordForm insecure_credential =
       CreateInsecureCredential(kUsername, kPassword);
 
-  PostSaveCompromisedHelper helper({&insecure_credential}, kUsername);
+  PostSaveCompromisedHelper helper(std::vector{insecure_credential}, kUsername);
   base::MockCallback<PostSaveCompromisedHelper::BubbleCallback> callback;
   ExpectGetLoginsCall({form1, form2});
   EXPECT_CALL(callback, Run(BubbleType::kNoBubble, 2));
@@ -168,7 +168,7 @@
   PasswordForm form2 = CreateInsecureCredential(kUsername2, kPassword2);
   PasswordForm form3 = CreateInsecureCredential(kUsername, kPassword);
 
-  PostSaveCompromisedHelper helper({&form2, &form3}, kUsername);
+  PostSaveCompromisedHelper helper(std::vector{form2, form3}, kUsername);
   base::MockCallback<PostSaveCompromisedHelper::BubbleCallback> callback;
   EXPECT_CALL(callback, Run(BubbleType::kPasswordUpdatedWithMoreToFix, 1));
   ExpectGetLoginsCall({form1, form2});
@@ -182,7 +182,7 @@
 TEST_F(PostSaveCompromisedHelperTest, FixedLast_BulkCheckNeverDone) {
   PasswordForm insecure_credential =
       CreateInsecureCredential(kUsername, kPassword);
-  PostSaveCompromisedHelper helper({&insecure_credential}, kUsername);
+  PostSaveCompromisedHelper helper(std::vector{insecure_credential}, kUsername);
   base::MockCallback<PostSaveCompromisedHelper::BubbleCallback> callback;
   EXPECT_CALL(callback, Run(BubbleType::kNoBubble, 0));
   EXPECT_CALL(*profile_store(), GetAutofillableLogins).Times(0);
@@ -199,7 +199,7 @@
       (base::Time::Now() - base::Days(5)).InSecondsFSinceUnixEpoch());
   PasswordForm insecure_credential =
       CreateInsecureCredential(kUsername, kPassword);
-  PostSaveCompromisedHelper helper({&insecure_credential}, kUsername);
+  PostSaveCompromisedHelper helper(std::vector{insecure_credential}, kUsername);
   base::MockCallback<PostSaveCompromisedHelper::BubbleCallback> callback;
   EXPECT_CALL(callback, Run(BubbleType::kNoBubble, 0));
   EXPECT_CALL(*profile_store(), GetAutofillableLogins).Times(0);
@@ -216,7 +216,7 @@
       (base::Time::Now() - base::Minutes(1)).InSecondsFSinceUnixEpoch());
   PasswordForm insecure_credential =
       CreateInsecureCredential(kUsername, kPassword);
-  PostSaveCompromisedHelper helper({&insecure_credential}, kUsername);
+  PostSaveCompromisedHelper helper(std::vector{insecure_credential}, kUsername);
   base::MockCallback<PostSaveCompromisedHelper::BubbleCallback> callback;
   EXPECT_CALL(callback, Run(BubbleType::kPasswordUpdatedSafeState, 0));
   ExpectGetLoginsCall({CreateForm(kSignonRealm, kUsername, kPassword)});
@@ -233,7 +233,7 @@
       (base::Time::Now() - base::Minutes(1)).InSecondsFSinceUnixEpoch());
   PasswordForm insecure_credential = CreateInsecureCredential(
       kUsername, kPassword, PasswordForm::Store::kProfileStore, IsMuted(true));
-  PostSaveCompromisedHelper helper({&insecure_credential}, kUsername);
+  PostSaveCompromisedHelper helper(std::vector{insecure_credential}, kUsername);
   base::MockCallback<PostSaveCompromisedHelper::BubbleCallback> callback;
   EXPECT_CALL(callback, Run(BubbleType::kPasswordUpdatedSafeState, 0));
   ExpectGetLoginsCall({CreateForm(kSignonRealm, kUsername, kPassword)});
@@ -250,7 +250,7 @@
       (base::Time::Now() - base::Minutes(1)).InSecondsFSinceUnixEpoch());
   PasswordForm insecure_credential =
       CreateInsecureCredential(kUsername, kPassword);
-  PostSaveCompromisedHelper helper({&insecure_credential}, kUsername);
+  PostSaveCompromisedHelper helper(std::vector{insecure_credential}, kUsername);
   base::MockCallback<PostSaveCompromisedHelper::BubbleCallback> callback;
   EXPECT_CALL(callback, Run(BubbleType::kPasswordUpdatedWithMoreToFix, 1));
   PasswordForm form1 = CreateForm(kSignonRealm, kUsername, kPassword);
@@ -300,9 +300,9 @@
   PasswordForm compromised_account_credential = CreateInsecureCredential(
       kUsername, kPassword, PasswordForm::Store::kAccountStore);
 
-  PostSaveCompromisedHelper helper(
-      {&compromised_profile_credential, &compromised_account_credential},
-      kUsername);
+  PostSaveCompromisedHelper helper(std::vector{compromised_profile_credential,
+                                               compromised_account_credential},
+                                   kUsername);
   EXPECT_CALL(*profile_store(), GetAutofillableLogins)
       .WillOnce(testing::WithArg<0>(
           [store =
diff --git a/components/password_manager/ios/password_form_helper.mm b/components/password_manager/ios/password_form_helper.mm
index f0a0d5e..ab00e86 100644
--- a/components/password_manager/ios/password_form_helper.mm
+++ b/components/password_manager/ios/password_form_helper.mm
@@ -252,7 +252,7 @@
             fromFillData:(password_manager::FillData)fillData
     withFieldDataManager:(autofill::FieldDataManager*)manager
                   driver:(IOSPasswordManagerDriver*)driver {
-  if (!result->is_dict()) {
+  if (!result || !result->is_dict()) {
     return NO;
   }
 
diff --git a/components/password_manager/ios/password_form_helper_unittest.mm b/components/password_manager/ios/password_form_helper_unittest.mm
index 0cb2143..f7dfd3aa 100644
--- a/components/password_manager/ios/password_form_helper_unittest.mm
+++ b/components/password_manager/ios/password_form_helper_unittest.mm
@@ -760,6 +760,24 @@
     ASSERT_TRUE(called);
     EXPECT_FALSE(succeeded);
   }
+
+  // Test a nullptr result.
+  {
+    main_frame_ptr->AddJsResultForFunctionCall(nullptr,
+                                               "passwords.fillPasswordForm");
+    __block bool called = false;
+    __block BOOL succeeded = false;
+    [helper fillPasswordFormWithFillData:fill_data
+                                 inFrame:main_frame_ptr
+                        triggeredOnField:username_field_id
+                       completionHandler:^(BOOL success) {
+                         called = true;
+                         succeeded = success;
+                       }];
+    WaitForBackgroundTasks();
+    ASSERT_TRUE(called);
+    EXPECT_FALSE(succeeded);
+  }
 }
 
 // Tests that extractPasswordFormData extracts wanted form on page with mutiple
diff --git a/components/search_provider_logos/google_logo_api.cc b/components/search_provider_logos/google_logo_api.cc
index c4c9b3fa..0d59d39e 100644
--- a/components/search_provider_logos/google_logo_api.cc
+++ b/components/search_provider_logos/google_logo_api.cc
@@ -248,60 +248,14 @@
        logo->metadata.type == LogoType::SIMPLE);
 
   if (is_eligible_for_share_button) {
-    const base::Value::Dict* share_button = ddljson->FindDict("share_button");
     const std::string* short_link_ptr = ddljson->FindString("short_link");
     // The short link in the doodle proto is an incomplete URL with the format
     // //g.co/*, //doodle.gle/* or //google.com?doodle=*.
     // Complete the URL if possible.
-    if (share_button && short_link_ptr && short_link_ptr->find("//") == 0) {
+    if (short_link_ptr && short_link_ptr->find("//") == 0) {
       std::string short_link_str = *short_link_ptr;
       short_link_str.insert(0, "https:");
       logo->metadata.short_link = GURL(std::move(short_link_str));
-      if (logo->metadata.short_link.is_valid()) {
-        if (std::optional<int> offset_x = share_button->FindInt("offset_x")) {
-          logo->metadata.share_button_x = *offset_x;
-        }
-        if (std::optional<int> offset_y = share_button->FindInt("offset_y")) {
-          logo->metadata.share_button_y = *offset_y;
-        }
-        if (std::optional<double> opacity =
-                share_button->FindDouble("opacity")) {
-          logo->metadata.share_button_opacity = *opacity;
-        }
-        if (const std::string* icon = share_button->FindString("icon_image")) {
-          logo->metadata.share_button_icon = *icon;
-        }
-        if (const std::string* bg_color =
-                share_button->FindString("background_color")) {
-          logo->metadata.share_button_bg = *bg_color;
-        }
-      }
-    }
-    const base::Value::Dict* dark_share_button =
-        ddljson->FindDict("dark_share_button");
-    if (dark_share_button) {
-      if (logo->metadata.short_link.is_valid()) {
-        if (std::optional<int> offset_x =
-                dark_share_button->FindInt("offset_x")) {
-          logo->metadata.dark_share_button_x = *offset_x;
-        }
-        if (std::optional<int> offset_y =
-                dark_share_button->FindInt("offset_y")) {
-          logo->metadata.dark_share_button_y = *offset_y;
-        }
-        if (std::optional<double> opacity =
-                dark_share_button->FindDouble("opacity")) {
-          logo->metadata.dark_share_button_opacity = *opacity;
-        }
-        if (const std::string* icon =
-                dark_share_button->FindString("icon_image")) {
-          logo->metadata.dark_share_button_icon = *icon;
-        }
-        if (const std::string* bg_color =
-                dark_share_button->FindString("background_color")) {
-          logo->metadata.dark_share_button_bg = *bg_color;
-        }
-      }
     }
   }
 
diff --git a/components/search_provider_logos/google_logo_api_unittest.cc b/components/search_provider_logos/google_logo_api_unittest.cc
index 53b299c..f67ddcb 100644
--- a/components/search_provider_logos/google_logo_api_unittest.cc
+++ b/components/search_provider_logos/google_logo_api_unittest.cc
@@ -194,21 +194,7 @@
   "ddljson": {
     "doodle_type": "SIMPLE",
     "data_uri": "data:image/png;base64,YWJj",
-    "short_link": "//g.co",
-    "share_button": {
-      "background_color": "#fe8080",
-      "icon_image": "test_img",
-      "offset_x": 111,
-      "offset_y": 222,
-      "opacity": 0.5
-    },
-    "dark_share_button": {
-      "background_color": "#ee22bb",
-      "icon_image": "dark_test_img",
-      "offset_x": 99,
-      "offset_y": 191,
-      "opacity": 0.7
-    }
+    "short_link": "//g.co"
   }
 })json";
 
@@ -221,17 +207,6 @@
   EXPECT_EQ("abc", logo->encoded_image->as_string());
   EXPECT_EQ(LogoType::SIMPLE, logo->metadata.type);
   EXPECT_EQ("https://g.co/", logo->metadata.short_link);
-
-  EXPECT_EQ("#fe8080", logo->metadata.share_button_bg);
-  EXPECT_EQ("test_img", logo->metadata.share_button_icon);
-  EXPECT_EQ(111, logo->metadata.share_button_x);
-  EXPECT_EQ(222, logo->metadata.share_button_y);
-  EXPECT_EQ(0.5, logo->metadata.share_button_opacity);
-  EXPECT_EQ("#ee22bb", logo->metadata.dark_share_button_bg);
-  EXPECT_EQ("dark_test_img", logo->metadata.dark_share_button_icon);
-  EXPECT_EQ(99, logo->metadata.dark_share_button_x);
-  EXPECT_EQ(191, logo->metadata.dark_share_button_y);
-  EXPECT_EQ(0.7, logo->metadata.dark_share_button_opacity);
 }
 
 TEST(GoogleNewLogoApiTest, ParsesNoShareButtonIfWrongShortLinkFormat) {
@@ -242,21 +217,7 @@
   "ddljson": {
     "doodle_type": "SIMPLE",
     "data_uri": "data:image/png;base64,YWJj",
-    "short_link": "www.//g.co",
-    "share_button": {
-      "background_color": "#fe8080",
-      "icon_image": "test_img",
-      "offset_x": 111,
-      "offset_y": 222,
-      "opacity": 0.5
-    },
-    "dark_share_button": {
-      "background_color": "#ee22bb",
-      "icon_image": "dark_test_img",
-      "offset_x": 99,
-      "offset_y": 191,
-      "opacity": 0.7
-    }
+    "short_link": "www.//g.co"
   }
 })json";
 
@@ -269,14 +230,6 @@
   EXPECT_EQ("abc", logo->encoded_image->as_string());
   EXPECT_EQ(LogoType::SIMPLE, logo->metadata.type);
   ASSERT_TRUE(logo->metadata.short_link.is_empty());
-  ASSERT_TRUE(logo->metadata.share_button_icon.empty());
-  EXPECT_EQ(-1, logo->metadata.share_button_x);
-  EXPECT_EQ(-1, logo->metadata.share_button_y);
-  EXPECT_EQ(0, logo->metadata.share_button_opacity);
-  ASSERT_TRUE(logo->metadata.dark_share_button_icon.empty());
-  EXPECT_EQ(-1, logo->metadata.dark_share_button_x);
-  EXPECT_EQ(-1, logo->metadata.dark_share_button_y);
-  EXPECT_EQ(0, logo->metadata.dark_share_button_opacity);
 }
 
 TEST(GoogleNewLogoApiTest, ParsesNoShareButtonIfShortLinkInvalid) {
@@ -287,21 +240,7 @@
   "ddljson": {
     "doodle_type": "SIMPLE",
     "data_uri": "data:image/png;base64,YWJj",
-    "short_link": "//dsdjf2(*&^%&",
-    "share_button": {
-      "background_color": "#fe8080",
-      "icon_image": "test_img",
-      "offset_x": 111,
-      "offset_y": 222,
-      "opacity": 0.5
-    },
-    "dark_share_button": {
-      "background_color": "#ee22bb",
-      "icon_image": "dark_test_img",
-      "offset_x": 99,
-      "offset_y": 191,
-      "opacity": 0.7
-    }
+    "short_link": "//dsdjf2(*&^%&"
   }
 })json";
 
@@ -314,14 +253,6 @@
   EXPECT_EQ("abc", logo->encoded_image->as_string());
   EXPECT_EQ(LogoType::SIMPLE, logo->metadata.type);
   ASSERT_FALSE(logo->metadata.short_link.is_valid());
-  ASSERT_TRUE(logo->metadata.share_button_icon.empty());
-  EXPECT_EQ(-1, logo->metadata.share_button_x);
-  EXPECT_EQ(-1, logo->metadata.share_button_y);
-  EXPECT_EQ(0, logo->metadata.share_button_opacity);
-  ASSERT_TRUE(logo->metadata.dark_share_button_icon.empty());
-  EXPECT_EQ(-1, logo->metadata.dark_share_button_x);
-  EXPECT_EQ(-1, logo->metadata.dark_share_button_y);
-  EXPECT_EQ(0, logo->metadata.dark_share_button_opacity);
 }
 
 TEST(GoogleNewLogoApiTest, ParsesShareButtonForAnimatedDoodle) {
@@ -337,21 +268,7 @@
       "url": "https://www.doodle.com/image.gif"
     },
     "short_link": "//g.co",
-    "cta_data_uri": "data:image/png;base64,YWJj",
-    "share_button": {
-      "background_color": "#fe8080",
-      "icon_image": "test_img",
-      "offset_x": 111,
-      "offset_y": 222,
-      "opacity": 0.5
-    },
-    "dark_share_button": {
-      "background_color": "#ee22bb",
-      "icon_image": "dark_test_img",
-      "offset_x": 99,
-      "offset_y": 191,
-      "opacity": 0.7
-    }
+    "cta_data_uri": "data:image/png;base64,YWJj"
   }
 })json";
 
@@ -366,17 +283,6 @@
   EXPECT_EQ("abc", logo->encoded_image->as_string());
   EXPECT_EQ(LogoType::ANIMATED, logo->metadata.type);
   EXPECT_EQ("https://g.co/", logo->metadata.short_link);
-
-  EXPECT_EQ("#fe8080", logo->metadata.share_button_bg);
-  EXPECT_EQ("test_img", logo->metadata.share_button_icon);
-  EXPECT_EQ(111, logo->metadata.share_button_x);
-  EXPECT_EQ(222, logo->metadata.share_button_y);
-  EXPECT_EQ(0.5, logo->metadata.share_button_opacity);
-  EXPECT_EQ("#ee22bb", logo->metadata.dark_share_button_bg);
-  EXPECT_EQ("dark_test_img", logo->metadata.dark_share_button_icon);
-  EXPECT_EQ(99, logo->metadata.dark_share_button_x);
-  EXPECT_EQ(191, logo->metadata.dark_share_button_y);
-  EXPECT_EQ(0.7, logo->metadata.dark_share_button_opacity);
 }
 
 TEST(GoogleNewLogoApiTest, ParsesAnimatedImage) {
diff --git a/components/services/storage/privileged/mojom/indexed_db_internals_types.mojom b/components/services/storage/privileged/mojom/indexed_db_internals_types.mojom
index 27e6659..ffdafb59 100644
--- a/components/services/storage/privileged/mojom/indexed_db_internals_types.mojom
+++ b/components/services/storage/privileged/mojom/indexed_db_internals_types.mojom
@@ -50,6 +50,7 @@
   IdbTransactionMode mode;
   IdbTransactionState status;
   int64 tid;
+  uint64 client_id;
   double age;
   double runtime;
   double tasks_scheduled;
diff --git a/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate.cc b/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate.cc
index aaaf9a3..24a5a48 100644
--- a/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate.cc
+++ b/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate.cc
@@ -70,10 +70,6 @@
     case SourceForRefreshTokenOperation::
         kAccountReconcilor_RevokeTokensNotInCookies:
       return "AccountReconcilor::RevokeTokensNotInCookies";
-    case SourceForRefreshTokenOperation::
-        kDiceResponseHandler_PasswordPromoSignin:
-      return "DiceResponseHandler::Signin from sign in promo after password "
-             "save";
   }
 }
 
diff --git a/components/signin/public/base/signin_metrics.h b/components/signin/public/base/signin_metrics.h
index 74c8809..37ee72a 100644
--- a/components/signin/public/base/signin_metrics.h
+++ b/components/signin/public/base/signin_metrics.h
@@ -457,9 +457,10 @@
   kLogoutTabHelper_PrimaryPageChanged = 19,
   kForceSigninReauthWithDifferentAccount = 20,
   kAccountReconcilor_RevokeTokensNotInCookies = 21,
-  kDiceResponseHandler_PasswordPromoSignin = 22,
+  // DEPRECATED on 05/2024
+  // kDiceResponseHandler_PasswordPromoSignin = 22,
 
-  kMaxValue = kDiceResponseHandler_PasswordPromoSignin,
+  kMaxValue = kAccountReconcilor_RevokeTokensNotInCookies,
 };
 
 // Different types of reporting. This is used as a histogram suffix.
diff --git a/components/signin/public/identity_manager/account_info.h b/components/signin/public/identity_manager/account_info.h
index fab0bab..facdc80d 100644
--- a/components/signin/public/identity_manager/account_info.h
+++ b/components/signin/public/identity_manager/account_info.h
@@ -73,8 +73,9 @@
   std::string last_downloaded_image_url_with_size;
   gfx::Image account_image;
 
-  // For metrics. This field is not consistently set on all platforms.
-  // Not persisted to disk. Resets to `ACCESS_POINT_UNKNOWN` on restart.
+  // Access point used to add the account, is also updated on reauth.
+  // This field is not consistently set on all platforms.
+  // Not persisted to disk: resets to `ACCESS_POINT_UNKNOWN` on restart.
   signin_metrics::AccessPoint access_point =
       signin_metrics::AccessPoint::ACCESS_POINT_UNKNOWN;
 
diff --git a/components/sync/service/model_type_controller.h b/components/sync/service/model_type_controller.h
index d348513..ec09585 100644
--- a/components/sync/service/model_type_controller.h
+++ b/components/sync/service/model_type_controller.h
@@ -195,7 +195,7 @@
   // We use a vector because it's allowed to call Stop() multiple times (i.e.
   // while STOPPING).
   std::vector<StopCallback> model_stop_callbacks_;
-  SyncStopMetadataFate model_stop_metadata_fate_;
+  SyncStopMetadataFate model_stop_metadata_fate_ = KEEP_METADATA;
 
   // Controller receives |activation_response_| from
   // ClientTagBasedModelTypeProcessor callback and must temporarily own it until
diff --git a/components/sync_device_info/device_info_sync_bridge.cc b/components/sync_device_info/device_info_sync_bridge.cc
index 6355ada..7cc78ef 100644
--- a/components/sync_device_info/device_info_sync_bridge.cc
+++ b/components/sync_device_info/device_info_sync_bridge.cc
@@ -499,10 +499,12 @@
   return std::nullopt;
 }
 
-void DeviceInfoSyncBridge::GetData(StorageKeyList storage_keys,
-                                   DataCallback callback) {
+void DeviceInfoSyncBridge::GetDataForCommit(StorageKeyList storage_keys,
+                                            DataCallback callback) {
   auto batch = std::make_unique<MutableDataBatch>();
   for (const auto& key : storage_keys) {
+    // TODO(crbug.com/341920243): verify that |key| corresponds to the local
+    // device.
     const auto& iter = all_data_.find(key);
     if (iter != all_data_.end()) {
       DCHECK_EQ(key, iter->second.specifics().cache_guid());
diff --git a/components/sync_device_info/device_info_sync_bridge.h b/components/sync_device_info/device_info_sync_bridge.h
index 9d37533e..0e78736 100644
--- a/components/sync_device_info/device_info_sync_bridge.h
+++ b/components/sync_device_info/device_info_sync_bridge.h
@@ -77,7 +77,8 @@
   std::optional<ModelError> ApplyIncrementalSyncChanges(
       std::unique_ptr<MetadataChangeList> metadata_change_list,
       EntityChangeList entity_changes) override;
-  void GetData(StorageKeyList storage_keys, DataCallback callback) override;
+  void GetDataForCommit(StorageKeyList storage_keys,
+                        DataCallback callback) override;
   void GetAllDataForDebugging(DataCallback callback) override;
   std::string GetClientTag(const EntityData& entity_data) override;
   std::string GetStorageKey(const EntityData& entity_data) override;
diff --git a/components/sync_device_info/device_info_sync_bridge_unittest.cc b/components/sync_device_info/device_info_sync_bridge_unittest.cc
index afdba18..86c3409 100644
--- a/components/sync_device_info/device_info_sync_bridge_unittest.cc
+++ b/components/sync_device_info/device_info_sync_bridge_unittest.cc
@@ -673,18 +673,19 @@
     return DataBatchToSpecificsMap(std::move(batch));
   }
 
-  std::map<std::string, sync_pb::EntitySpecifics> GetData(
+  std::map<std::string, sync_pb::EntitySpecifics> GetDataForCommit(
       const std::vector<std::string>& storage_keys) {
     base::RunLoop loop;
     std::unique_ptr<DataBatch> batch;
-    bridge_->GetData(storage_keys, base::BindOnce(
-                                       [](base::RunLoop* loop,
-                                          std::unique_ptr<DataBatch>* out_batch,
-                                          std::unique_ptr<DataBatch> batch) {
-                                         *out_batch = std::move(batch);
-                                         loop->Quit();
-                                       },
-                                       &loop, &batch));
+    bridge_->GetDataForCommit(
+        storage_keys,
+        base::BindOnce(
+            [](base::RunLoop* loop, std::unique_ptr<DataBatch>* out_batch,
+               std::unique_ptr<DataBatch> batch) {
+              *out_batch = std::move(batch);
+              loop->Quit();
+            },
+            &loop, &batch));
     loop.Run();
     EXPECT_NE(nullptr, batch);
     return DataBatchToSpecificsMap(std::move(batch));
@@ -818,26 +819,28 @@
       StateWithEncryption("ekn"));
   InitializeAndPump();
 
-  EXPECT_THAT(GetData({specifics1.cache_guid()}),
+  EXPECT_THAT(GetDataForCommit({specifics1.cache_guid()}),
               UnorderedElementsAre(
                   Pair(specifics1.cache_guid(), HasDeviceInfo(specifics1))));
 
-  EXPECT_THAT(GetData({specifics1.cache_guid(), specifics3.cache_guid()}),
-              UnorderedElementsAre(
-                  Pair(specifics1.cache_guid(), HasDeviceInfo(specifics1)),
-                  Pair(specifics3.cache_guid(), HasDeviceInfo(specifics3))));
+  EXPECT_THAT(
+      GetDataForCommit({specifics1.cache_guid(), specifics3.cache_guid()}),
+      UnorderedElementsAre(
+          Pair(specifics1.cache_guid(), HasDeviceInfo(specifics1)),
+          Pair(specifics3.cache_guid(), HasDeviceInfo(specifics3))));
 
-  EXPECT_THAT(GetData({specifics1.cache_guid(), specifics2.cache_guid(),
-                       specifics3.cache_guid()}),
-              UnorderedElementsAre(
-                  Pair(specifics1.cache_guid(), HasDeviceInfo(specifics1)),
-                  Pair(specifics2.cache_guid(), HasDeviceInfo(specifics2)),
-                  Pair(specifics3.cache_guid(), HasDeviceInfo(specifics3))));
+  EXPECT_THAT(
+      GetDataForCommit({specifics1.cache_guid(), specifics2.cache_guid(),
+                        specifics3.cache_guid()}),
+      UnorderedElementsAre(
+          Pair(specifics1.cache_guid(), HasDeviceInfo(specifics1)),
+          Pair(specifics2.cache_guid(), HasDeviceInfo(specifics2)),
+          Pair(specifics3.cache_guid(), HasDeviceInfo(specifics3))));
 }
 
 TEST_F(DeviceInfoSyncBridgeTest, GetDataMissing) {
   InitializeAndPump();
-  EXPECT_THAT(GetData({"does_not_exist"}), IsEmpty());
+  EXPECT_THAT(GetDataForCommit({"does_not_exist"}), IsEmpty());
 }
 
 TEST_F(DeviceInfoSyncBridgeTest, GetAllData) {
diff --git a/components/sync_sessions/session_sync_bridge.cc b/components/sync_sessions/session_sync_bridge.cc
index b32fa25..96392a1 100644
--- a/components/sync_sessions/session_sync_bridge.cc
+++ b/components/sync_sessions/session_sync_bridge.cc
@@ -266,9 +266,11 @@
   return std::nullopt;
 }
 
-void SessionSyncBridge::GetData(StorageKeyList storage_keys,
-                                DataCallback callback) {
+void SessionSyncBridge::GetDataForCommit(StorageKeyList storage_keys,
+                                         DataCallback callback) {
   DCHECK(syncing_);
+  // TODO(crbug.com/341920243): verify that |storage_keys| corresponds to the
+  // local session.
   std::move(callback).Run(store_->GetSessionDataForKeys(storage_keys));
 }
 
diff --git a/components/sync_sessions/session_sync_bridge.h b/components/sync_sessions/session_sync_bridge.h
index e9ae834..2761885 100644
--- a/components/sync_sessions/session_sync_bridge.h
+++ b/components/sync_sessions/session_sync_bridge.h
@@ -64,7 +64,8 @@
   std::optional<syncer::ModelError> ApplyIncrementalSyncChanges(
       std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
       syncer::EntityChangeList entity_changes) override;
-  void GetData(StorageKeyList storage_keys, DataCallback callback) override;
+  void GetDataForCommit(StorageKeyList storage_keys,
+                        DataCallback callback) override;
   void GetAllDataForDebugging(DataCallback callback) override;
   std::string GetClientTag(const syncer::EntityData& entity_data) override;
   std::string GetStorageKey(const syncer::EntityData& entity_data) override;
diff --git a/components/sync_sessions/session_sync_bridge_unittest.cc b/components/sync_sessions/session_sync_bridge_unittest.cc
index f803598..41df9f7da 100644
--- a/components/sync_sessions/session_sync_bridge_unittest.cc
+++ b/components/sync_sessions/session_sync_bridge_unittest.cc
@@ -251,11 +251,11 @@
     return BatchToEntityDataMap(std::move(batch));
   }
 
-  std::map<std::string, std::unique_ptr<EntityData>> GetData(
+  std::map<std::string, std::unique_ptr<EntityData>> GetDataForCommit(
       const std::vector<std::string>& storage_keys) {
     base::RunLoop loop;
     std::unique_ptr<DataBatch> batch;
-    bridge_->GetData(
+    bridge_->GetDataForCommit(
         storage_keys,
         base::BindLambdaForTesting(
             [&loop, &batch](std::unique_ptr<DataBatch> input_batch) {
@@ -267,9 +267,9 @@
     return BatchToEntityDataMap(std::move(batch));
   }
 
-  std::unique_ptr<EntityData> GetData(const std::string& storage_key) {
+  std::unique_ptr<EntityData> GetDataForCommit(const std::string& storage_key) {
     std::map<std::string, std::unique_ptr<EntityData>> entity_data_map =
-        GetData(std::vector<std::string>{storage_key});
+        GetDataForCommit(std::vector<std::string>{storage_key});
     EXPECT_LE(entity_data_map.size(), 1U);
     if (entity_data_map.empty()) {
       return nullptr;
@@ -395,7 +395,7 @@
 
 // Tests that local windows and tabs that exist at the time the bridge is
 // started (e.g. after a Chrome restart) are properly exposed via the bridge's
-// GetData() and GetAllData() methods, as well as notified via Put().
+// GetDataForCommit() and GetAllData() methods, as well as notified via Put().
 TEST_F(SessionSyncBridgeTest, ShouldExposeInitialLocalTabsToProcessor) {
   const int kWindowId = 1000001;
   const int kTabId1 = 1000002;
@@ -434,7 +434,7 @@
 
   StartSyncing();
 
-  EXPECT_THAT(GetData(header_storage_key),
+  EXPECT_THAT(GetDataForCommit(header_storage_key),
               EntityDataHasSpecifics(MatchesHeader(kLocalCacheGuid, {kWindowId},
                                                    {kTabId1, kTabId2})));
   EXPECT_THAT(
@@ -454,7 +454,7 @@
 
 // Tests that the creation of a new tab while sync is enabled is propagated to:
 // 1) The processor, via Put().
-// 2) The in-memory representation exposed via GetData().
+// 2) The in-memory representation exposed via GetDataForCommit().
 // 3) The persisted store, exposed via GetAllData().
 TEST_F(SessionSyncBridgeTest, ShouldReportLocalTabCreation) {
   const int kWindowId = 1000001;
@@ -517,10 +517,10 @@
           Pair(tab_storage_key, EntityDataHasSpecifics(MatchesTab(
                                     kLocalCacheGuid, kWindowId, kTabId2,
                                     /*tab_node_id=*/_, {"http://bar.com/"})))));
-  EXPECT_THAT(GetData(header_storage_key),
+  EXPECT_THAT(GetDataForCommit(header_storage_key),
               EntityDataHasSpecifics(MatchesHeader(kLocalCacheGuid, {kWindowId},
                                                    {kTabId1, kTabId2})));
-  EXPECT_THAT(GetData(tab_storage_key),
+  EXPECT_THAT(GetDataForCommit(tab_storage_key),
               EntityDataHasSpecifics(
                   MatchesTab(kLocalCacheGuid, kWindowId, kTabId2,
                              /*tab_node_id=*/_, {"http://bar.com/"})));
@@ -549,15 +549,15 @@
   InitializeBridge();
   StartSyncing();
 
-  ASSERT_THAT(GetData(header_storage_key),
+  ASSERT_THAT(GetDataForCommit(header_storage_key),
               EntityDataHasSpecifics(MatchesHeader(
                   kLocalCacheGuid, {kWindowId1}, {kTabId1, kTabId2})));
   ASSERT_THAT(
-      GetData(tab_storage_key1),
+      GetDataForCommit(tab_storage_key1),
       EntityDataHasSpecifics(MatchesTab(kLocalCacheGuid, kWindowId1, kTabId1,
                                         kTabNodeId1, {"http://foo.com/"})));
   ASSERT_THAT(
-      GetData(tab_storage_key2),
+      GetDataForCommit(tab_storage_key2),
       EntityDataHasSpecifics(MatchesTab(kLocalCacheGuid, kWindowId1, kTabId2,
                                         kTabNodeId2, {"http://bar.com/"})));
 
@@ -589,15 +589,15 @@
   // Although we haven't notified the processor about the window-ID change, if
   // it hypothetically asked for these entities, the returned entities are
   // up-to-date.
-  EXPECT_THAT(GetData(header_storage_key),
+  EXPECT_THAT(GetDataForCommit(header_storage_key),
               EntityDataHasSpecifics(MatchesHeader(
                   kLocalCacheGuid, {kWindowId2}, {kTabId1, kTabId2})));
   EXPECT_THAT(
-      GetData(tab_storage_key1),
+      GetDataForCommit(tab_storage_key1),
       EntityDataHasSpecifics(MatchesTab(kLocalCacheGuid, kWindowId2, kTabId1,
                                         kTabNodeId1, {"http://foo.com/"})));
   EXPECT_THAT(
-      GetData(tab_storage_key2),
+      GetDataForCommit(tab_storage_key2),
       EntityDataHasSpecifics(MatchesTab(kLocalCacheGuid, kWindowId2, kTabId2,
                                         kTabNodeId2, {"http://bar.com/"})));
 
@@ -1020,7 +1020,7 @@
   InitializeBridge();
   StartSyncing();
 
-  ASSERT_THAT(GetData(header_storage_key),
+  ASSERT_THAT(GetDataForCommit(header_storage_key),
               EntityDataHasSpecifics(MatchesHeader(
                   kLocalCacheGuid, {kWindowId1}, {kTabId1, kTabId2})));
 
@@ -1029,7 +1029,7 @@
   CloseTab(kTabId2);
   tab1->Navigate("http://foo2.com/");
 
-  ASSERT_THAT(GetData(header_storage_key),
+  ASSERT_THAT(GetDataForCommit(header_storage_key),
               EntityDataHasSpecifics(
                   MatchesHeader(kLocalCacheGuid, {kWindowId1}, {kTabId1})));
 
@@ -1078,7 +1078,7 @@
 
   const std::string header_storage_key =
       SessionStore::GetHeaderStorageKey(kLocalCacheGuid);
-  ASSERT_THAT(GetData(header_storage_key),
+  ASSERT_THAT(GetDataForCommit(header_storage_key),
               EntityDataHasSpecifics(
                   MatchesHeader(kLocalCacheGuid, {kWindowId}, {kTabId})));
   ASSERT_THAT(GetAllData(), Not(IsEmpty()));
@@ -1087,7 +1087,7 @@
   real_processor()->OnSyncStopping(syncer::CLEAR_METADATA);
 
   StartSyncing();
-  ASSERT_THAT(GetData(header_storage_key),
+  ASSERT_THAT(GetDataForCommit(header_storage_key),
               EntityDataHasSpecifics(
                   MatchesHeader(kLocalCacheGuid, {kWindowId}, {kTabId})));
 }
@@ -1161,7 +1161,7 @@
       Put(_, EntityDataHasSpecifics(MatchesHeader(kLocalCacheGuid, _, _)), _));
 
   StartSyncing({foreign_header});
-  ASSERT_THAT(GetData(foreign_header_storage_key), NotNull());
+  ASSERT_THAT(GetDataForCommit(foreign_header_storage_key), NotNull());
 
   std::vector<raw_ptr<const SyncedSession, VectorExperimental>>
       foreign_sessions;
@@ -1172,7 +1172,7 @@
   ShutdownBridge();
   InitializeBridge();
   StartSyncing();
-  ASSERT_THAT(GetData(foreign_header_storage_key), NotNull());
+  ASSERT_THAT(GetDataForCommit(foreign_header_storage_key), NotNull());
 
   EXPECT_FALSE(bridge()->GetOpenTabsUIDelegate()->GetAllForeignSessions(
       &foreign_sessions));
@@ -1726,8 +1726,8 @@
 
   // Test fixture expects the two foreign entities in the model as well as the
   // underlying store.
-  ASSERT_THAT(GetData(foreign_header_storage_key), NotNull());
-  ASSERT_THAT(GetData(foreign_tab_storage_key), NotNull());
+  ASSERT_THAT(GetDataForCommit(foreign_header_storage_key), NotNull());
+  ASSERT_THAT(GetDataForCommit(foreign_tab_storage_key), NotNull());
 
   const sessions::SessionTab* foreign_session_tab = nullptr;
   ASSERT_TRUE(bridge()->GetOpenTabsUIDelegate()->GetForeignTab(
@@ -1759,8 +1759,8 @@
       &foreign_sessions));
 
   // Verify store.
-  EXPECT_THAT(GetData(foreign_header_storage_key), IsNull());
-  EXPECT_THAT(GetData(foreign_tab_storage_key), IsNull());
+  EXPECT_THAT(GetDataForCommit(foreign_header_storage_key), IsNull());
+  EXPECT_THAT(GetDataForCommit(foreign_tab_storage_key), IsNull());
 }
 
 // Verifies that attempts to delete the local session from the UI are ignored,
@@ -1777,8 +1777,9 @@
   const SyncedSession* session = nullptr;
   EXPECT_TRUE(bridge()->GetOpenTabsUIDelegate()->GetLocalSession(&session));
   EXPECT_THAT(session, NotNull());
-  EXPECT_THAT(GetData(SessionStore::GetHeaderStorageKey(kLocalCacheGuid)),
-              NotNull());
+  EXPECT_THAT(
+      GetDataForCommit(SessionStore::GetHeaderStorageKey(kLocalCacheGuid)),
+      NotNull());
 }
 
 // Verifies that receiving an empty update list does not broadcast a foreign
@@ -1850,7 +1851,7 @@
   InitializeBridge();
   StartSyncing();
 
-  std::unique_ptr<EntityData> tab_data = GetData(
+  std::unique_ptr<EntityData> tab_data = GetDataForCommit(
       SessionStore::GetTabStorageKey(kLocalCacheGuid, /*tab_node_id=*/0));
   ASSERT_THAT(tab_data, NotNull());
 
diff --git a/components/tpcd/metadata/browser/manager.cc b/components/tpcd/metadata/browser/manager.cc
index 7f4e09a..4c0ab320 100644
--- a/components/tpcd/metadata/browser/manager.cc
+++ b/components/tpcd/metadata/browser/manager.cc
@@ -138,8 +138,7 @@
 
     std::optional<content_settings::mojom::TpcdMetadataCohort> cohort;
 
-    if (!Parser::IsDtrpEligible(
-            Parser::ToRuleSource(metadata_entry.source())) ||
+    if (!metadata_entry.has_dtrp() ||
         !base::FeatureList::IsEnabled(
             net::features::kTpcdMetadataStageControl)) {
       cohort = content_settings::mojom::TpcdMetadataCohort::DEFAULT;
@@ -231,8 +230,7 @@
   }
 
   auto reset_cohort = [&](const MetadataEntry& metadata_entry) -> bool {
-    return Parser::IsDtrpEligible(
-        Parser::ToRuleSource(metadata_entry.source()));
+    return metadata_entry.has_dtrp();
   };
   SetGrants(BuildGrantsWithPredicate(reset_cohort));
 }
diff --git a/components/tpcd/metadata/browser/manager_unittest.cc b/components/tpcd/metadata/browser/manager_unittest.cc
index 265b2b1..5c6800c1 100644
--- a/components/tpcd/metadata/browser/manager_unittest.cc
+++ b/components/tpcd/metadata/browser/manager_unittest.cc
@@ -12,6 +12,7 @@
 #include <tuple>
 
 #include "base/run_loop.h"
+#include "base/strings/strcat.h"
 #include "base/test/bind.h"
 #include "base/test/gtest_util.h"
 #include "base/test/metrics/histogram_tester.h"
@@ -202,11 +203,9 @@
   }
 }
 
-class ManagerCohortsTest
-    : public testing::Test,
-      public testing::WithParamInterface<std::tuple<
-          /*IsTpcdMetadataStagedRollbackEnabled:*/ bool,
-          /*content_settings::mojom::TpcdMetadataRuleSource:*/ int32_t>> {
+class ManagerCohortsTest : public testing::Test,
+                           public testing::WithParamInterface<
+                               /*IsTpcdMetadataStagedRollbackEnabled:*/ bool> {
  public:
   ManagerCohortsTest() = default;
   ~ManagerCohortsTest() override = default;
@@ -225,11 +224,7 @@
     return manager_.get();
   }
 
-  bool IsTpcdMetadataStagedRollbackEnabled() { return std::get<0>(GetParam()); }
-  content_settings::mojom::TpcdMetadataRuleSource GetTpcdMetadataRuleSource() {
-    return static_cast<content_settings::mojom::TpcdMetadataRuleSource>(
-        std::get<1>(GetParam()));
-  }
+  bool IsTpcdMetadataStagedRollbackEnabled() { return GetParam(); }
 
   std::string ToRuleSourceStr(
       const content_settings::mojom::TpcdMetadataRuleSource& rule_source) {
@@ -287,53 +282,7 @@
 INSTANTIATE_TEST_SUITE_P(
     /* no label */,
     ManagerCohortsTest,
-    testing::Combine(
-        testing::Bool(),
-        testing::Range<int32_t>(
-            static_cast<int32_t>(
-                content_settings::mojom::TpcdMetadataRuleSource::kMinValue),
-            static_cast<int32_t>(
-                content_settings::mojom::TpcdMetadataRuleSource::kMaxValue) +
-                1,
-            /*step=*/1)));
-
-TEST_P(ManagerCohortsTest, DTRP_Eligibility) {
-  const std::string primary_pattern_spec = "https://www.der.com";
-  const std::string secondary_pattern_spec = "https://www.foo.com";
-
-  Metadata metadata;
-  helpers::AddEntryToMetadata(metadata, primary_pattern_spec,
-                              secondary_pattern_spec,
-                              ToRuleSourceStr(GetTpcdMetadataRuleSource()));
-  EXPECT_EQ(metadata.metadata_entries_size(), 1);
-
-  Manager* manager = GetManager();
-  GetParser()->ParseMetadata(metadata.SerializeAsString());
-
-  EXPECT_EQ(manager->GetGrants().size(), 1u);
-  auto grant = manager->GetGrants().front();
-  EXPECT_EQ(grant.primary_pattern.ToString(), primary_pattern_spec);
-  EXPECT_EQ(grant.secondary_pattern.ToString(), secondary_pattern_spec);
-
-  auto rule_source = grant.metadata.tpcd_metadata_rule_source();
-  EXPECT_EQ(rule_source, GetTpcdMetadataRuleSource());
-
-  switch (GetTpcdMetadataRuleSource()) {
-    case content_settings::mojom::TpcdMetadataRuleSource::SOURCE_TEST:
-    case content_settings::mojom::TpcdMetadataRuleSource::SOURCE_1P_DT:
-    case content_settings::mojom::TpcdMetadataRuleSource::SOURCE_3P_DT:
-      EXPECT_TRUE(Parser::IsDtrpEligible(rule_source));
-      break;
-    case content_settings::mojom::TpcdMetadataRuleSource::SOURCE_DOGFOOD:
-    case content_settings::mojom::TpcdMetadataRuleSource::
-        SOURCE_CRITICAL_SECTOR:
-    case content_settings::mojom::TpcdMetadataRuleSource::SOURCE_CUJ:
-    case content_settings::mojom::TpcdMetadataRuleSource::SOURCE_GOV_EDU_TLD:
-    case content_settings::mojom::TpcdMetadataRuleSource::SOURCE_UNSPECIFIED:
-      EXPECT_FALSE(Parser::IsDtrpEligible(rule_source));
-      break;
-  }
-}
+    testing::Bool());
 
 TEST_P(ManagerCohortsTest, DTRP_0Percent) {
   const uint32_t dtrp_being_tested = 0;
@@ -342,9 +291,9 @@
   const std::string secondary_pattern_spec = "https://www.foo.com";
 
   Metadata metadata;
-  helpers::AddEntryToMetadata(
-      metadata, primary_pattern_spec, secondary_pattern_spec,
-      ToRuleSourceStr(GetTpcdMetadataRuleSource()), dtrp_being_tested);
+  helpers::AddEntryToMetadata(metadata, primary_pattern_spec,
+                              secondary_pattern_spec, Parser::kSourceTest,
+                              dtrp_being_tested);
   EXPECT_EQ(metadata.metadata_entries_size(), 1);
 
   Manager* manager = GetManager();
@@ -355,13 +304,10 @@
   EXPECT_EQ(grant.primary_pattern.ToString(), primary_pattern_spec);
   EXPECT_EQ(grant.secondary_pattern.ToString(), secondary_pattern_spec);
 
-  auto rule_source = grant.metadata.tpcd_metadata_rule_source();
-  EXPECT_EQ(rule_source, GetTpcdMetadataRuleSource());
-
-  EXPECT_EQ(grant.metadata.tpcd_metadata_elected_dtrp(), dtrp_being_tested);
-  auto picked_cohort = grant.metadata.tpcd_metadata_cohort();
+  auto picked_cohort =
+      manager->GetGrants().front().metadata.tpcd_metadata_cohort();
   if (IsTpcdMetadataStagedRollbackEnabled() &&
-      Parser::IsDtrpEligible(rule_source)) {
+      metadata.metadata_entries().begin()->has_dtrp()) {
     EXPECT_EQ(
         picked_cohort,
         content_settings::mojom::TpcdMetadataCohort::GRACE_PERIOD_FORCED_ON);
@@ -378,9 +324,9 @@
   const std::string secondary_pattern_spec = "https://www.foo.com";
 
   Metadata metadata;
-  helpers::AddEntryToMetadata(
-      metadata, primary_pattern_spec, secondary_pattern_spec,
-      ToRuleSourceStr(GetTpcdMetadataRuleSource()), dtrp_being_tested);
+  helpers::AddEntryToMetadata(metadata, primary_pattern_spec,
+                              secondary_pattern_spec, Parser::kSourceTest,
+                              dtrp_being_tested);
   EXPECT_EQ(metadata.metadata_entries_size(), 1);
 
   Manager* manager = GetManager();
@@ -391,13 +337,10 @@
   EXPECT_EQ(grant.primary_pattern.ToString(), primary_pattern_spec);
   EXPECT_EQ(grant.secondary_pattern.ToString(), secondary_pattern_spec);
 
-  auto rule_source = grant.metadata.tpcd_metadata_rule_source();
-  EXPECT_EQ(rule_source, GetTpcdMetadataRuleSource());
-
-  EXPECT_EQ(grant.metadata.tpcd_metadata_elected_dtrp(), dtrp_being_tested);
-  auto picked_cohort = grant.metadata.tpcd_metadata_cohort();
+  auto picked_cohort =
+      manager->GetGrants().front().metadata.tpcd_metadata_cohort();
   if (IsTpcdMetadataStagedRollbackEnabled() &&
-      Parser::IsDtrpEligible(rule_source)) {
+      metadata.metadata_entries().begin()->has_dtrp()) {
     EXPECT_EQ(
         picked_cohort,
         content_settings::mojom::TpcdMetadataCohort::GRACE_PERIOD_FORCED_OFF);
@@ -417,9 +360,9 @@
   const std::string secondary_pattern_spec = "https://www.foo.com";
 
   Metadata metadata;
-  helpers::AddEntryToMetadata(
-      metadata, primary_pattern_spec, secondary_pattern_spec,
-      ToRuleSourceStr(GetTpcdMetadataRuleSource()), dtrp_being_tested);
+  helpers::AddEntryToMetadata(metadata, primary_pattern_spec,
+                              secondary_pattern_spec, Parser::kSourceTest,
+                              dtrp_being_tested);
   EXPECT_EQ(metadata.metadata_entries_size(), 1);
 
   Manager* manager = GetManager();
@@ -432,13 +375,10 @@
   EXPECT_EQ(grant.primary_pattern.ToString(), primary_pattern_spec);
   EXPECT_EQ(grant.secondary_pattern.ToString(), secondary_pattern_spec);
 
-  auto rule_source = grant.metadata.tpcd_metadata_rule_source();
-  EXPECT_EQ(rule_source, GetTpcdMetadataRuleSource());
-
-  EXPECT_EQ(grant.metadata.tpcd_metadata_elected_dtrp(), dtrp_being_tested);
-  auto picked_cohort = grant.metadata.tpcd_metadata_cohort();
+  auto picked_cohort =
+      manager->GetGrants().front().metadata.tpcd_metadata_cohort();
   if (IsTpcdMetadataStagedRollbackEnabled() &&
-      Parser::IsDtrpEligible(rule_source)) {
+      metadata.metadata_entries().begin()->has_dtrp()) {
     EXPECT_EQ(
         picked_cohort,
         content_settings::mojom::TpcdMetadataCohort::GRACE_PERIOD_FORCED_OFF);
@@ -458,9 +398,9 @@
   const std::string secondary_pattern_spec = "https://www.foo.com";
 
   Metadata metadata;
-  helpers::AddEntryToMetadata(
-      metadata, primary_pattern_spec, secondary_pattern_spec,
-      ToRuleSourceStr(GetTpcdMetadataRuleSource()), dtrp_being_tested);
+  helpers::AddEntryToMetadata(metadata, primary_pattern_spec,
+                              secondary_pattern_spec, Parser::kSourceTest,
+                              dtrp_being_tested);
   EXPECT_EQ(metadata.metadata_entries_size(), 1);
 
   Manager* manager = GetManager();
@@ -473,13 +413,10 @@
   EXPECT_EQ(grant.primary_pattern.ToString(), primary_pattern_spec);
   EXPECT_EQ(grant.secondary_pattern.ToString(), secondary_pattern_spec);
 
-  auto rule_source = grant.metadata.tpcd_metadata_rule_source();
-  EXPECT_EQ(rule_source, GetTpcdMetadataRuleSource());
-
-  EXPECT_EQ(grant.metadata.tpcd_metadata_elected_dtrp(), dtrp_being_tested);
-  auto picked_cohort = grant.metadata.tpcd_metadata_cohort();
+  auto picked_cohort =
+      manager->GetGrants().front().metadata.tpcd_metadata_cohort();
   if (IsTpcdMetadataStagedRollbackEnabled() &&
-      Parser::IsDtrpEligible(rule_source)) {
+      metadata.metadata_entries().begin()->has_dtrp()) {
     EXPECT_EQ(
         picked_cohort,
         content_settings::mojom::TpcdMetadataCohort::GRACE_PERIOD_FORCED_ON);
@@ -490,20 +427,24 @@
 }
 
 TEST_P(ManagerCohortsTest, MetadataCohortDistributionUma) {
-  std::optional<uint32_t> dtrp =
-      Parser::IsDtrpEligible(GetTpcdMetadataRuleSource()) ? std::optional(0)
-                                                          : std::nullopt;
-
   Metadata metadata;
-  MetadataEntry* me = helpers::AddEntryToMetadata(
-      metadata, "*", "*", ToRuleSourceStr(GetTpcdMetadataRuleSource()), dtrp);
-  EXPECT_TRUE(Parser::IsValidMetadata(metadata));
+  for (const auto& source : testing::Range<int32_t>(
+           static_cast<int32_t>(TpcdMetadataRuleSource::kMinValue),
+           static_cast<int32_t>(TpcdMetadataRuleSource::kMaxValue) + 1,
+           /*step=*/1)) {
+    helpers::AddEntryToMetadata(
+        metadata, "*",
+        base::StrCat({"[*.]foo-", base::NumberToString(source), ".com"}),
+        ToRuleSourceStr(static_cast<TpcdMetadataRuleSource>(source)),
+        /*dtrp=*/0);
+  }
+  helpers::AddEntryToMetadata(metadata, "*", "[*.]foo.com");
 
   base::HistogramTester histogram_tester;
   GetParser()->ParseMetadata(metadata.SerializeAsString());
-  EXPECT_EQ(GetManager()->GetGrants().size(), 1u);
+  EXPECT_EQ(GetManager()->GetGrants().size(), 9u);
 
-  if (IsTpcdMetadataStagedRollbackEnabled() && Parser::IsTestEntry(*me)) {
+  if (IsTpcdMetadataStagedRollbackEnabled()) {
     histogram_tester.ExpectUniqueSample(
         helpers::kMetadataCohortDistributionHistogram,
         content_settings::mojom::TpcdMetadataCohort::GRACE_PERIOD_FORCED_ON, 1);
diff --git a/components/tpcd/metadata/browser/parser.cc b/components/tpcd/metadata/browser/parser.cc
index c47b731..ab49f2a 100644
--- a/components/tpcd/metadata/browser/parser.cc
+++ b/components/tpcd/metadata/browser/parser.cc
@@ -53,23 +53,6 @@
 }
 
 // static
-bool Parser::IsDtrpEligible(const TpcdMetadataRuleSource& rule_source) {
-  switch (rule_source) {
-    case content_settings::mojom::TpcdMetadataRuleSource::SOURCE_TEST:
-    case content_settings::mojom::TpcdMetadataRuleSource::SOURCE_1P_DT:
-    case content_settings::mojom::TpcdMetadataRuleSource::SOURCE_3P_DT:
-      return true;
-    case content_settings::mojom::TpcdMetadataRuleSource::SOURCE_UNSPECIFIED:
-    case content_settings::mojom::TpcdMetadataRuleSource::SOURCE_DOGFOOD:
-    case content_settings::mojom::TpcdMetadataRuleSource::
-        SOURCE_CRITICAL_SECTOR:
-    case content_settings::mojom::TpcdMetadataRuleSource::SOURCE_CUJ:
-    case content_settings::mojom::TpcdMetadataRuleSource::SOURCE_GOV_EDU_TLD:
-      return false;
-  }
-}
-
-// static
 bool Parser::IsValidMetadata(const Metadata& metadata,
                              RecordInstallationResultCallback callback) {
   for (const tpcd::metadata::MetadataEntry& me : metadata.metadata_entries()) {
@@ -100,25 +83,15 @@
 
     if (base::FeatureList::IsEnabled(
             net::features::kTpcdMetadataStageControl)) {
-      if (tpcd::metadata::Parser::IsDtrpEligible(
-              tpcd::metadata::Parser::ToRuleSource(me.source()))) {
-        if (!me.has_dtrp() || !IsValidDtrp(me.dtrp())) {
-          if (callback) {
-            std::move(callback).Run(InstallationResult::kErroneousDtrp);
-          }
-          return false;
-        } else if (me.has_dtrp_override() && !IsValidDtrp(me.dtrp_override())) {
-          if (callback) {
-            std::move(callback).Run(InstallationResult::kErroneousDtrp);
-          }
-          return false;
-        }
-      } else if (me.has_dtrp() || me.has_dtrp_override()) {
-        // Catching this cases as they could point to a server-side
-        // misconfiguration of the TPCD metadata generally stemming from human
-        // errors.
+      if (me.has_dtrp() && !IsValidDtrp(me.dtrp())) {
         if (callback) {
-          std::move(callback).Run(InstallationResult::kIllicitDtrp);
+          std::move(callback).Run(InstallationResult::kErroneousDtrp);
+        }
+        return false;
+      } else if (me.has_dtrp_override() &&
+                 (!me.has_dtrp() || !IsValidDtrp(me.dtrp_override()))) {
+        if (callback) {
+          std::move(callback).Run(InstallationResult::kErroneousDtrp);
         }
         return false;
       }
@@ -240,6 +213,9 @@
   if (dtrp_override.has_value()) {
     me->set_dtrp_override(dtrp_override.value());
   }
+
+  DCHECK(Parser::IsValidMetadata(metadata));
+
   return me;
 }
 }  // namespace helpers
diff --git a/components/tpcd/metadata/browser/parser.h b/components/tpcd/metadata/browser/parser.h
index ba2b218..0b20cb75 100644
--- a/components/tpcd/metadata/browser/parser.h
+++ b/components/tpcd/metadata/browser/parser.h
@@ -111,9 +111,6 @@
   // `content_settings::RuleSource` enum value.
   static TpcdMetadataRuleSource ToRuleSource(const std::string& source);
 
-  // Returns true if the given source of the MetadataEntry matches the defined
-  // sources for which `dtrp` is expected to be set.
-  static bool IsDtrpEligible(const TpcdMetadataRuleSource& rule_source);
   static bool IsValidMetadata(
       const Metadata& metadata,
       RecordInstallationResultCallback callback = base::NullCallback());
diff --git a/components/trusted_vault/BUILD.gn b/components/trusted_vault/BUILD.gn
index adf158ee..3124f53 100644
--- a/components/trusted_vault/BUILD.gn
+++ b/components/trusted_vault/BUILD.gn
@@ -163,6 +163,8 @@
     sources += [
       "test/fake_security_domains_server.cc",
       "test/fake_security_domains_server.h",
+      "test/mock_trusted_vault_connection.cc",
+      "test/mock_trusted_vault_connection.h",
     ]
 
     public_deps += [
diff --git a/components/trusted_vault/standalone_trusted_vault_backend_unittest.cc b/components/trusted_vault/standalone_trusted_vault_backend_unittest.cc
index cd81f7e..0d34e59 100644
--- a/components/trusted_vault/standalone_trusted_vault_backend_unittest.cc
+++ b/components/trusted_vault/standalone_trusted_vault_backend_unittest.cc
@@ -36,6 +36,7 @@
 #include "components/trusted_vault/recovery_key_store_controller.h"
 #include "components/trusted_vault/securebox.h"
 #include "components/trusted_vault/test/mock_recovery_key_store_connection.h"
+#include "components/trusted_vault/test/mock_trusted_vault_connection.h"
 #include "components/trusted_vault/trusted_vault_connection.h"
 #include "components/trusted_vault/trusted_vault_histograms.h"
 #include "components/trusted_vault/trusted_vault_server_constants.h"
@@ -146,44 +147,6 @@
   MOCK_METHOD(void, NotifyStateChanged, (), (override));
 };
 
-class MockTrustedVaultConnection : public TrustedVaultConnection {
- public:
-  MockTrustedVaultConnection() = default;
-  ~MockTrustedVaultConnection() override = default;
-  MOCK_METHOD(std::unique_ptr<Request>,
-              RegisterAuthenticationFactor,
-              (const CoreAccountInfo& account_info,
-               const MemberKeysSource& member_keys_source,
-               const SecureBoxPublicKey& authentication_factor_public_key,
-               AuthenticationFactorType authentication_factor_type,
-               RegisterAuthenticationFactorCallback callback),
-              (override));
-  MOCK_METHOD(std::unique_ptr<Request>,
-              RegisterDeviceWithoutKeys,
-              (const CoreAccountInfo& account_info,
-               const SecureBoxPublicKey& device_public_key,
-               RegisterAuthenticationFactorCallback callback),
-              (override));
-  MOCK_METHOD(
-      std::unique_ptr<Request>,
-      DownloadNewKeys,
-      (const CoreAccountInfo& account_info,
-       const TrustedVaultKeyAndVersion& last_trusted_vault_key_and_version,
-       std::unique_ptr<SecureBoxKeyPair> device_key_pair,
-       DownloadNewKeysCallback callback),
-      (override));
-  MOCK_METHOD(std::unique_ptr<Request>,
-              DownloadIsRecoverabilityDegraded,
-              (const CoreAccountInfo& account_info,
-               IsRecoverabilityDegradedCallback),
-              (override));
-  MOCK_METHOD(std::unique_ptr<Request>,
-              DownloadAuthenticationFactorsRegistrationState,
-              (const CoreAccountInfo& account_info,
-               DownloadAuthenticationFactorsRegistrationStateCallback callback),
-              (override));
-};
-
 class FakeRecoveryKeyProvider
     : public RecoveryKeyStoreController::RecoveryKeyProvider {
  public:
diff --git a/components/trusted_vault/test/mock_trusted_vault_connection.cc b/components/trusted_vault/test/mock_trusted_vault_connection.cc
new file mode 100644
index 0000000..ca5f6c7e
--- /dev/null
+++ b/components/trusted_vault/test/mock_trusted_vault_connection.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 "components/trusted_vault/test/mock_trusted_vault_connection.h"
+
+namespace trusted_vault {
+
+MockTrustedVaultConnection::MockTrustedVaultConnection() = default;
+
+MockTrustedVaultConnection::~MockTrustedVaultConnection() = default;
+
+}  // namespace trusted_vault
diff --git a/components/trusted_vault/test/mock_trusted_vault_connection.h b/components/trusted_vault/test/mock_trusted_vault_connection.h
new file mode 100644
index 0000000..45502d0f
--- /dev/null
+++ b/components/trusted_vault/test/mock_trusted_vault_connection.h
@@ -0,0 +1,57 @@
+// 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 COMPONENTS_TRUSTED_VAULT_TEST_MOCK_TRUSTED_VAULT_CONNECTION_H_
+#define COMPONENTS_TRUSTED_VAULT_TEST_MOCK_TRUSTED_VAULT_CONNECTION_H_
+
+#include <memory>
+
+#include "components/signin/public/identity_manager/account_info.h"
+#include "components/trusted_vault/securebox.h"
+#include "components/trusted_vault/trusted_vault_connection.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace trusted_vault {
+
+class MockTrustedVaultConnection : public TrustedVaultConnection {
+ public:
+  MockTrustedVaultConnection();
+  ~MockTrustedVaultConnection() override;
+  MOCK_METHOD(std::unique_ptr<Request>,
+              RegisterAuthenticationFactor,
+              (const CoreAccountInfo& account_info,
+               const MemberKeysSource& member_keys_source,
+               const SecureBoxPublicKey& authentication_factor_public_key,
+               AuthenticationFactorType authentication_factor_type,
+               RegisterAuthenticationFactorCallback callback),
+              (override));
+  MOCK_METHOD(std::unique_ptr<Request>,
+              RegisterDeviceWithoutKeys,
+              (const CoreAccountInfo& account_info,
+               const SecureBoxPublicKey& device_public_key,
+               RegisterAuthenticationFactorCallback callback),
+              (override));
+  MOCK_METHOD(
+      std::unique_ptr<Request>,
+      DownloadNewKeys,
+      (const CoreAccountInfo& account_info,
+       const TrustedVaultKeyAndVersion& last_trusted_vault_key_and_version,
+       std::unique_ptr<SecureBoxKeyPair> device_key_pair,
+       DownloadNewKeysCallback callback),
+      (override));
+  MOCK_METHOD(std::unique_ptr<Request>,
+              DownloadIsRecoverabilityDegraded,
+              (const CoreAccountInfo& account_info,
+               IsRecoverabilityDegradedCallback callback),
+              (override));
+  MOCK_METHOD(std::unique_ptr<Request>,
+              DownloadAuthenticationFactorsRegistrationState,
+              (const CoreAccountInfo& account_info,
+               DownloadAuthenticationFactorsRegistrationStateCallback callback),
+              (override));
+};
+
+}  // namespace trusted_vault
+
+#endif  // COMPONENTS_TRUSTED_VAULT_TEST_MOCK_TRUSTED_VAULT_CONNECTION_H_
diff --git a/components/trusted_vault/trusted_vault_degraded_recoverability_handler_unittest.cc b/components/trusted_vault/trusted_vault_degraded_recoverability_handler_unittest.cc
index 36fc1751..7ef4355 100644
--- a/components/trusted_vault/trusted_vault_degraded_recoverability_handler_unittest.cc
+++ b/components/trusted_vault/trusted_vault_degraded_recoverability_handler_unittest.cc
@@ -8,6 +8,7 @@
 #include <string>
 #include <utility>
 #include <vector>
+
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/mock_callback.h"
 #include "base/test/task_environment.h"
@@ -17,6 +18,7 @@
 #include "components/trusted_vault/proto/local_trusted_vault.pb.h"
 #include "components/trusted_vault/proto_time_conversion.h"
 #include "components/trusted_vault/securebox.h"
+#include "components/trusted_vault/test/mock_trusted_vault_connection.h"
 #include "components/trusted_vault/trusted_vault_connection.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -42,44 +44,6 @@
              expected_state.last_refresh_time_millis_since_unix_epoch();
 }
 
-class MockTrustedVaultConnection : public TrustedVaultConnection {
- public:
-  MockTrustedVaultConnection() = default;
-  ~MockTrustedVaultConnection() override = default;
-  MOCK_METHOD(std::unique_ptr<Request>,
-              RegisterAuthenticationFactor,
-              (const CoreAccountInfo& account_info,
-               const MemberKeysSource& member_keys_source,
-               const SecureBoxPublicKey& authentication_factor_public_key,
-               AuthenticationFactorType authentication_factor_type,
-               RegisterAuthenticationFactorCallback callback),
-              (override));
-  MOCK_METHOD(std::unique_ptr<Request>,
-              RegisterDeviceWithoutKeys,
-              (const CoreAccountInfo& account_info,
-               const SecureBoxPublicKey& device_public_key,
-               RegisterAuthenticationFactorCallback callback),
-              (override));
-  MOCK_METHOD(
-      std::unique_ptr<Request>,
-      DownloadNewKeys,
-      (const CoreAccountInfo& account_info,
-       const TrustedVaultKeyAndVersion& last_trusted_vault_key_and_version,
-       std::unique_ptr<SecureBoxKeyPair> device_key_pair,
-       DownloadNewKeysCallback callback),
-      (override));
-  MOCK_METHOD(std::unique_ptr<Request>,
-              DownloadIsRecoverabilityDegraded,
-              (const CoreAccountInfo& account_info,
-               IsRecoverabilityDegradedCallback callback),
-              (override));
-  MOCK_METHOD(std::unique_ptr<Request>,
-              DownloadAuthenticationFactorsRegistrationState,
-              (const CoreAccountInfo& account_info,
-               DownloadAuthenticationFactorsRegistrationStateCallback callback),
-              (override));
-};
-
 class MockDelegate
     : public TrustedVaultDegradedRecoverabilityHandler::Delegate {
  public:
diff --git a/components/visited_url_ranking/internal/session_url_visit_data_fetcher_unittest.cc b/components/visited_url_ranking/internal/session_url_visit_data_fetcher_unittest.cc
index a0c4e5da..aea1f4e 100644
--- a/components/visited_url_ranking/internal/session_url_visit_data_fetcher_unittest.cc
+++ b/components/visited_url_ranking/internal/session_url_visit_data_fetcher_unittest.cc
@@ -10,6 +10,7 @@
 #include "base/callback_list.h"
 #include "base/functional/callback.h"
 #include "base/test/mock_callback.h"
+#include "base/test/task_environment.h"
 #include "base/time/time.h"
 #include "components/sync_sessions/open_tabs_ui_delegate.h"
 #include "components/sync_sessions/session_sync_service.h"
@@ -157,8 +158,29 @@
             testing::Invoke([this]() { return &open_tabs_ui_delegate_; }));
   }
 
+  FetchResult FetchAndGetResult(const FetchOptions& options) {
+    FetchResult result = FetchResult(FetchResult::Status::kError, {});
+    base::RunLoop wait_loop;
+    auto session_url_visit_data_fetcher =
+        SessionURLVisitDataFetcher(&mock_session_sync_service_);
+    session_url_visit_data_fetcher.FetchURLVisitData(
+        options, base::BindOnce(
+                     [](base::OnceClosure stop_waiting, FetchResult* result,
+                        FetchResult result_arg) {
+                       result->status = result_arg.status;
+                       result->data = std::move(result_arg.data);
+                       std::move(stop_waiting).Run();
+                     },
+                     wait_loop.QuitClosure(), &result));
+    wait_loop.Run();
+    return result;
+  }
+
  protected:
   sync_sessions::MockOpenTabsUIDelegate open_tabs_ui_delegate_;
+
+ private:
+  base::test::TaskEnvironment task_env_;
   sync_sessions::MockSessionSyncService mock_session_sync_service_;
 };
 
@@ -183,19 +205,9 @@
             return true;
           }));
 
-  auto result = FetchResult(FetchResult::Status::kError, {});
-  base::MockCallback<URLVisitDataFetcher::FetchResultCallback> callback;
-  EXPECT_CALL(callback, Run(testing::_))
-      .Times(1)
-      .WillOnce(testing::Invoke(
-          [&result](FetchResult arg) { result = std::move(arg); }));
-  auto session_url_visit_fetcher =
-      SessionURLVisitDataFetcher(&mock_session_sync_service_);
   base::Time yesterday = base::Time::Now() - base::Days(1);
-  session_url_visit_fetcher.FetchURLVisitData(
-      FetchOptions({{Fetcher::kSession, FetchOptions::kOriginSources}},
-                   yesterday),
-      callback.Get());
+  auto result = FetchAndGetResult(FetchOptions(
+      {{Fetcher::kSession, FetchOptions::kOriginSources}}, yesterday));
   EXPECT_EQ(result.status, FetchResult::Status::kSuccess);
   EXPECT_EQ(result.data.size(), 2u);
   for (const auto& url_key : {kSampleSearchUrl, kSampleSearchUrl2}) {
@@ -234,18 +246,9 @@
             }));
   }
 
-  auto result = FetchResult(FetchResult::Status::kError, {});
-  base::MockCallback<URLVisitDataFetcher::FetchResultCallback> callback;
-  EXPECT_CALL(callback, Run(testing::_))
-      .Times(1)
-      .WillOnce(testing::Invoke(
-          [&result](FetchResult arg) { result = std::move(arg); }));
-
-  auto session_url_visit_fetcher =
-      SessionURLVisitDataFetcher(&mock_session_sync_service_);
   auto options = FetchOptions({{Fetcher::kSession, {source}}},
                               base::Time::Now() - base::Days(1));
-  session_url_visit_fetcher.FetchURLVisitData(options, callback.Get());
+  auto result = FetchAndGetResult(options);
   EXPECT_EQ(result.status, FetchResult::Status::kSuccess);
   EXPECT_EQ(result.data.size(), 2u);
   for (const auto& url_key : {kSampleSearchUrl, kSampleSearchUrl2}) {
diff --git a/components/visited_url_ranking/public/BUILD.gn b/components/visited_url_ranking/public/BUILD.gn
index a5eca54..3899aa32 100644
--- a/components/visited_url_ranking/public/BUILD.gn
+++ b/components/visited_url_ranking/public/BUILD.gn
@@ -23,3 +23,19 @@
     "//url",
   ]
 }
+
+source_set("test_support") {
+  testonly = true
+
+  sources = [
+    "testing/mock_visited_url_ranking_service.cc",
+    "testing/mock_visited_url_ranking_service.h",
+  ]
+
+  deps = [
+    ":public",
+    "//base",
+    "//testing/gmock",
+    "//testing/gtest",
+  ]
+}
diff --git a/components/visited_url_ranking/public/testing/mock_visited_url_ranking_service.cc b/components/visited_url_ranking/public/testing/mock_visited_url_ranking_service.cc
new file mode 100644
index 0000000..9e93c5c0
--- /dev/null
+++ b/components/visited_url_ranking/public/testing/mock_visited_url_ranking_service.cc
@@ -0,0 +1,12 @@
+// 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 "components/visited_url_ranking/public/testing/mock_visited_url_ranking_service.h"
+
+namespace visited_url_ranking {
+
+MockVisitedURLRankingService::MockVisitedURLRankingService() = default;
+MockVisitedURLRankingService::~MockVisitedURLRankingService() = default;
+
+}  // namespace visited_url_ranking
diff --git a/components/visited_url_ranking/public/testing/mock_visited_url_ranking_service.h b/components/visited_url_ranking/public/testing/mock_visited_url_ranking_service.h
new file mode 100644
index 0000000..ae54b72e
--- /dev/null
+++ b/components/visited_url_ranking/public/testing/mock_visited_url_ranking_service.h
@@ -0,0 +1,33 @@
+// 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 COMPONENTS_VISITED_URL_RANKING_PUBLIC_TESTING_MOCK_VISITED_URL_RANKING_SERVICE_H_
+#define COMPONENTS_VISITED_URL_RANKING_PUBLIC_TESTING_MOCK_VISITED_URL_RANKING_SERVICE_H_
+
+#include "components/visited_url_ranking/public/visited_url_ranking_service.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace visited_url_ranking {
+
+class MockVisitedURLRankingService : public VisitedURLRankingService {
+ public:
+  MockVisitedURLRankingService();
+  MockVisitedURLRankingService(const MockVisitedURLRankingService&) = delete;
+  MockVisitedURLRankingService& operator=(const MockVisitedURLRankingService&) =
+      delete;
+  ~MockVisitedURLRankingService() override;
+
+  MOCK_METHOD2(FetchURLVisitAggregates,
+               void(const FetchOptions& options,
+                    GetURLVisitAggregatesCallback callback));
+
+  MOCK_METHOD3(RankURLVisitAggregates,
+               void(const Config& config,
+                    std::vector<URLVisitAggregate> visits,
+                    RankVisitAggregatesCallback callback));
+};
+
+}  // namespace visited_url_ranking
+
+#endif  // COMPONENTS_VISITED_URL_RANKING_PUBLIC_TESTING_MOCK_VISITED_URL_RANKING_SERVICE_H_
diff --git a/components/viz/common/features.cc b/components/viz/common/features.cc
index f11d1da..c5e6e6d 100644
--- a/components/viz/common/features.cc
+++ b/components/viz/common/features.cc
@@ -262,11 +262,7 @@
 // SkiaOutputDeviceBufferQueue itself.
 BASE_FEATURE(kRendererAllocatesImages,
              "RendererAllocatesImages",
-#if BUILDFLAG(IS_MAC)
              base::FEATURE_ENABLED_BY_DEFAULT
-#else
-             base::FEATURE_DISABLED_BY_DEFAULT
-#endif
 );
 #endif
 
diff --git a/components/viz/service/display/direct_renderer.cc b/components/viz/service/display/direct_renderer.cc
index 6c495e3..64ebc62 100644
--- a/components/viz/service/display/direct_renderer.cc
+++ b/components/viz/service/display/direct_renderer.cc
@@ -635,12 +635,6 @@
 
   if (can_skip_rp) {
     skipped_render_pass_ids_.insert(render_pass->id);
-
-    int pixel_size =
-        render_pass->output_rect.width() * render_pass->output_rect.height();
-    UMA_HISTOGRAM_COUNTS_10M(
-        "Compositing.DirectRenderer.SkippedNonRootRenderPassOutputSize",
-        pixel_size);
     return;
   }
 
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc b/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc
index 355c0f92..bbddb22 100644
--- a/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc
+++ b/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc
@@ -2335,9 +2335,7 @@
     return true;
   }
 #elif BUILDFLAG(IS_MAC)
-  if (features::UseGpuVsync()) {
-    presenter_->SetVSyncDisplayID(renderer_settings_.display_id);
-  }
+  presenter_->SetVSyncDisplayID(renderer_settings_.display_id);
 #elif BUILDFLAG(IS_CHROMEOS)
   if (!presenter_) {
     return false;
@@ -2374,9 +2372,7 @@
     CHECK(presenter_);
 
 #if BUILDFLAG(IS_MAC)
-    if (features::UseGpuVsync()) {
-      presenter_->SetVSyncDisplayID(renderer_settings_.display_id);
-    }
+    presenter_->SetVSyncDisplayID(renderer_settings_.display_id);
 #endif  // BUILDFLAG(IS_MAC)
     output_device_ = std::make_unique<SkiaOutputDeviceBufferQueue>(
         std::make_unique<OutputPresenterGL>(
diff --git a/components/viz/service/frame_sinks/shared_image_interface_provider.cc b/components/viz/service/frame_sinks/shared_image_interface_provider.cc
index 4763757..fb92a5cf 100644
--- a/components/viz/service/frame_sinks/shared_image_interface_provider.cc
+++ b/components/viz/service/frame_sinks/shared_image_interface_provider.cc
@@ -7,6 +7,7 @@
 #include <memory>
 
 #include "base/functional/callback.h"
+#include "base/synchronization/lock.h"
 #include "base/synchronization/waitable_event.h"
 #include "components/viz/service/gl/gpu_service_impl.h"
 #include "gpu/command_buffer/service/scheduler_sequence.h"
@@ -24,6 +25,7 @@
 SharedImageInterfaceProvider::~SharedImageInterfaceProvider() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(gpu_sequence_checker_);
 
+  base::AutoLock hold(context_lock_);
   if (shared_context_state_) {
     shared_context_state_->RemoveContextLostObserver(this);
     shared_context_state_.reset();
@@ -38,11 +40,15 @@
   return shared_image_interface_.get();
 }
 
-bool SharedImageInterfaceProvider::NeedsNewSharedImageInterface() const {
-  return !shared_image_interface_ || !shared_context_state_;
+bool SharedImageInterfaceProvider::NeedsNewSharedImageInterface() {
+  base::AutoLock hold(context_lock_);
+  return !shared_image_interface_ || context_lost_;
 }
 
 void SharedImageInterfaceProvider::CreateSharedImageInterface() {
+  // This function should only be called on the compositor thread.
+  CHECK(!gpu_service_->main_runner()->BelongsToCurrentThread());
+
   if (!scheduler_sequence_) {
     // TODO(vmpstr): This can use compositor_gpu_task_runner instead. However,
     // we also then need to create a SharedContextState from the same runner.
@@ -54,9 +60,6 @@
         /*target_thread_is_always_available=*/true);
   }
 
-  // This function should only be called on the compositor thread.
-  CHECK(!gpu_service_->main_runner()->BelongsToCurrentThread());
-
   base::WaitableEvent event;
   scheduler_sequence_->ScheduleTask(
       base::BindOnce(
@@ -69,10 +72,12 @@
 void SharedImageInterfaceProvider::CreateSharedImageInterfaceOnGpu(
     base::WaitableEvent* event) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(gpu_sequence_checker_);
+  base::AutoLock hold(context_lock_);
   shared_context_state_ =
       gl::GetGLImplementation() != gl::kGLImplementationDisabled && gpu_service_
           ? gpu_service_->GetContextState()
           : nullptr;
+  context_lost_ = false;
 
   shared_image_interface_ =
       base::MakeRefCounted<gpu::SharedImageInterfaceInProcess>(
@@ -93,8 +98,10 @@
 
 void SharedImageInterfaceProvider::OnContextLost() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(gpu_sequence_checker_);
+  base::AutoLock hold(context_lock_);
   shared_context_state_->RemoveContextLostObserver(this);
   shared_context_state_ = nullptr;
+  context_lost_ = true;
 }
 
 }  // namespace viz
diff --git a/components/viz/service/frame_sinks/shared_image_interface_provider.h b/components/viz/service/frame_sinks/shared_image_interface_provider.h
index a997f4a..3e81bb6 100644
--- a/components/viz/service/frame_sinks/shared_image_interface_provider.h
+++ b/components/viz/service/frame_sinks/shared_image_interface_provider.h
@@ -8,6 +8,7 @@
 #include <memory>
 
 #include "base/sequence_checker.h"
+#include "base/synchronization/lock.h"
 #include "components/viz/service/viz_service_export.h"
 #include "gpu/command_buffer/service/shared_context_state.h"
 
@@ -36,16 +37,24 @@
   // Sequence checker for tasks that run on the gpu "thread".
   SEQUENCE_CHECKER(gpu_sequence_checker_);
 
-  bool NeedsNewSharedImageInterface() const;
+  bool NeedsNewSharedImageInterface();
 
   void CreateSharedImageInterface();
   void CreateSharedImageInterfaceOnGpu(base::WaitableEvent* event);
   void OnContextLost() override;
 
+  // These are accessed by both threads, but compositor threads blocks when GPU
+  // work thread is happening.
   const raw_ptr<GpuServiceImpl> gpu_service_;
-  scoped_refptr<gpu::SharedContextState> shared_context_state_;
   std::unique_ptr<gpu::SchedulerSequence> scheduler_sequence_;
   scoped_refptr<gpu::SharedImageInterfaceInProcess> shared_image_interface_;
+
+  // These are accessed by both threads and are synchronized by the lock.
+  base::Lock context_lock_;
+  // Shared context state is nullptr in software, and is never lost.
+  bool context_lost_ GUARDED_BY(context_lock_) = false;
+  scoped_refptr<gpu::SharedContextState> shared_context_state_
+      GUARDED_BY(context_lock_);
 };
 
 }  // namespace viz
diff --git a/components/webauthn/android/java/src/org/chromium/components/webauthn/Fido2CredentialRequest.java b/components/webauthn/android/java/src/org/chromium/components/webauthn/Fido2CredentialRequest.java
index 8324c0f..37c6516 100644
--- a/components/webauthn/android/java/src/org/chromium/components/webauthn/Fido2CredentialRequest.java
+++ b/components/webauthn/android/java/src/org/chromium/components/webauthn/Fido2CredentialRequest.java
@@ -113,7 +113,13 @@
      */
     public Fido2CredentialRequest(AuthenticationContextProvider authenticationContextProvider) {
         mAuthenticationContextProvider = authenticationContextProvider;
-        mPlayServicesAvailable = Fido2ApiCallHelper.getInstance().arePlayServicesAvailable();
+        boolean playServicesAvailable;
+        try {
+            playServicesAvailable = Fido2ApiCallHelper.getInstance().arePlayServicesAvailable();
+        } catch (Exception e) {
+            playServicesAvailable = false;
+        }
+        mPlayServicesAvailable = playServicesAvailable;
         mCredManHelper =
                 new CredManHelper(mAuthenticationContextProvider, this, mPlayServicesAvailable);
         mBarrier = new Barrier(this::returnErrorAndResetCallback);
diff --git a/content/browser/android/content_feature_map.cc b/content/browser/android/content_feature_map.cc
index 5b06e402..037721d 100644
--- a/content/browser/android/content_feature_map.cc
+++ b/content/browser/android/content_feature_map.cc
@@ -36,7 +36,6 @@
     &features::kOptimizeImmHideCalls,
     &features::kProcessSharingWithStrictSiteInstances,
     &features::kReduceGpuPriorityOnBackground,
-    &features::kRequestDesktopSiteWindowSetting,
     &features::kSelectionMenuItemModification,
     &features::kSmartZoom,
     &features::kTouchDragAndContextMenu,
diff --git a/content/browser/attribution_reporting/attribution_data_host_manager_impl.cc b/content/browser/attribution_reporting/attribution_data_host_manager_impl.cc
index 2459948..282ec1f9 100644
--- a/content/browser/attribution_reporting/attribution_data_host_manager_impl.cc
+++ b/content/browser/attribution_reporting/attribution_data_host_manager_impl.cc
@@ -767,10 +767,7 @@
           attribution_reporting::kAttributionReportingRegisterSourceHeader,
           &value)) {
         if (web_source_header.has_value()) {
-          MaybeLogAuditIssue(registrations.render_frame_id(), reporting_url,
-                             registrations.devtools_request_id(),
-                             /*invalid_parameter=*/std::nullopt,
-                             AttributionReportingIssueType::kSourceIgnored);
+          // TODO(https://crbug.com/40242261): Log an audit issue.
           return std::nullopt;
         }
         web_source_header = std::move(value);
@@ -786,10 +783,7 @@
           attribution_reporting::kAttributionReportingRegisterTriggerHeader,
           &value)) {
         if (web_trigger_header.has_value()) {
-          MaybeLogAuditIssue(registrations.render_frame_id(), reporting_url,
-                             registrations.devtools_request_id(),
-                             /*invalid_parameter=*/std::nullopt,
-                             AttributionReportingIssueType::kTriggerIgnored);
+          // TODO(https://crbug.com/40242261): Log an audit issue.
           return std::nullopt;
         }
         web_trigger_header = std::move(value);
diff --git a/content/browser/gpu/gpu_process_host.cc b/content/browser/gpu/gpu_process_host.cc
index 0c78fc4..c5a855e 100644
--- a/content/browser/gpu/gpu_process_host.cc
+++ b/content/browser/gpu/gpu_process_host.cc
@@ -928,7 +928,7 @@
     in_process_gpu_thread_.reset(GetGpuMainThreadFactory()(
         InProcessChildThreadParams(
             base::SingleThreadTaskRunner::GetCurrentDefault(),
-            process_->GetInProcessMojoInvitation()),
+            process_->GetInProcessMojoInvitation(), GetIOThreadTaskRunner()),
         gpu_preferences));
     base::Thread::Options options;
 #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
diff --git a/content/browser/indexed_db/file_path_util.cc b/content/browser/indexed_db/file_path_util.cc
index 41fa5ae..b9e3e4d 100644
--- a/content/browser/indexed_db/file_path_util.cc
+++ b/content/browser/indexed_db/file_path_util.cc
@@ -6,6 +6,8 @@
 
 #include <inttypes.h>
 
+#include "base/files/file_util.h"
+#include "base/metrics/histogram_functions.h"
 #include "base/strings/stringprintf.h"
 #include "components/services/storage/public/cpp/buckets/bucket_locator.h"
 #include "storage/common/database/database_identifier.h"
@@ -90,5 +92,32 @@
   return path;
 }
 
+bool IsPathTooLong(const base::FilePath& leveldb_dir) {
+  int limit = base::GetMaximumPathComponentLength(leveldb_dir.DirName());
+  if (limit < 0) {
+    DLOG(WARNING) << "GetMaximumPathComponentLength returned -1";
+// In limited testing, ChromeOS returns 143, other OSes 255.
+#if BUILDFLAG(IS_CHROMEOS)
+    limit = 143;
+#else
+    limit = 255;
+#endif
+  }
+  size_t component_length = leveldb_dir.BaseName().value().length();
+  if (component_length > static_cast<uint32_t>(limit)) {
+    DLOG(WARNING) << "Path component length (" << component_length
+                  << ") exceeds maximum (" << limit
+                  << ") allowed by this filesystem.";
+    const int min = 140;
+    const int max = 300;
+    const int num_buckets = 12;
+    base::UmaHistogramCustomCounts(
+        "WebCore.IndexedDB.BackingStore.OverlyLargeOriginLength",
+        component_length, min, max, num_buckets);
+    return true;
+  }
+  return false;
+}
+
 }  // namespace indexed_db
 }  // namespace content
diff --git a/content/browser/indexed_db/indexed_db_backing_store.cc b/content/browser/indexed_db/indexed_db_backing_store.cc
index 505f6bb..c548dfff 100644
--- a/content/browser/indexed_db/indexed_db_backing_store.cc
+++ b/content/browser/indexed_db/indexed_db_backing_store.cc
@@ -411,34 +411,6 @@
   return true;
 }
 
-bool IsPathTooLong(const base::FilePath& leveldb_dir) {
-  std::optional<int> limit =
-      base::GetMaximumPathComponentLength(leveldb_dir.DirName());
-  if (!limit.has_value()) {
-    DLOG(WARNING) << "GetMaximumPathComponentLength returned -1";
-// In limited testing, ChromeOS returns 143, other OSes 255.
-#if BUILDFLAG(IS_CHROMEOS)
-    limit = 143;
-#else
-    limit = 255;
-#endif
-  }
-  size_t component_length = leveldb_dir.BaseName().value().length();
-  if (component_length > static_cast<uint32_t>(*limit)) {
-    DLOG(WARNING) << "Path component length (" << component_length
-                  << ") exceeds maximum (" << *limit
-                  << ") allowed by this filesystem.";
-    const int min = 140;
-    const int max = 300;
-    const int num_buckets = 12;
-    base::UmaHistogramCustomCounts(
-        "WebCore.IndexedDB.BackingStore.OverlyLargeOriginLength",
-        component_length, min, max, num_buckets);
-    return true;
-  }
-  return false;
-}
-
 Status DeleteBlobsInRange(IndexedDBBackingStore::Transaction* transaction,
                           int64_t database_id,
                           const std::string& start_key,
@@ -1381,7 +1353,7 @@
     const std::string& message) {
   const base::FilePath info_path =
       path_base.Append(indexed_db::ComputeCorruptionFileName(bucket_locator));
-  if (IsPathTooLong(info_path)) {
+  if (indexed_db::IsPathTooLong(info_path)) {
     return false;
   }
 
diff --git a/content/browser/indexed_db/indexed_db_bucket_context.cc b/content/browser/indexed_db/indexed_db_bucket_context.cc
index ecf77bd..9042328 100644
--- a/content/browser/indexed_db/indexed_db_bucket_context.cc
+++ b/content/browser/indexed_db/indexed_db_bucket_context.cc
@@ -1062,6 +1062,7 @@
         }
 
         transaction_info->tid = transaction->id();
+        transaction_info->client_id = connection->client_id();
         transaction_info->age =
             (base::Time::Now() - transaction->diagnostics().creation_time)
                 .InMillisecondsF();
diff --git a/content/browser/indexed_db/indexed_db_leveldb_operations.cc b/content/browser/indexed_db/indexed_db_leveldb_operations.cc
index 90e7f93..8109153 100644
--- a/content/browser/indexed_db/indexed_db_leveldb_operations.cc
+++ b/content/browser/indexed_db/indexed_db_leveldb_operations.cc
@@ -8,7 +8,6 @@
 
 #include "base/files/file_util.h"
 #include "base/json/json_reader.h"
-#include "base/metrics/histogram_functions.h"
 #include "base/no_destructor.h"
 #include "base/values.h"
 #include "build/build_config.h"
@@ -54,38 +53,10 @@
       .Append(FILE_PATH_LITERAL("corruption_info.json"));
 }
 
-bool IsPathTooLong(const base::FilePath& leveldb_dir) {
-  std::optional<int> limit =
-      base::GetMaximumPathComponentLength(leveldb_dir.DirName());
-  if (!limit.has_value()) {
-    DLOG(WARNING) << "GetMaximumPathComponentLength returned -1";
-// In limited testing, ChromeOS returns 143, other OSes 255.
-#if BUILDFLAG(IS_CHROMEOS)
-    limit = 143;
-#else
-    limit = 255;
-#endif
-  }
-  size_t component_length = leveldb_dir.BaseName().value().length();
-  if (component_length > static_cast<uint32_t>(*limit)) {
-    DLOG(WARNING) << "Path component length (" << component_length
-                  << ") exceeds maximum (" << *limit
-                  << ") allowed by this filesystem.";
-    const int min = 140;
-    const int max = 300;
-    const int num_buckets = 12;
-    base::UmaHistogramCustomCounts(
-        "WebCore.IndexedDB.BackingStore.OverlyLargeOriginLength",
-        component_length, min, max, num_buckets);
-    return true;
-  }
-  return false;
-}
-
 std::string ReadCorruptionInfo(const base::FilePath& path_base,
                                const storage::BucketLocator& bucket_locator) {
   const base::FilePath info_path =
-      path_base.Append(indexed_db::ComputeCorruptionFileName(bucket_locator));
+      path_base.Append(ComputeCorruptionFileName(bucket_locator));
   std::string message;
   if (IsPathTooLong(info_path)) {
     return message;
@@ -243,8 +214,7 @@
       database_id, DatabaseMetaDataKey::MAX_OBJECT_STORE_ID);
   *max_object_store_id = -1;
   bool found = false;
-  Status s = indexed_db::GetInt(db, max_object_store_id_key,
-                                max_object_store_id, &found);
+  Status s = GetInt(db, max_object_store_id_key, max_object_store_id, &found);
   if (!s.ok())
     return s;
   if (!found)
@@ -285,10 +255,9 @@
 
   if (object_store_id <= max_object_store_id) {
     INTERNAL_CONSISTENCY_ERROR(SET_MAX_OBJECT_STORE_ID);
-    return indexed_db::InternalInconsistencyStatus();
+    return InternalInconsistencyStatus();
   }
-  return indexed_db::PutInt(transaction, max_object_store_id_key,
-                            object_store_id);
+  return PutInt(transaction, max_object_store_id_key, object_store_id);
 }
 
 Status GetNewVersionNumber(TransactionalLevelDBTransaction* transaction,
@@ -388,8 +357,8 @@
   *new_id = -1;
   int64_t max_database_id = -1;
   bool found = false;
-  Status s = indexed_db::GetInt(transaction, MaxDatabaseIdKey::Encode(),
-                                &max_database_id, &found);
+  Status s =
+      GetInt(transaction, MaxDatabaseIdKey::Encode(), &max_database_id, &found);
   if (!s.ok()) {
     INTERNAL_READ_ERROR(GET_NEW_DATABASE_ID);
     return s;
@@ -400,7 +369,7 @@
   DCHECK_GE(max_database_id, 0);
 
   int64_t database_id = max_database_id + 1;
-  s = indexed_db::PutInt(transaction, MaxDatabaseIdKey::Encode(), database_id);
+  s = PutInt(transaction, MaxDatabaseIdKey::Encode(), database_id);
   if (!s.ok()) {
     INTERNAL_READ_ERROR(GET_NEW_DATABASE_ID);
     return s;
@@ -540,8 +509,7 @@
   *earliest_sweep = base::Time();
   bool found = false;
   int64_t time_micros = 0;
-  Status s =
-      indexed_db::GetInt(db, earliest_sweep_time_key, &time_micros, &found);
+  Status s = GetInt(db, earliest_sweep_time_key, &time_micros, &found);
   if (!s.ok())
     return s;
   if (!found)
@@ -565,7 +533,7 @@
                                      base::Time earliest_sweep) {
   const std::string earliest_sweep_time_key = EarliestSweepKey::Encode();
   int64_t time_micros = (earliest_sweep - base::Time()).InMicroseconds();
-  return indexed_db::PutInt(txn, earliest_sweep_time_key, time_micros);
+  return PutInt(txn, earliest_sweep_time_key, time_micros);
 }
 
 Status GetEarliestCompactionTime(TransactionalLevelDBDatabase* db,
@@ -575,8 +543,7 @@
   *earliest_compaction = base::Time();
   bool found = false;
   int64_t time_micros = 0;
-  Status s = indexed_db::GetInt(db, earliest_compaction_time_key, &time_micros,
-                                &found);
+  Status s = GetInt(db, earliest_compaction_time_key, &time_micros, &found);
   if (!s.ok())
     return s;
   if (!found)
@@ -601,7 +568,7 @@
   const std::string earliest_compaction_time_key =
       EarliestCompactionKey::Encode();
   int64_t time_micros = (earliest_compaction - base::Time()).InMicroseconds();
-  return indexed_db::PutInt(txn, earliest_compaction_time_key, time_micros);
+  return PutInt(txn, earliest_compaction_time_key, time_micros);
 }
 
 const leveldb::Comparator* GetDefaultLevelDBComparator() {
diff --git a/content/browser/interest_group/interest_group_storage.cc b/content/browser/interest_group/interest_group_storage.cc
index f283aa9..5778e1d 100644
--- a/content/browser/interest_group/interest_group_storage.cc
+++ b/content/browser/interest_group/interest_group_storage.cc
@@ -92,6 +92,7 @@
 // Version 23 - 2024/01 - crrev.com/c/5173733
 // Version 24 - 2024/01 - crrev.com/c/5245196
 // Version 25 - 2024/04 - crrev.com/c/5497898
+// Version 26 - 2024/05 - crrev.com/c/5555460
 //
 // Version 1 adds a table for interest groups.
 // Version 2 adds a column for rate limiting interest group updates.
@@ -126,7 +127,8 @@
 // Version 23 adds trusted bidding signals URL length limit.
 // Version 24 adds cached B&A server keys.
 // Version 25 uses hashed k-anon keys instead of the unhashed versions.
-const int kCurrentVersionNumber = 25;
+// Version 26 runs a VACUUM command.
+const int kCurrentVersionNumber = 26;
 
 // Earliest version of the code which can use a |kCurrentVersionNumber| database
 // without failing.
@@ -861,7 +863,7 @@
 
 // Initializes the tables, returning true on success.
 // The tables cannot exist when calling this function.
-bool CreateV25Schema(sql::Database& db) {
+bool CreateV26Schema(sql::Database& db) {
   DCHECK(!db.DoesTableExist("interest_groups"));
   static const char kInterestGroupTableSql[] =
       // clang-format off
@@ -1031,6 +1033,11 @@
   return true;
 }
 
+bool VacuumDB(sql::Database& db) {
+  static const char kVacuum[] = "VACUUM";
+  return db.Execute(kVacuum);
+}
+
 bool UpgradeV24SchemaToV25(sql::Database& db, sql::MetaTable& meta_table) {
   static const char kCreateKAnonTableSql[] =
       "CREATE TABLE k_anon_new("
@@ -4709,7 +4716,7 @@
   }
 
   if (new_db) {
-    return CreateV25Schema(*db_);
+    return CreateV26Schema(*db_);
   }
 
   const int db_version = meta_table.GetVersionNumber();
@@ -4722,6 +4729,10 @@
     return true;
   }
 
+  // Whether to vacuum the database after the upgrade. The vacuum must happen
+  // after the transaction is committed.
+  bool vacuum_db_post_upgrade = false;
+
   // Older versions - should be migrated.
   // db_version < kCurrentVersionNumber
   // db_version > kDeprecatedVersionNumber
@@ -4825,11 +4836,22 @@
         if (!UpgradeV24SchemaToV25(*db_, meta_table)) {
           return false;
         }
+        ABSL_FALLTHROUGH_INTENDED;
+      case 25:
+        vacuum_db_post_upgrade = true;
         if (!meta_table.SetVersionNumber(kCurrentVersionNumber)) {
           return false;
         }
     }
-    return transaction.Commit();
+    bool committed = transaction.Commit();
+    if (!committed) {
+      return false;
+    }
+    if (vacuum_db_post_upgrade && !VacuumDB(*db_)) {
+      DLOG(ERROR) << "Failed to vacuum: " << db_->GetErrorMessage();
+    }
+
+    return true;
   }
 
   NOTREACHED_IN_MIGRATION();  // Only versions 6 up to the current version
diff --git a/content/browser/renderer_host/navigation_transitions/OWNERS b/content/browser/renderer_host/navigation_transitions/OWNERS
new file mode 100644
index 0000000..d9234a91
--- /dev/null
+++ b/content/browser/renderer_host/navigation_transitions/OWNERS
@@ -0,0 +1 @@
+file://content/browser/navigation_transitions/OWNERS
diff --git a/content/browser/renderer_host/navigation_transitions/navigation_transition_utils.cc b/content/browser/renderer_host/navigation_transitions/navigation_transition_utils.cc
index 928142a0..ce198de 100644
--- a/content/browser/renderer_host/navigation_transitions/navigation_transition_utils.cc
+++ b/content/browser/renderer_host/navigation_transitions/navigation_transition_utils.cc
@@ -79,12 +79,6 @@
                          const SkBitmap& bitmap) {
   auto navigation_entry_id = entry.GetUniqueID();
 
-  if (GetTestScreenshotCallback()) {
-    InvokeTestCallback(
-        controller.GetEntryIndexWithUniqueID(navigation_entry_id), bitmap,
-        true);
-  }
-
   if (&entry == controller.GetLastCommittedEntry()) {
     // TODO(crbug.com/40278616): We shouldn't cache the screenshot into
     // the navigation entry if the entry is re-navigated after we send out the
@@ -111,6 +105,12 @@
     return;
   }
 
+  if (GetTestScreenshotCallback()) {
+    InvokeTestCallback(
+        controller.GetEntryIndexWithUniqueID(navigation_entry_id), bitmap,
+        true);
+  }
+
   SkBitmap immutable_copy(bitmap);
   immutable_copy.setImmutable();
 
diff --git a/content/browser/resources/indexed_db/BUILD.gn b/content/browser/resources/indexed_db/BUILD.gn
index 44e7cf7..c0535f1 100644
--- a/content/browser/resources/indexed_db/BUILD.gn
+++ b/content/browser/resources/indexed_db/BUILD.gn
@@ -8,11 +8,15 @@
   grd_prefix = "indexed_db"
 
   static_files = [
+    "common.css",
     "indexeddb_internals.html",
     "indexeddb_internals.css",
   ]
 
-  web_component_files = [ "database.ts" ]
+  web_component_files = [
+    "database.ts",
+    "transaction_table.ts",
+  ]
   html_to_wrapper_template = "native"
   non_web_component_files = [ "indexeddb_internals.ts" ]
 
diff --git a/content/browser/resources/indexed_db/common.css b/content/browser/resources/indexed_db/common.css
new file mode 100644
index 0000000..5940330
--- /dev/null
+++ b/content/browser/resources/indexed_db/common.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. */
+
+.metadata-list-item {
+    display: block;
+    border-inline-start: solid 1px black;
+    padding-inline-start: 5px;
+    margin-inline-start: 20px;
+    margin-top: 5px;
+}
+
+.controls .control {
+    color: #777;
+    cursor: pointer;
+    margin-block-end: 16px;
+    text-decoration: underline;
+}
diff --git a/content/browser/resources/indexed_db/database.html b/content/browser/resources/indexed_db/database.html
index 20212e2b..859bc04 100644
--- a/content/browser/resources/indexed_db/database.html
+++ b/content/browser/resources/indexed_db/database.html
@@ -1,75 +1,18 @@
+<link rel="stylesheet" type="text/css" href="common.css">
 <style>
-  :host {
-    padding-bottom: 6px;
-    padding-top: 6px;
-    padding-inline-start: 24px;
-  }
-
   .connection-count {
     margin: 0 8px;
   }
 
   .connection-count.active,
-  .connection-count.pending,
-  .transaction.started,
-  .transaction.running,
-  .transaction.committing {
+  .connection-count.pending {
     font-weight: bold;
   }
-
-  .transaction-list {
-    margin-inline-start: 10px;
-    border-collapse: collapse;
-  }
-
-  .transaction-list th,
-  .transaction-list td {
-    padding: 2px 10px;
-    min-width: 50px;
-    max-width: 75px;
-  }
-
-  td.transaction-scope {
-    min-width: 200px;
-    max-width: 500px;
-  }
-
-  .transaction-list th {
-    background-color: rgb(249, 249, 249);
-    border: 1px solid rgb(156, 194, 239);
-    font-weight: normal;
-    text-align: start;
-  }
-
-  .transaction {
-    background-color: rgb(235, 239, 249);
-    border-bottom: 2px solid white;
-  }
-
-  .transaction.created {
-    font-weight: italic;
-  }
-
-  .transaction.started .state {
-    background-color: rgb(249, 249, 235);
-  }
-
-  .transaction.running .state {
-    background-color: rgb(235, 249, 235);
-  }
-
-  .transaction.committing .state {
-    background-color: rgb(235, 235, 249);
-  }
-
-  .transaction.blocked .state {
-    background-color: rgb(249, 235, 235);
-  }
 </style>
 <span>Open database:</span>
 <span class="open-databases"></span>
 <div>
-  <span>Connections:</span>
+  <span>Connection counts:</span>
   <span class="connection-count open">
     <span>open:</span>
     <span class="value"></span>
@@ -82,51 +25,13 @@
     <span>pending opens/deletes:</span>
     <span class="value"></span>
   </span>
-  </div>
-<div id="transactions">
-  <span>Transactions:</span>
-  <table class="transaction-list">
-    <thead>
-      <tr>
-        <th title="Transaction ID (unique within Process)">
-          ID
-        </th>
-        <th title="Type of transaction">
-          Mode
-        </th>
-        <th title="Names of object stores used by the transaction">
-          Scope
-        </th>
-        <th title="Number of requests that have been executed">
-          Completed Requests
-        </th>
-        <th title="Number of requests that have not yet been executed">
-          Pending Requests
-        </th>
-        <th title="Time since transaction creation">
-          Age (ms)
-        </th>
-        <th title="Time since transaction started">
-          Runtime (ms)
-        </th>
-        <th title="Status in the transaction queue">
-          Status
-        </th>
-      </tr>
-    </thead>
-    <tbody></tbody>
-  </table>
-
-  <template id="transaction-row">
-    <tr class="transaction">
-      <td class="tid"></td>
-      <td class="mode"></td>
-      <td class="scope"></td>
-      <td class="requests-complete"></td>
-      <td class="requests-pending"></td>
-      <td class="age"></td>
-      <td class="runtime"></td>
-      <td class="state"></td>
-    </tr>
-  </template>
 </div>
+<div id="transactions"></div>
+
+<!-- This should be made a standalone component when adding more fields. -->
+<template id="client-metadata">
+  <span>
+    Script
+    <span class="client-id"></span>
+  </span>
+</template>
diff --git a/content/browser/resources/indexed_db/database.ts b/content/browser/resources/indexed_db/database.ts
index 37db58a..36cb783 100644
--- a/content/browser/resources/indexed_db/database.ts
+++ b/content/browser/resources/indexed_db/database.ts
@@ -2,50 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import './transaction_table.js';
+
 import {CustomElement} from 'chrome://resources/js/custom_element.js';
 import {mojoString16ToString} from 'chrome://resources/js/mojo_type_util.js';
-import type {String16} from 'chrome://resources/mojo/mojo/public/mojom/base/string16.mojom-webui.js';
 
 import {getTemplate} from './database.html.js';
-import {IdbTransactionMode, IdbTransactionState} from './indexed_db_internals_types.mojom-webui.js';
-import type {IdbDatabaseMetadata} from './indexed_db_internals_types.mojom-webui.js';
-
-// Joins a list of Mojom strings to a comma separated JS string.
-function scope(mojoScope: String16[]): string {
-  return `[${mojoScope.map(s => mojoString16ToString(s)).join(', ')}]`;
-}
-
-// Converts IdbTransactionState enum into a readable string.
-function transactionState(mojoState: IdbTransactionState): string {
-  switch (mojoState) {
-    case IdbTransactionState.kBlocked:
-      return 'Blocked';
-    case IdbTransactionState.kRunning:
-      return 'Running';
-    case IdbTransactionState.kStarted:
-      return 'Started';
-    case IdbTransactionState.kCommitting:
-      return 'Comitting';
-    case IdbTransactionState.kFinished:
-      return 'Finished';
-    default:
-      return 'Unknown';
-  }
-}
-
-// Converts IdbTransactionMode enum into a readable string.
-function transactionMode(mojoMode: IdbTransactionMode): string {
-  switch (mojoMode) {
-    case IdbTransactionMode.kReadOnly:
-      return 'ReadOnly';
-    case IdbTransactionMode.kReadWrite:
-      return 'ReadWrite';
-    case IdbTransactionMode.kVersionChange:
-      return 'VersionChange';
-    default:
-      return 'Unknown';
-  }
-}
+import type {IdbDatabaseMetadata, IdbTransactionMetadata} from './indexed_db_internals_types.mojom-webui.js';
+import type {IndexedDbTransactionTable} from './transaction_table.js';
 
 export class IndexedDbDatabase extends CustomElement {
   static override get template() {
@@ -64,10 +28,6 @@
     const openConnectionElement = this.$a('.connection-count.open');
     const activeConnectionElement = this.$a('.connection-count.active');
     const pendingConnectionElement = this.$a('.connection-count.pending');
-    const transactionsBlockElement = this.$a('#transactions');
-    const transactionTableBodyElement = this.$a('.transaction-list tbody');
-    const transactionRowTemplateElement =
-        this.$a<HTMLTemplateElement>(`#transaction-row`);
 
     openDatabasesElement.textContent = mojoString16ToString(metadata.name);
 
@@ -83,36 +43,48 @@
     pendingConnectionElement.querySelector('.value')!.textContent =
         metadata.pendingOpenDelete.toString();
 
-    // Only show the transactions block if there are any transactions.
-    transactionsBlockElement.hidden =
-        !metadata.transactions || metadata.transactions.length === 0;
+    this.groupAndShowTransactionsByClient(metadata.transactions);
+  }
 
-    transactionTableBodyElement.textContent = '';
-    for (const transaction of metadata.transactions) {
-      const row = (transactionRowTemplateElement.content.cloneNode(true) as
-                   DocumentFragment)
-                      .firstElementChild!;
-      row.classList.add(transactionState(transaction.status).toLowerCase());
-      row.querySelector('td.tid')!.textContent = transaction.tid.toString();
-      row.querySelector('td.mode')!.textContent =
-          transactionMode(transaction.mode);
-      row.querySelector('td.scope')!.textContent = scope(transaction.scope);
-      row.querySelector('td.requests-complete')!.textContent =
-          transaction.tasksCompleted.toString();
-      row.querySelector('td.requests-pending')!.textContent =
-          (transaction.tasksScheduled - transaction.tasksCompleted).toString();
-      row.querySelector('td.age')!.textContent =
-          Math.round(transaction.age).toString();
-      if (transaction.status === IdbTransactionState.kStarted ||
-          transaction.status === IdbTransactionState.kRunning ||
-          transaction.status === IdbTransactionState.kCommitting) {
-        row.querySelector('td.runtime')!.textContent =
-            Math.round(transaction.runtime).toString();
+  private groupAndShowTransactionsByClient(transactions:
+                                               IdbTransactionMetadata[]) {
+    const groupedTransactions = new Map<string, IdbTransactionMetadata[]>();
+    for (const transaction of transactions) {
+      const client = transaction.clientId.toString();
+      if (!groupedTransactions.has(client)) {
+        groupedTransactions.set(client, []);
       }
-      row.querySelector('td.state')!.textContent =
-          transactionState(transaction.status);
-      transactionTableBodyElement.appendChild(row);
+      groupedTransactions.get(client)!.push(transaction);
     }
+
+    const transactionsBlockElement = this.$a('#transactions');
+    transactionsBlockElement.textContent = '';
+    for (const [clientId, clientTransactions] of groupedTransactions) {
+      const container =
+          this.createClientTransactionsContainer(clientId, clientTransactions);
+      container.classList.add('metadata-list-item');
+      transactionsBlockElement.appendChild(container);
+    }
+  }
+
+  // Creates a div containing an instantiation of the client metadata template
+  // and a table of transactions.
+  private createClientTransactionsContainer(
+      clientId: string, transactions: IdbTransactionMetadata[]): HTMLElement {
+    const clientMetadataTemplate =
+        this.$a<HTMLTemplateElement>('#client-metadata');
+    const clientMetadata =
+        (clientMetadataTemplate.content.cloneNode(true) as DocumentFragment)
+            .firstElementChild!;
+    clientMetadata.querySelector('.client-id')!.textContent = clientId;
+    const transactionTable =
+        document.createElement('indexeddb-transaction-table') as
+        IndexedDbTransactionTable;
+    transactionTable.transactions = transactions;
+    const container = document.createElement('div');
+    container.appendChild(clientMetadata);
+    container.appendChild(transactionTable);
+    return container;
   }
 }
 
diff --git a/content/browser/resources/indexed_db/indexeddb_internals.css b/content/browser/resources/indexed_db/indexeddb_internals.css
index 3d11a39f..904eb32 100644
--- a/content/browser/resources/indexed_db/indexeddb_internals.css
+++ b/content/browser/resources/indexed_db/indexeddb_internals.css
@@ -2,6 +2,8 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file. */
 
+@import url('common.css');
+
 .indexeddb-summary {
     background-color: rgb(235, 239, 249);
     border-top: 1px solid rgb(156, 194, 239);
@@ -11,17 +13,6 @@
     font-weight: bold;
 }
 
-.metadata-list-item {
-    border-inline-start: solid 1px black;
-    padding-inline-start: 5px;
-    margin-inline-start: 20px;
-    margin-top: 5px;
-}
-
-indexeddb-bucket {
-    display: block;
-}
-
 .indexeddb-url {
     color: rgb(85, 102, 221);
     display: inline-block;
@@ -39,10 +30,3 @@
   display: block;
     margin-inline-start: 1em;
 }
-
-.controls .control {
-    color: #777;
-    cursor: pointer;
-    margin-block-end: 16px;
-    text-decoration: underline;
-}
diff --git a/content/browser/resources/indexed_db/indexeddb_internals.html b/content/browser/resources/indexed_db/indexeddb_internals.html
index f9dbbab..961510c 100644
--- a/content/browser/resources/indexed_db/indexeddb_internals.html
+++ b/content/browser/resources/indexed_db/indexeddb_internals.html
@@ -74,7 +74,9 @@
                             target="_blank">?</a>
                           <span class="download-status" style="display: none">Loading...</span>
                       </div>
-                      <indexeddb-database jsselect="$this.databases" jsvalues=".data:$this;">
+                      <indexeddb-database class="metadata-list-item"
+                                          jsselect="$this.databases"
+                                          jsvalues=".data:$this;">
                       </indexeddb-database>
                   </div>
                 </div>
diff --git a/content/browser/resources/indexed_db/transaction_table.html b/content/browser/resources/indexed_db/transaction_table.html
new file mode 100644
index 0000000..173ee846
--- /dev/null
+++ b/content/browser/resources/indexed_db/transaction_table.html
@@ -0,0 +1,103 @@
+<style>
+  .transaction.started,
+  .transaction.running,
+  .transaction.committing {
+    font-weight: bold;
+  }
+
+  .transaction-list {
+    margin-inline-start: 10px;
+    border-collapse: collapse;
+  }
+
+  .transaction-list th,
+  .transaction-list td {
+    padding: 2px 10px;
+    min-width: 50px;
+    max-width: 75px;
+  }
+
+  td.transaction-scope {
+    min-width: 200px;
+    max-width: 500px;
+  }
+
+  .transaction-list th {
+    background-color: rgb(249, 249, 249);
+    border: 1px solid rgb(156, 194, 239);
+    font-weight: normal;
+    text-align: start;
+  }
+
+  .transaction {
+    background-color: rgb(235, 239, 249);
+    border-bottom: 2px solid white;
+  }
+
+  .transaction.created {
+    font-weight: italic;
+  }
+
+  .transaction.started .state {
+    background-color: rgb(249, 249, 235);
+  }
+
+  .transaction.running .state {
+    background-color: rgb(235, 249, 235);
+  }
+
+  .transaction.committing .state {
+    background-color: rgb(235, 235, 249);
+  }
+
+  .transaction.blocked .state {
+    background-color: rgb(249, 235, 235);
+  }
+</style>
+<div id="transactions">
+  <span>Transactions:</span>
+  <table class="transaction-list">
+    <thead>
+      <tr>
+        <th title="Transaction ID (unique within Process)">
+          ID
+        </th>
+        <th title="Type of transaction">
+          Mode
+        </th>
+        <th title="Names of object stores used by the transaction">
+          Scope
+        </th>
+        <th title="Number of requests that have been executed">
+          Completed Requests
+        </th>
+        <th title="Number of requests that have not yet been executed">
+          Pending Requests
+        </th>
+        <th title="Time since transaction creation">
+          Age (ms)
+        </th>
+        <th title="Time since transaction started">
+          Runtime (ms)
+        </th>
+        <th title="Status in the transaction queue">
+          Status
+        </th>
+      </tr>
+    </thead>
+    <tbody></tbody>
+  </table>
+
+  <template id="transaction-row">
+    <tr class="transaction">
+      <td class="tid"></td>
+      <td class="mode"></td>
+      <td class="scope"></td>
+      <td class="requests-complete"></td>
+      <td class="requests-pending"></td>
+      <td class="age"></td>
+      <td class="runtime"></td>
+      <td class="state"></td>
+    </tr>
+  </template>
+</div>
diff --git a/content/browser/resources/indexed_db/transaction_table.ts b/content/browser/resources/indexed_db/transaction_table.ts
new file mode 100644
index 0000000..dc82afa
--- /dev/null
+++ b/content/browser/resources/indexed_db/transaction_table.ts
@@ -0,0 +1,102 @@
+// 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.
+
+import {CustomElement} from 'chrome://resources/js/custom_element.js';
+import {mojoString16ToString} from 'chrome://resources/js/mojo_type_util.js';
+import type {String16} from 'chrome://resources/mojo/mojo/public/mojom/base/string16.mojom-webui.js';
+
+import {IdbTransactionMode, IdbTransactionState} from './indexed_db_internals_types.mojom-webui.js';
+import type {IdbTransactionMetadata} from './indexed_db_internals_types.mojom-webui.js';
+import {getTemplate} from './transaction_table.html.js';
+
+// Joins a list of Mojom strings to a comma separated JS string.
+function scope(mojoScope: String16[]): string {
+  return `[${mojoScope.map(s => mojoString16ToString(s)).join(', ')}]`;
+}
+
+// Converts IdbTransactionState enum into a readable string.
+function transactionState(mojoState: IdbTransactionState): string {
+  switch (mojoState) {
+    case IdbTransactionState.kBlocked:
+      return 'Blocked';
+    case IdbTransactionState.kRunning:
+      return 'Running';
+    case IdbTransactionState.kStarted:
+      return 'Started';
+    case IdbTransactionState.kCommitting:
+      return 'Comitting';
+    case IdbTransactionState.kFinished:
+      return 'Finished';
+    default:
+      return 'Unknown';
+  }
+}
+
+// Converts IdbTransactionMode enum into a readable string.
+function transactionMode(mojoMode: IdbTransactionMode): string {
+  switch (mojoMode) {
+    case IdbTransactionMode.kReadOnly:
+      return 'ReadOnly';
+    case IdbTransactionMode.kReadWrite:
+      return 'ReadWrite';
+    case IdbTransactionMode.kVersionChange:
+      return 'VersionChange';
+    default:
+      return 'Unknown';
+  }
+}
+
+export class IndexedDbTransactionTable extends CustomElement {
+  static override get template() {
+    return getTemplate();
+  }
+
+  // Similar to CustomElement.$, but asserts that the element exists.
+  $a<T extends HTMLElement = HTMLElement>(query: string): T {
+    return this.getRequiredElement<T>(query);
+  }
+
+  // Setter for `data` property. Updates the component contents with the
+  // provided metadata.
+  set transactions(transactions: IdbTransactionMetadata[]) {
+    const transactionTableBodyElement = this.$a('.transaction-list tbody');
+    const transactionRowTemplateElement =
+        this.$a<HTMLTemplateElement>(`#transaction-row`);
+
+    transactionTableBodyElement.textContent = '';
+    for (const transaction of transactions) {
+      const row = (transactionRowTemplateElement.content.cloneNode(true) as
+                   DocumentFragment)
+                      .firstElementChild!;
+      row.classList.add(transactionState(transaction.status).toLowerCase());
+      row.querySelector('td.tid')!.textContent = transaction.tid.toString();
+      row.querySelector('td.mode')!.textContent =
+          transactionMode(transaction.mode);
+      row.querySelector('td.scope')!.textContent = scope(transaction.scope);
+      row.querySelector('td.requests-complete')!.textContent =
+          transaction.tasksCompleted.toString();
+      row.querySelector('td.requests-pending')!.textContent =
+          (transaction.tasksScheduled - transaction.tasksCompleted).toString();
+      row.querySelector('td.age')!.textContent =
+          Math.round(transaction.age).toString();
+      if (transaction.status === IdbTransactionState.kStarted ||
+          transaction.status === IdbTransactionState.kRunning ||
+          transaction.status === IdbTransactionState.kCommitting) {
+        row.querySelector('td.runtime')!.textContent =
+            Math.round(transaction.runtime).toString();
+      }
+      row.querySelector('td.state')!.textContent =
+          transactionState(transaction.status);
+      transactionTableBodyElement.appendChild(row);
+    }
+  }
+}
+
+declare global {
+  interface HTMLElementTagNameMap {
+    'indexeddb-transaction-table': IndexedDbTransactionTable;
+  }
+}
+
+customElements.define('indexeddb-transaction-table', IndexedDbTransactionTable);
diff --git a/content/browser/shared_storage/shared_storage_browsertest.cc b/content/browser/shared_storage/shared_storage_browsertest.cc
index 16852fb..9827e61 100644
--- a/content/browser/shared_storage/shared_storage_browsertest.cc
+++ b/content/browser/shared_storage/shared_storage_browsertest.cc
@@ -1319,7 +1319,7 @@
   WebContentsConsoleObserver console_observer(shell()->web_contents());
 
   std::string expected_error = base::StrCat(
-      {"a JavaScript error: \"Error: Failed to load ",
+      {"a JavaScript error: \"OperationError: Failed to load ",
        https_server()
            ->GetURL("a.test", "/shared_storage/nonexistent_module.js")
            .spec(),
@@ -1349,7 +1349,7 @@
   WebContentsConsoleObserver console_observer(shell()->web_contents());
 
   std::string expected_error = base::StrCat(
-      {"a JavaScript error: \"Error: Unexpected redirect on ",
+      {"a JavaScript error: \"OperationError: Unexpected redirect on ",
        https_server()
            ->GetURL("a.test",
                     "/server-redirect?shared_storage/simple_module.js")
@@ -6538,7 +6538,7 @@
 
   // `selectURL()` fails when map is full.
   std::string expected_error = base::StrCat(
-      {"a JavaScript error: \"Error: ",
+      {"a JavaScript error: \"OperationError: ",
        "sharedStorage.selectURL() failed because number of urn::uuid to url ",
        "mappings has reached the limit.\"\n"});
   EXPECT_EQ(expected_error, extra_result.error);
diff --git a/content/browser/tracing/background_startup_tracing_observer.cc b/content/browser/tracing/background_startup_tracing_observer.cc
index ed0a97a6..cbaa23a 100644
--- a/content/browser/tracing/background_startup_tracing_observer.cc
+++ b/content/browser/tracing/background_startup_tracing_observer.cc
@@ -6,6 +6,7 @@
 
 #include "base/functional/bind.h"
 #include "base/no_destructor.h"
+#include "base/trace_event/named_trigger.h"
 #include "components/tracing/common/trace_startup_config.h"
 #include "content/browser/tracing/background_tracing_rule.h"
 #include "content/public/browser/browser_task_traits.h"
@@ -94,11 +95,12 @@
   if (!enabled_in_current_session_ || startup_rule)
     return config;
 
-  auto rules_dict = base::Value::Dict()
-                        .Set("rule", "MONITOR_AND_DUMP_WHEN_TRIGGER_NAMED")
-                        .Set("trigger_name", kStartupTracingTriggerName)
-                        .Set("trigger_delay", 30)
-                        .Set("rule_id", kStartupTracingRuleId);
+  auto rules_dict =
+      base::Value::Dict()
+          .Set("rule", "MONITOR_AND_DUMP_WHEN_TRIGGER_NAMED")
+          .Set("trigger_name", base::trace_event::kStartupTracingTriggerName)
+          .Set("trigger_delay", 30)
+          .Set("rule_id", kStartupTracingRuleId);
 
   if (config) {
     config->AddReactiveRule(rules_dict);
diff --git a/content/browser/tracing/background_startup_tracing_observer.h b/content/browser/tracing/background_startup_tracing_observer.h
index 39528daf..a93e29b8 100644
--- a/content/browser/tracing/background_startup_tracing_observer.h
+++ b/content/browser/tracing/background_startup_tracing_observer.h
@@ -13,8 +13,6 @@
 
 namespace content {
 
-constexpr const char* kStartupTracingTriggerName = "startup";
-
 // Observes for startup tracing config and sets up preferences to trace on next
 // startup.
 // TODO(crbug.com/40257548): Rename this class since this is no longer an
diff --git a/content/browser/tracing/background_tracing_manager_browsertest.cc b/content/browser/tracing/background_tracing_manager_browsertest.cc
index 72d1f16..3c849336 100644
--- a/content/browser/tracing/background_tracing_manager_browsertest.cc
+++ b/content/browser/tracing/background_tracing_manager_browsertest.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include <stddef.h>
+
 #include <memory>
 #include <string>
 #include <utility>
@@ -31,6 +32,7 @@
 #include "base/test/test_proto_loader.h"
 #include "base/test/trace_event_analyzer.h"
 #include "base/threading/thread_restrictions.h"
+#include "base/trace_event/named_trigger.h"
 #include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
 #include "components/tracing/common/trace_startup_config.h"
@@ -1665,6 +1667,8 @@
 
   EXPECT_TRUE(BackgroundTracingManager::GetInstance().SetActiveScenario(
       std::move(config), BackgroundTracingManager::ANONYMIZE_DATA));
+  base::trace_event::EmitNamedTrigger(
+      base::trace_event::kStartupTracingTriggerName);
 
   tracelog_helper.WaitForStartTracing();
   background_tracing_helper.WaitForTraceStarted();
diff --git a/content/browser/tracing/background_tracing_manager_impl.cc b/content/browser/tracing/background_tracing_manager_impl.cc
index f0e93f1..dd4022e 100644
--- a/content/browser/tracing/background_tracing_manager_impl.cc
+++ b/content/browser/tracing/background_tracing_manager_impl.cc
@@ -498,7 +498,6 @@
     enabled_scenarios_.back()->Enable();
   }
   RecordMetric(Metrics::SCENARIO_ACTIVATED_SUCCESSFULLY);
-  DoEmitNamedTrigger(kStartupTracingTriggerName, std::nullopt);
   return true;
 }
 
@@ -559,7 +558,6 @@
       it->second->Enable();
     }
   }
-  DoEmitNamedTrigger(kStartupTracingTriggerName, std::nullopt);
   return true;
 }
 
@@ -629,7 +627,6 @@
 
   if (startup_tracing_enabled) {
     RecordMetric(Metrics::STARTUP_SCENARIO_TRIGGERED);
-    DoEmitNamedTrigger(kStartupTracingTriggerName, std::nullopt);
   }
 
   legacy_active_scenario_->StartTracingIfConfigNeedsIt();
diff --git a/content/browser/webui/web_ui_browsertest.cc b/content/browser/webui/web_ui_browsertest.cc
index 5d749ea..68db55eea 100644
--- a/content/browser/webui/web_ui_browsertest.cc
+++ b/content/browser/webui/web_ui_browsertest.cc
@@ -1117,7 +1117,7 @@
       kLoadSharedWorkerScript);
 
   std::string expected_failure =
-      "a JavaScript error: \"Error: Failed to construct 'SharedWorker'";
+      "a JavaScript error: \"SecurityError: Failed to construct 'SharedWorker'";
   EXPECT_THAT(result.error, ::testing::StartsWith(expected_failure));
 }
 
@@ -1191,7 +1191,8 @@
       kLoadSharedWorkerScript);
 
   std::string expected_failure =
-      "a JavaScript error: \"Error: Failed to construct 'SharedWorker': "
+      "a JavaScript error: \"SecurityError: Failed to construct "
+      "'SharedWorker': "
       "Script at 'chrome-untrusted://untrusted/web_ui_shared_worker.js' cannot "
       "be accessed from origin 'chrome://trusted'";
   EXPECT_THAT(result.error, ::testing::StartsWith(expected_failure));
@@ -1209,7 +1210,8 @@
       kLoadSharedWorkerScript);
 
   std::string expected_failure =
-      "a JavaScript error: \"Error: Failed to construct 'SharedWorker': "
+      "a JavaScript error: \"SecurityError: Failed to construct "
+      "'SharedWorker': "
       "Script at 'chrome-untrusted://untrusted/web_ui_shared_worker.js' cannot "
       "be accessed from origin 'http://localhost";
   EXPECT_THAT(result.error, ::testing::StartsWith(expected_failure));
@@ -1226,7 +1228,8 @@
       GetWebUIURL("trusted/web_ui_shared_worker.js"), kLoadSharedWorkerScript);
 
   std::string expected_failure =
-      "a JavaScript error: \"Error: Failed to construct 'SharedWorker': Script "
+      "a JavaScript error: \"SecurityError: Failed to construct "
+      "'SharedWorker': Script "
       "at 'chrome://trusted/web_ui_shared_worker.js' cannot be accessed from "
       "origin 'chrome-untrusted://untrusted'.";
   EXPECT_THAT(result.error, ::testing::StartsWith(expected_failure));
@@ -1256,7 +1259,7 @@
       kLoadDedicatedWorkerScript);
 
   std::string expected_failure =
-      "a JavaScript error: \"Error: Failed to construct 'Worker'";
+      "a JavaScript error: \"SecurityError: Failed to construct 'Worker'";
   EXPECT_THAT(result.error, ::testing::StartsWith(expected_failure));
 }
 
@@ -1328,7 +1331,7 @@
       kLoadDedicatedWorkerScript);
 
   std::string expected_failure =
-      "a JavaScript error: \"Error: Failed to construct 'Worker': "
+      "a JavaScript error: \"SecurityError: Failed to construct 'Worker': "
       "Script at 'chrome-untrusted://untrusted/web_ui_dedicated_worker.js' "
       "cannot be accessed from origin 'chrome://trusted'";
   EXPECT_THAT(result.error, ::testing::StartsWith(expected_failure));
@@ -1346,7 +1349,7 @@
       kLoadDedicatedWorkerScript);
 
   std::string expected_failure =
-      "a JavaScript error: \"Error: Failed to construct 'Worker': "
+      "a JavaScript error: \"SecurityError: Failed to construct 'Worker': "
       "Script at 'chrome-untrusted://untrusted/web_ui_dedicated_worker.js' "
       "cannot be accessed from origin 'http://localhost";
   EXPECT_THAT(result.error, ::testing::StartsWith(expected_failure));
@@ -1365,7 +1368,8 @@
       kLoadDedicatedWorkerScript);
 
   std::string expected_failure =
-      "a JavaScript error: \"Error: Failed to construct 'Worker': Script "
+      "a JavaScript error: \"SecurityError: Failed to construct 'Worker': "
+      "Script "
       "at 'chrome://trusted/web_ui_dedicated_worker.js' cannot be accessed "
       "from origin 'chrome-untrusted://untrusted'.";
   EXPECT_THAT(result.error, ::testing::StartsWith(expected_failure));
diff --git a/content/browser/worker_host/worker_script_fetcher.cc b/content/browser/worker_host/worker_script_fetcher.cc
index c2da484..46e8f51 100644
--- a/content/browser/worker_host/worker_script_fetcher.cc
+++ b/content/browser/worker_host/worker_script_fetcher.cc
@@ -730,13 +730,12 @@
     const network::URLLoaderCompletionStatus& status) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
-  if (status.error_code == net::OK) {
-    // It's possible to reach here when the `response_head_` doesn't have a
-    // `parsed_headers` and ask NetworkService to parse headers in
-    // OnReceiveResponse(). DidParseHeaders() will be called eventually
-    // and `this` will be deleted in it.
-    return;
-  }
+  // Successful completion must be passed to `url_loader_client_endpoints` and
+  // shouldn't reach here, because in successful fetch `url_loader_->Unbind()`
+  // should be called in `WorkerScriptFetcher::OnReceiveResponse` and thus
+  // subsequent URLLoaderClient mojo calls shouldn't reach to
+  // `WorkerScriptFetcher`.
+  CHECK_NE(status.error_code, net::OK);
 
   std::move(callback_).Run(/*main_script_load_params=*/nullptr,
                            /*subresource_loader_params=*/{}, &status);
diff --git a/content/child/child_process.cc b/content/child/child_process.cc
index b47e533..f44667e6 100644
--- a/content/child/child_process.cc
+++ b/content/child/child_process.cc
@@ -35,6 +35,10 @@
 #include "content/common/android/cpu_time_metrics.h"
 #endif
 
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
+#include "content/child/sandboxed_process_thread_type_handler.h"
+#endif
+
 namespace content {
 
 namespace {
@@ -141,8 +145,14 @@
   thread_options.thread_type = base::ThreadType::kCompositing;
 #endif
   CHECK(io_thread_->StartWithOptions(std::move(thread_options)));
+  io_thread_runner_ = io_thread_->task_runner();
 }
 
+ChildProcess::ChildProcess(
+    scoped_refptr<base::SingleThreadTaskRunner> io_thread_runner)
+    : resetter_(&child_process, this, nullptr),
+      io_thread_runner_(std::move(io_thread_runner)) {}
+
 ChildProcess::~ChildProcess() {
   DCHECK_EQ(child_process, this);
 
@@ -163,8 +173,10 @@
     }
   }
 
-  io_thread_->Stop();
-  io_thread_.reset();
+  if (io_thread_) {
+    io_thread_->Stop();
+    io_thread_.reset();
+  }
 
   if (initialized_thread_pool_) {
     DCHECK(base::ThreadPoolInstance::Get());
@@ -187,6 +199,23 @@
   main_thread_.reset(thread);
 }
 
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
+void ChildProcess::SetIOThreadType(base::ThreadType thread_type) {
+  if (!io_thread_) {
+    return;
+  }
+
+  // The SandboxedProcessThreadTypeHandler isn't created in
+  // in --single-process mode or if certain base::Features are disabled. See
+  // instances of SandboxedProcessThreadTypeHandler::Create() for more details.
+  if (SandboxedProcessThreadTypeHandler* sandboxed_process_thread_type_handler =
+          SandboxedProcessThreadTypeHandler::Get()) {
+    sandboxed_process_thread_type_handler->HandleThreadTypeChange(
+        io_thread_->GetThreadId(), base::ThreadType::kCompositing);
+  }
+}
+#endif
+
 void ChildProcess::AddRefProcess() {
   DCHECK(!main_thread_.get() ||  // null in unittests.
          main_thread_->main_thread_runner()->BelongsToCurrentThread());
diff --git a/content/child/child_process.h b/content/child/child_process.h
index f1fedbd..bbf1f3e 100644
--- a/content/child/child_process.h
+++ b/content/child/child_process.h
@@ -52,6 +52,13 @@
       std::unique_ptr<base::ThreadPoolInstance::InitParams>
           thread_pool_init_params = nullptr);
 
+  // This constructor can be used to create a ChildProcess within the browser
+  // process which shares the IO thread. `io_thread_runner` passes an existing
+  // task runner to use for the child IO thread instead of creating a new
+  // thread.
+  explicit ChildProcess(
+      scoped_refptr<base::SingleThreadTaskRunner> io_thread_runner);
+
   ChildProcess(const ChildProcess&) = delete;
   ChildProcess& operator=(const ChildProcess&) = delete;
 
@@ -69,9 +76,13 @@
   void StopIOThreadForTesting() { io_thread_->Stop(); }
 
   base::SingleThreadTaskRunner* io_task_runner() {
-    return io_thread_->task_runner().get();
+    return io_thread_runner_.get();
   }
-  base::PlatformThreadId io_thread_id() { return io_thread_->GetThreadId(); }
+
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
+  // Changes the thread type of the child process IO thread.
+  void SetIOThreadType(base::ThreadType thread_type);
+#endif
 
   // A global event object that is signalled when the main thread's message
   // loop exits.  This gives background threads a way to observe the main
@@ -107,9 +118,13 @@
       base::WaitableEvent::ResetPolicy::MANUAL,
       base::WaitableEvent::InitialState::NOT_SIGNALED};
 
-  // The thread that handles IO events.
+  // The thread that handles IO events. May be null if `io_thread_runner` was
+  // passed to the constructor.
   std::unique_ptr<base::Thread> io_thread_;
 
+  // The task runner to use for IO thread tasks.
+  scoped_refptr<base::SingleThreadTaskRunner> io_thread_runner_;
+
   // NOTE: make sure that main_thread_ is listed after shutdown_event_, since
   // it depends on it (indirectly through IPC::SyncChannel).  Same for
   // io_thread_.
diff --git a/content/common/in_process_child_thread_params.cc b/content/common/in_process_child_thread_params.cc
index 4653fc8..0e33688 100644
--- a/content/common/in_process_child_thread_params.cc
+++ b/content/common/in_process_child_thread_params.cc
@@ -9,8 +9,11 @@
 
 InProcessChildThreadParams::InProcessChildThreadParams(
     scoped_refptr<base::SingleThreadTaskRunner> io_runner,
-    mojo::OutgoingInvitation* mojo_invitation)
-    : io_runner_(std::move(io_runner)), mojo_invitation_(mojo_invitation) {}
+    mojo::OutgoingInvitation* mojo_invitation,
+    scoped_refptr<base::SingleThreadTaskRunner> child_io_runner)
+    : io_runner_(std::move(io_runner)),
+      mojo_invitation_(mojo_invitation),
+      child_io_runner_(std::move(child_io_runner)) {}
 
 InProcessChildThreadParams::InProcessChildThreadParams(
     const InProcessChildThreadParams& other) = default;
diff --git a/content/common/in_process_child_thread_params.h b/content/common/in_process_child_thread_params.h
index 85d4d46..09c52a4 100644
--- a/content/common/in_process_child_thread_params.h
+++ b/content/common/in_process_child_thread_params.h
@@ -15,24 +15,31 @@
 
 // Tells ChildThreadImpl to run in in-process mode. There are a couple of
 // parameters to run in the mode: An emulated io task runner used by
-// ChnanelMojo, an IPC channel name to open.
+// ChnanelMojo, an IPC channel name to open. `child_io_runner` can be passed
+// with the current IO thread to allow it to be shared by the child process.
 class CONTENT_EXPORT InProcessChildThreadParams {
  public:
   InProcessChildThreadParams(
       scoped_refptr<base::SingleThreadTaskRunner> io_runner,
-      mojo::OutgoingInvitation* mojo_invitation);
+      mojo::OutgoingInvitation* mojo_invitation,
+      scoped_refptr<base::SingleThreadTaskRunner> child_io_runner = nullptr);
   InProcessChildThreadParams(const InProcessChildThreadParams& other);
   ~InProcessChildThreadParams();
 
-  scoped_refptr<base::SingleThreadTaskRunner> io_runner() const {
+  const scoped_refptr<base::SingleThreadTaskRunner>& io_runner() const {
     return io_runner_;
   }
 
+  const scoped_refptr<base::SingleThreadTaskRunner>& child_io_runner() const {
+    return child_io_runner_;
+  }
+
   mojo::OutgoingInvitation* mojo_invitation() const { return mojo_invitation_; }
 
  private:
   scoped_refptr<base::SingleThreadTaskRunner> io_runner_;
   const raw_ptr<mojo::OutgoingInvitation> mojo_invitation_;
+  scoped_refptr<base::SingleThreadTaskRunner> child_io_runner_;
 };
 
 }  // namespace content
diff --git a/content/gpu/in_process_gpu_thread.cc b/content/gpu/in_process_gpu_thread.cc
index 2d1bafa..58b21f4 100644
--- a/content/gpu/in_process_gpu_thread.cc
+++ b/content/gpu/in_process_gpu_thread.cc
@@ -5,6 +5,7 @@
 #include "content/gpu/in_process_gpu_thread.h"
 
 #include "base/command_line.h"
+#include "base/feature_list.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
 #include "content/child/child_process.h"
@@ -25,6 +26,13 @@
 #endif
 
 namespace content {
+namespace {
+
+BASE_FEATURE(kInProcessGpuUseIOThread,
+             "InProcessGpuUseIOThread",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
+}  // namespace
 
 InProcessGpuThread::InProcessGpuThread(
     const InProcessChildThreadParams& params,
@@ -57,7 +65,11 @@
   io_thread_type = base::ThreadType::kDisplayCritical;
 #endif
 
-  gpu_process_ = std::make_unique<ChildProcess>(io_thread_type);
+  if (base::FeatureList::IsEnabled(kInProcessGpuUseIOThread)) {
+    gpu_process_ = std::make_unique<ChildProcess>(params_.child_io_runner());
+  } else {
+    gpu_process_ = std::make_unique<ChildProcess>(io_thread_type);
+  }
 
   auto gpu_init = std::make_unique<gpu::GpuInit>();
   gpu_init->InitializeInProcess(base::CommandLine::ForCurrentProcess(),
diff --git a/content/public/android/java/src/org/chromium/content_public/browser/ContentFeatureList.java b/content/public/android/java/src/org/chromium/content_public/browser/ContentFeatureList.java
index a1cdbe8..e0b1eec 100644
--- a/content/public/android/java/src/org/chromium/content_public/browser/ContentFeatureList.java
+++ b/content/public/android/java/src/org/chromium/content_public/browser/ContentFeatureList.java
@@ -39,9 +39,6 @@
     public static final String PROCESS_SHARING_WITH_STRICT_SITE_INSTANCES =
             "ProcessSharingWithStrictSiteInstances";
 
-    public static final String REQUEST_DESKTOP_SITE_WINDOW_SETTING =
-            "RequestDesktopSiteWindowSetting";
-
     public static final String SMART_ZOOM = "SmartZoom";
 
     public static final String WEB_BLUETOOTH_NEW_PERMISSIONS_BACKEND =
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc
index d472ab0..0eee32e7 100644
--- a/content/public/common/content_features.cc
+++ b/content/public/common/content_features.cc
@@ -1183,11 +1183,6 @@
              "MouseAndTrackpadDropdownMenu",
              base::FEATURE_ENABLED_BY_DEFAULT);
 
-// Request Desktop Site based on window width for Android.
-BASE_FEATURE(kRequestDesktopSiteWindowSetting,
-             "RequestDesktopSiteWindowSetting",
-             base::FEATURE_ENABLED_BY_DEFAULT);
-
 // Apply text selection menu order correction logic for Android.
 // TODO(crbug.com/40947146) This is a kill switch landed in M122.
 // Please remove after M124.
diff --git a/content/public/common/content_features.h b/content/public/common/content_features.h
index 589158d7..d6b8da60a 100644
--- a/content/public/common/content_features.h
+++ b/content/public/common/content_features.h
@@ -250,7 +250,6 @@
     kGinJavaBridgeMojoSkipClearObjectsOnMainDocumentReady);
 CONTENT_EXPORT BASE_DECLARE_FEATURE(kReduceGpuPriorityOnBackground);
 CONTENT_EXPORT BASE_DECLARE_FEATURE(kMouseAndTrackpadDropdownMenu);
-CONTENT_EXPORT BASE_DECLARE_FEATURE(kRequestDesktopSiteWindowSetting);
 CONTENT_EXPORT BASE_DECLARE_FEATURE(kSelectionMenuItemModification);
 CONTENT_EXPORT BASE_DECLARE_FEATURE(kSmartZoom);
 CONTENT_EXPORT BASE_DECLARE_FEATURE(kUserMediaScreenCapturing);
diff --git a/content/public/test/android/javatests/src/org/chromium/content_public/browser/test/ContentJUnit4ClassRunner.java b/content/public/test/android/javatests/src/org/chromium/content_public/browser/test/ContentJUnit4ClassRunner.java
index 09a80f44..2cb7153 100644
--- a/content/public/test/android/javatests/src/org/chromium/content_public/browser/test/ContentJUnit4ClassRunner.java
+++ b/content/public/test/android/javatests/src/org/chromium/content_public/browser/test/ContentJUnit4ClassRunner.java
@@ -12,10 +12,10 @@
 import org.chromium.base.test.util.SkipCheck;
 import org.chromium.net.test.EmbeddedTestServer;
 import org.chromium.ui.display.DisplayUtil;
-import org.chromium.ui.test.util.DeviceRestrictionSkipCheck;
-import org.chromium.ui.test.util.GmsCoreVersionRestrictionSkipCheck;
+import org.chromium.ui.test.util.DeviceRestriction;
+import org.chromium.ui.test.util.GmsCoreVersionRestriction;
 import org.chromium.ui.test.util.UiDisableIfSkipCheck;
-import org.chromium.ui.test.util.UiRestrictionSkipCheck;
+import org.chromium.ui.test.util.UiRestriction;
 
 import java.util.List;
 
@@ -28,6 +28,9 @@
      */
     public ContentJUnit4ClassRunner(final Class<?> klass) throws InitializationError {
         super(klass);
+        UiRestriction.registerChecks(mRestrictionSkipCheck);
+        DeviceRestriction.registerChecks(mRestrictionSkipCheck);
+        GmsCoreVersionRestriction.registerChecks(mRestrictionSkipCheck);
 
         // Display ui scale-up on auto for tests by default, individual tests can restore this
         // scaling.
@@ -39,9 +42,6 @@
     protected List<SkipCheck> getSkipChecks() {
         return addToList(
                 super.getSkipChecks(),
-                new UiRestrictionSkipCheck(InstrumentationRegistry.getTargetContext()),
-                new DeviceRestrictionSkipCheck(InstrumentationRegistry.getTargetContext()),
-                new UiDisableIfSkipCheck(InstrumentationRegistry.getTargetContext()),
-                new GmsCoreVersionRestrictionSkipCheck(getApplication().getApplicationContext()));
+                new UiDisableIfSkipCheck(InstrumentationRegistry.getTargetContext()));
     }
 }
diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc
index 9dd563c..d4dd7ec 100644
--- a/content/renderer/render_thread_impl.cc
+++ b/content/renderer/render_thread_impl.cc
@@ -615,14 +615,7 @@
       discardable_memory_allocator_.get());
 
 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
-  // The SandboxedProcessThreadTypeHandler isn't created in
-  // render_thread_impl_browsertest.cc, nor in --single-process mode.
-  if (SandboxedProcessThreadTypeHandler* sandboxed_process_thread_type_handler =
-          SandboxedProcessThreadTypeHandler::Get()) {
-    sandboxed_process_thread_type_handler->HandleThreadTypeChange(
-        ChildProcess::current()->io_thread_id(),
-        base::ThreadType::kCompositing);
-  }
+  ChildProcess::current()->SetIOThreadType(base::ThreadType::kCompositing);
 #endif
 
   process_foregrounded_count_ = 0;
diff --git a/content/test/content_browser_test_utils_internal.cc b/content/test/content_browser_test_utils_internal.cc
index 0ea241d..a056244 100644
--- a/content/test/content_browser_test_utils_internal.cc
+++ b/content/test/content_browser_test_utils_internal.cc
@@ -290,16 +290,11 @@
       to_explore.push(node->child_at(i));
     }
 
-    // Sort the proxies by SiteInstance ID to avoid unordered_map ordering.
+    // Sort the proxies by SiteInstanceGroup ID to avoid unordered_map ordering.
     std::vector<SiteInstance*> site_instances;
     for (const auto& proxy_pair :
          node->render_manager()->GetAllProxyHostsForTesting()) {
       SiteInstanceGroup* group = proxy_pair.second->site_instance_group();
-
-      // Currently, each SiteInstanceGroup only has one SiteInstance in it.
-      // TODO(crbug.com/1195535, yangsharon): Remove when multiple SiteInstances
-      // per group is supported.
-      CHECK_EQ(group->site_instances_for_testing().size(), 1u);
       for (raw_ptr<SiteInstanceImpl> instance :
            group->site_instances_for_testing()) {
         site_instances.push_back(instance);
@@ -394,16 +389,8 @@
       // Sort these alphabetically, to avoid hash_map ordering dependency.
       std::vector<std::string> sorted_proxy_hosts;
       for (const auto& proxy_pair : proxy_host_map) {
-        // Get the first SiteInstance from each group, since there's only one
-        // SiteInstance per group.
-        // TODO(crbug.com/1447896, yangsharon): Add support for multiple
-        // SiteInstances per group.
-        auto site_instances_for_testing =
-            proxy_pair.second->site_instance_group()
-                ->site_instances_for_testing();
-        CHECK_EQ(site_instances_for_testing.size(), 1u);
-        SiteInstance* site_instance = *(site_instances_for_testing.begin());
-        sorted_proxy_hosts.push_back(GetName(site_instance));
+        sorted_proxy_hosts.push_back(
+            GetGroupName(proxy_pair.second->site_instance_group()));
       }
       std::sort(sorted_proxy_hosts.begin(), sorted_proxy_hosts.end());
       for (std::string& proxy_name : sorted_proxy_hosts) {
@@ -462,6 +449,33 @@
     return base::StringPrintf("Z%d", static_cast<int>(index - 25));
 }
 
+std::string FrameTreeVisualizer::GetGroupName(SiteInstanceGroup* group) {
+  // If there's only one SiteInstance in `group`, get the name of the
+  // SiteInstance directly. This preserves test expectations for DepictFrameTree
+  // uses that predate SiteInstanceGroup.
+  if (group->site_instances_for_testing().size() == 1) {
+    return GetName(*group->site_instances_for_testing().begin());
+  }
+
+  // Alphabetically sort the SiteInstances within the group.
+  std::vector<std::string> sorted_instance_names;
+  for (auto& site_instance : group->site_instances_for_testing()) {
+    sorted_instance_names.push_back(GetName(site_instance));
+  }
+  std::sort(sorted_instance_names.begin(), sorted_instance_names.end());
+
+  // Name the group using set notation.
+  CHECK(sorted_instance_names.size() >= 1u);
+  std::string result = "{";
+  for (auto& site_instance_name : sorted_instance_names) {
+    base::StringAppendF(&result, "%s,", site_instance_name.c_str());
+  }
+  result.resize(result.length() - 1);
+  result.append("}");
+
+  return result;
+}
+
 GURL FrameTreeVisualizer::GetUrlWithoutPort(const GURL& url) {
   GURL::Replacements replacements;
   replacements.ClearPort();
diff --git a/content/test/content_browser_test_utils_internal.h b/content/test/content_browser_test_utils_internal.h
index 309a566..41e24e4 100644
--- a/content/test/content_browser_test_utils_internal.h
+++ b/content/test/content_browser_test_utils_internal.h
@@ -47,6 +47,7 @@
 class RenderWidgetHostImpl;
 class Shell;
 class SiteInstance;
+class SiteInstanceGroup;
 class ToRenderFrameHost;
 
 // Navigates the frame represented by |node| to |url|, blocking until the
@@ -114,24 +115,37 @@
 // is appropriate for use in assertions.
 //
 // The diagrams show frame tree structure, the SiteInstance of current frames,
-// presence of pending frames, and the SiteInstances of any and all proxies.
-// They look like this:
+// presence of pending frames, and the SiteInstanceGroups of any and all
+// proxies. They look like this:
 //
-//        Site A (D pending) -- proxies for B C
-//          |--Site B --------- proxies for A C
+//        Site A (D pending) -- proxies for B {C,E}
+//          |--Site B --------- proxies for A {C,E}
 //          +--Site C --------- proxies for B A
-//               |--Site A ---- proxies for B
-//               +--Site A ---- proxies for B
-//                    +--Site A -- proxies for B
+//               |--Site A ---- proxies for B {C,E}
+//               +--Site A ---- proxies for B {C,E}
+//                    +--Site E -- proxies for A B
 //       Where A = http://127.0.0.1/
 //             B = http://foo.com/ (no process)
 //             C = http://bar.com/
 //             D = http://next.com/
+//             E = data:nonce_E
 //
 // SiteInstances are assigned single-letter names (A, B, C) which are remembered
 // across invocations of the pretty-printer. Port numbers are excluded from the
 // descriptions by default for DepictFrameTree. Isolated sandboxed SiteInstances
 // are denoted with "(sandboxed)".
+//
+// SiteInstanceGroups with more than once SiteInstance are denoted as a set of
+// the SiteInstances in the group. See comment for `GetGroupName`. In this case,
+// E is in C's SiteInstanceGroup, denoted {C,E}. Note that SiteInstanceGroups
+// may show SiteInstances that are no longer in the FrameTree. For example, if a
+// subframe B does a same-SiteInstanceGroup navigation to data:nonce_C, B's
+// SiteInstance is kept alive by a FrameNavigationEntry, and it retains its
+// group and process because the active frame count is tracked on the
+// SiteInstanceGroup (shared with data:nonce_C) and not the B SiteInstance
+// itself. (This is not necessary but has no impact outside of DepictFrameTree
+// output). That means it still exists from the perspective
+// of DepictFrameTree.
 class FrameTreeVisualizer {
  public:
   FrameTreeVisualizer();
@@ -148,6 +162,18 @@
   // Assign or retrive the abbreviated short name (A, B, C) for a site instance.
   std::string GetName(SiteInstance* site_instance);
 
+  // Assign the name for a SiteInstanceGroup. A group's name is denoted as a set
+  // containing all the SiteInstances in the group, using their abbreviated
+  // names. For example, if a group contains foo.com and bar.com, which are
+  // assigned A and B respectively, the group name will be {A,B}. If there is
+  // only one SiteInstance in the group, it is directly depicted as the short
+  // name without set notation to minimize changes to existing tests. E.g.
+  // SiteInstanceGroup that contains only SiteInstance A is depicted as A rather
+  // than {A}.
+  // TODO(crbug.com/40176090): Always use set notation, to indicate that proxies
+  // are associated with SiteInstanceGroups rather than SiteInstances.
+  std::string GetGroupName(SiteInstanceGroup* group);
+
   // Returns an identical URL except the port, if any, has been removed.
   GURL GetUrlWithoutPort(const GURL& url);
 
diff --git a/device/bluetooth/floss/bluetooth_local_gatt_descriptor_floss.h b/device/bluetooth/floss/bluetooth_local_gatt_descriptor_floss.h
index 410fe58..f58b117 100644
--- a/device/bluetooth/floss/bluetooth_local_gatt_descriptor_floss.h
+++ b/device/bluetooth/floss/bluetooth_local_gatt_descriptor_floss.h
@@ -101,7 +101,8 @@
                                  std::vector<uint8_t>& value);
 
   // Notification type of the CCCD.
-  device::BluetoothGattCharacteristic::NotificationType cccd_type_;
+  device::BluetoothGattCharacteristic::NotificationType cccd_type_ =
+      device::BluetoothGattCharacteristic::NotificationType::kNone;
 
   // Cached instance of the latest pending read/write request, if one exists.
   std::optional<GattRequest> pending_request_;
diff --git a/device/bluetooth/floss/floss_lescan_client.h b/device/bluetooth/floss/floss_lescan_client.h
index 5abbffd6..f7e382f 100644
--- a/device/bluetooth/floss/floss_lescan_client.h
+++ b/device/bluetooth/floss/floss_lescan_client.h
@@ -106,15 +106,15 @@
 struct DEVICE_BLUETOOTH_EXPORT ScanResult {
   std::string name;
   std::string address;
-  uint8_t addr_type;
-  uint16_t event_type;
-  uint8_t primary_phy;
-  uint8_t secondary_phy;
-  uint8_t advertising_sid;
-  int8_t tx_power;
-  int8_t rssi;
-  uint16_t periodic_adv_int;
-  uint8_t flags;
+  uint8_t addr_type = 0;
+  uint16_t event_type = 0;
+  uint8_t primary_phy = 0;
+  uint8_t secondary_phy = 0;
+  uint8_t advertising_sid = 0;
+  int8_t tx_power = 0;
+  int8_t rssi = 0;
+  uint16_t periodic_adv_int = 0;
+  uint8_t flags = 0;
   std::vector<device::BluetoothUUID> service_uuids;
   std::map<std::string, std::vector<uint8_t>> service_data;
   std::map<uint16_t, std::vector<uint8_t>> manufacturer_data;
diff --git a/device/fido/fido_request_handler_base.cc b/device/fido/fido_request_handler_base.cc
index 30eae25..145346e 100644
--- a/device/fido/fido_request_handler_base.cc
+++ b/device/fido/fido_request_handler_base.cc
@@ -11,6 +11,7 @@
 #include "base/functional/bind.h"
 #include "base/location.h"
 #include "base/logging.h"
+#include "base/notreached.h"
 #include "base/strings/string_piece.h"
 #include "base/task/sequenced_task_runner.h"
 #include "base/time/time.h"
@@ -36,6 +37,24 @@
 
 namespace device {
 
+namespace {
+bool IsGpmPasskeyAuthenticator(const FidoAuthenticator& authenticator) {
+  switch (authenticator.GetType()) {
+    case AuthenticatorType::kWinNative:
+    case AuthenticatorType::kTouchID:
+    case AuthenticatorType::kChromeOS:
+    case AuthenticatorType::kPhone:
+    case AuthenticatorType::kICloudKeychain:
+    case AuthenticatorType::kOther:
+      return false;
+    case AuthenticatorType::kEnclave:
+    case AuthenticatorType::kChromeOSPasskeys:
+      return true;
+  }
+  NOTREACHED_NORETURN();
+}
+}  // namespace
+
 // TransportAvailabilityCallbackReadiness stores state that tracks whether
 // |FidoRequestHandlerBase| is ready to call
 // |OnTransportAvailabilityEnumerated|.
@@ -399,7 +418,10 @@
       transport_availability_callback_readiness_->platform_discovery_succeeded =
           true;
       for (FidoAuthenticator* platform_authenticator : authenticators) {
-        if (platform_authenticator->GetType() == AuthenticatorType::kEnclave) {
+        if (IsGpmPasskeyAuthenticator(*platform_authenticator)) {
+          // GPM credential availability is checked in
+          // ChromeAuthenticatorRequestDelegate, so the authenticators don't
+          // implement GetPlatformCredentialStatus.
           continue;
         }
         transport_availability_info_.has_icloud_keychain |=
diff --git a/docs/website b/docs/website
index dc8137b..c181700 160000
--- a/docs/website
+++ b/docs/website
@@ -1 +1 @@
-Subproject commit dc8137be14f186da19c9cd33643cc075e15b568e
+Subproject commit c18170086240b4b598a76c73e89ff026af5d3c57
diff --git a/extensions/renderer/extension_throttle_simulation_unittest.cc b/extensions/renderer/extension_throttle_simulation_unittest.cc
index 714b621..37aaf8f5 100644
--- a/extensions/renderer/extension_throttle_simulation_unittest.cc
+++ b/extensions/renderer/extension_throttle_simulation_unittest.cc
@@ -215,7 +215,7 @@
     if (num_ticks % ticks_per_column)
       ++num_columns;
     DCHECK_LE(num_columns, terminal_width);
-    std::unique_ptr<int[]> columns(new int[num_columns]);
+    std::vector<int> columns(num_columns);
     for (int tx = 0; tx < num_ticks; ++tx) {
       int cx = tx / ticks_per_column;
       if (tx % ticks_per_column == 0)
diff --git a/headless/test/data/protocol/sanity/show-file-picker-interception-expected.txt b/headless/test/data/protocol/sanity/show-file-picker-interception-expected.txt
index 6e675e7f..af8e06e 100644
--- a/headless/test/data/protocol/sanity/show-file-picker-interception-expected.txt
+++ b/headless/test/data/protocol/sanity/show-file-picker-interception-expected.txt
@@ -1,11 +1,8 @@
 Tests that file picker interception works as expected
 {
-    stack : Error: Failed to execute 'showOpenFilePicker' on 'Window': Intercepted by Page.setInterceptFileChooserDialog().     at <anonymous>:2:12
 }
 {
-    stack : Error: Failed to execute 'showSaveFilePicker' on 'Window': Intercepted by Page.setInterceptFileChooserDialog().     at <anonymous>:2:12
 }
 {
-    stack : Error: Failed to execute 'showDirectoryPicker' on 'Window': Intercepted by Page.setInterceptFileChooserDialog().     at <anonymous>:2:12
 }
 Intercepted file chooser mode: selectSingle
\ No newline at end of file
diff --git a/infra/config/generated/builders/try/mac_chromium_10.15_rel_ng/gn-args.json b/infra/config/generated/builders/try/mac_chromium_10.15_rel_ng/gn-args.json
index 354eeb5..ea5fe78 100644
--- a/infra/config/generated/builders/try/mac_chromium_10.15_rel_ng/gn-args.json
+++ b/infra/config/generated/builders/try/mac_chromium_10.15_rel_ng/gn-args.json
@@ -4,7 +4,6 @@
     "is_component_build": false,
     "is_debug": false,
     "symbol_level": 0,
-    "target_cpu": "x64",
     "use_remoteexec": true,
     "use_siso": true
   }
diff --git a/infra/config/generated/luci/cr-buildbucket.cfg b/infra/config/generated/luci/cr-buildbucket.cfg
index 57ab040..2c310c4 100644
--- a/infra/config/generated/luci/cr-buildbucket.cfg
+++ b/infra/config/generated/luci/cr-buildbucket.cfg
@@ -22901,7 +22901,8 @@
       name: "Mac10.15 Tests"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
-      dimensions: "cpu:arm64"
+      dimensions: "cores:8"
+      dimensions: "cpu:x86-64"
       dimensions: "free_space:standard"
       dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.chromium.ci"
@@ -99124,7 +99125,7 @@
       name: "mac-updater-try-builder-rel"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
-      dimensions: "cpu:x86-64"
+      dimensions: "cpu:arm64"
       dimensions: "os:Mac"
       dimensions: "pool:luci.chromium.try"
       exe {
@@ -101118,7 +101119,7 @@
       name: "mac_chromium_10.15_rel_ng"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
-      dimensions: "cpu:arm64"
+      dimensions: "cpu:x86-64"
       dimensions: "os:Mac-14"
       dimensions: "pool:luci.chromium.try"
       dimensions: "ssd:1"
diff --git a/infra/config/generated/testing/variants.pyl b/infra/config/generated/testing/variants.pyl
index d11961e..12c7f96 100644
--- a/infra/config/generated/testing/variants.pyl
+++ b/infra/config/generated/testing/variants.pyl
@@ -267,16 +267,16 @@
   },
   'LACROS_VERSION_SKEW_CANARY': {
     'identifier': 'Lacros version skew testing ash canary',
-    'description': 'Run with ash-chrome version 127.0.6492.0',
+    'description': 'Run with ash-chrome version 127.0.6493.0',
     'args': [
-      '--ash-chrome-path-override=../../lacros_version_skew_tests_v127.0.6492.0/test_ash_chrome',
+      '--ash-chrome-path-override=../../lacros_version_skew_tests_v127.0.6493.0/test_ash_chrome',
     ],
     'swarming': {
       'cipd_packages': [
         {
           'cipd_package': 'chromium/testing/linux-ash-chromium/x86_64/ash.zip',
-          'location': 'lacros_version_skew_tests_v127.0.6492.0',
-          'revision': 'version:127.0.6492.0',
+          'location': 'lacros_version_skew_tests_v127.0.6493.0',
+          'revision': 'version:127.0.6493.0',
         },
       ],
     },
diff --git a/infra/config/subprojects/chromium/ci/chromium.mac.star b/infra/config/subprojects/chromium/ci/chromium.mac.star
index 98241db..aec4d23 100644
--- a/infra/config/subprojects/chromium/ci/chromium.mac.star
+++ b/infra/config/subprojects/chromium/ci/chromium.mac.star
@@ -425,8 +425,6 @@
         ),
         build_gs_bucket = "chromium-mac-archive",
     ),
-    cores = None,
-    cpu = cpu.ARM64,
     console_view_entry = consoles.console_view_entry(
         category = "mac",
         short_name = "15",
diff --git a/infra/config/subprojects/chromium/try/tryserver.chromium.mac.star b/infra/config/subprojects/chromium/try/tryserver.chromium.mac.star
index ad52b83..a2f139e 100644
--- a/infra/config/subprojects/chromium/try/tryserver.chromium.mac.star
+++ b/infra/config/subprojects/chromium/try/tryserver.chromium.mac.star
@@ -330,11 +330,8 @@
         configs = [
             "release_try_builder",
             "reclient",
-            "x64",
         ],
     ),
-    cores = None,
-    cpu = cpu.ARM64,
     siso_remote_jobs = siso.remote_jobs.LOW_JOBS_FOR_CQ,
 )
 
diff --git a/infra/config/subprojects/chromium/try/tryserver.chromium.updater.star b/infra/config/subprojects/chromium/try/tryserver.chromium.updater.star
index 171e815..540ad8fa 100644
--- a/infra/config/subprojects/chromium/try/tryserver.chromium.updater.star
+++ b/infra/config/subprojects/chromium/try/tryserver.chromium.updater.star
@@ -3,7 +3,7 @@
 # found in the LICENSE file.
 """Definitions of builders in the tryserver.chromium.updater builder group."""
 
-load("//lib/builders.star", "os", "siso")
+load("//lib/builders.star", "cpu", "os", "siso")
 load("//lib/try.star", "try_")
 load("//lib/consoles.star", "consoles")
 load("//lib/gn_args.star", "gn_args")
@@ -109,6 +109,7 @@
             "release_try_builder",
         ],
     ),
+    cpu = cpu.ARM64,
     main_list_view = "try",
     tryjob = try_.job(
         location_filters = [
diff --git a/infra/config/targets/lacros-version-skew-variants.json b/infra/config/targets/lacros-version-skew-variants.json
index 64550e71..378b6933 100644
--- a/infra/config/targets/lacros-version-skew-variants.json
+++ b/infra/config/targets/lacros-version-skew-variants.json
@@ -1,16 +1,16 @@
 {
   "LACROS_VERSION_SKEW_CANARY": {
     "args": [
-      "--ash-chrome-path-override=../../lacros_version_skew_tests_v127.0.6492.0/test_ash_chrome"
+      "--ash-chrome-path-override=../../lacros_version_skew_tests_v127.0.6493.0/test_ash_chrome"
     ],
-    "description": "Run with ash-chrome version 127.0.6492.0",
+    "description": "Run with ash-chrome version 127.0.6493.0",
     "identifier": "Lacros version skew testing ash canary",
     "swarming": {
       "cipd_packages": [
         {
           "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-          "location": "lacros_version_skew_tests_v127.0.6492.0",
-          "revision": "version:127.0.6492.0"
+          "location": "lacros_version_skew_tests_v127.0.6493.0",
+          "revision": "version:127.0.6493.0"
         }
       ]
     }
diff --git a/internal b/internal
index 41f93f8e..be8ef87 160000
--- a/internal
+++ b/internal
@@ -1 +1 @@
-Subproject commit 41f93f8ed68a00c511a47259273c4e829383fad3
+Subproject commit be8ef875426e1cd969603fe4a9d6ac614ffa6e1f
diff --git a/ios/chrome/browser/push_notification/model/push_notification_client_manager.mm b/ios/chrome/browser/push_notification/model/push_notification_client_manager.mm
index aa80294..be5a23c8 100644
--- a/ios/chrome/browser/push_notification/model/push_notification_client_manager.mm
+++ b/ios/chrome/browser/push_notification/model/push_notification_client_manager.mm
@@ -28,7 +28,7 @@
     AddPushNotificationClient(std::make_unique<TipsNotificationClient>());
   }
 
-  if (IsContentPushNotificationsEnabled()) {
+  if (IsContentNotificationExperimentEnalbed()) {
     AddPushNotificationClient(std::make_unique<ContentNotificationClient>());
   }
 }
@@ -98,7 +98,7 @@
 PushNotificationClientManager::GetClients() {
   std::vector<PushNotificationClientId> client_ids = {
       PushNotificationClientId::kCommerce};
-  if (IsContentPushNotificationsEnabled()) {
+  if (IsContentNotificationExperimentEnalbed()) {
     client_ids.push_back(PushNotificationClientId::kContent);
     client_ids.push_back(PushNotificationClientId::kSports);
   }
diff --git a/ios/chrome/browser/push_notification/model/push_notification_settings_util_unittest.mm b/ios/chrome/browser/push_notification/model/push_notification_settings_util_unittest.mm
index 2e98c476..26fa28c 100644
--- a/ios/chrome/browser/push_notification/model/push_notification_settings_util_unittest.mm
+++ b/ios/chrome/browser/push_notification/model/push_notification_settings_util_unittest.mm
@@ -49,7 +49,7 @@
     fake_id_ = [FakeSystemIdentity fakeIdentity1];
     // TODO(b/318863934): Remove flag when enabled by default.
     feature_list_.InitWithFeatures(
-        {/*enabled=*/kContentPushNotifications, kIOSTipsNotifications},
+        {/*enabled=*/kContentNotificationExperiment, kIOSTipsNotifications},
         {/*disabled=*/});
     AddTestCasesToManager(manager_, browser_state_info(),
                           base::SysNSStringToUTF8(fake_id_.gaiaID),
diff --git a/ios/chrome/browser/shared/coordinator/scene/scene_controller.mm b/ios/chrome/browser/shared/coordinator/scene/scene_controller.mm
index c1241ac..02bbe361c 100644
--- a/ios/chrome/browser/shared/coordinator/scene/scene_controller.mm
+++ b/ios/chrome/browser/shared/coordinator/scene/scene_controller.mm
@@ -2239,11 +2239,13 @@
 
 - (void)showPasswordDetailsForCredential:
             (password_manager::CredentialUIEntry)credential
+                              inEditMode:(BOOL)editMode
                         showCancelButton:(BOOL)showCancelButton {
   UIViewController* baseViewController = self.currentInterface.viewController;
   if (self.settingsNavigationController) {
     [self.settingsNavigationController
         showPasswordDetailsForCredential:credential
+                              inEditMode:editMode
                         showCancelButton:showCancelButton];
     return;
   }
@@ -2252,6 +2254,7 @@
       passwordDetailsControllerForBrowser:browser
                                  delegate:self
                                credential:credential
+                               inEditMode:editMode
                          showCancelButton:showCancelButton];
   [baseViewController presentViewController:self.settingsNavigationController
                                    animated:YES
diff --git a/ios/chrome/browser/shared/public/commands/settings_commands.h b/ios/chrome/browser/shared/public/commands/settings_commands.h
index 67a449f0..b8a1e0e7 100644
--- a/ios/chrome/browser/shared/public/commands/settings_commands.h
+++ b/ios/chrome/browser/shared/public/commands/settings_commands.h
@@ -50,11 +50,13 @@
             (UIViewController*)baseViewController
                                     showCancelButton:(BOOL)showCancelButton;
 
-// Shows the password details page for a credential.
-// `showCancelButton` indicates whether a cancel button should be added as the
-// left navigation item of the password details view.
+// Shows the password details page for a credential. `editMode` indicates
+// whether the details page should be opened in edit mode. `showCancelButton`
+// indicates whether a cancel button should be added as the left navigation item
+// of the password details view.
 - (void)showPasswordDetailsForCredential:
             (password_manager::CredentialUIEntry)credential
+                              inEditMode:(BOOL)editMode
                         showCancelButton:(BOOL)showCancelButton;
 
 // Shows the list of profiles (addresses) in the settings.
diff --git a/ios/chrome/browser/ui/autofill/form_input_accessory/form_input_accessory_coordinator.mm b/ios/chrome/browser/ui/autofill/form_input_accessory/form_input_accessory_coordinator.mm
index 5d042b2..3bea225 100644
--- a/ios/chrome/browser/ui/autofill/form_input_accessory/form_input_accessory_coordinator.mm
+++ b/ios/chrome/browser/ui/autofill/form_input_accessory/form_input_accessory_coordinator.mm
@@ -510,12 +510,13 @@
   [generationProvider triggerPasswordGeneration];
 }
 
-- (void)openPasswordDetailsForCredential:
+- (void)openPasswordDetailsInEditModeForCredential:
     (password_manager::CredentialUIEntry)credential {
   [self reset];
   id<SettingsCommands> settingsCommandsHandler = HandlerForProtocol(
       self.browser->GetCommandDispatcher(), SettingsCommands);
   [settingsCommandsHandler showPasswordDetailsForCredential:credential
+                                                 inEditMode:YES
                                            showCancelButton:YES];
 }
 
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_password_coordinator.h b/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_password_coordinator.h
index 2a73321..8b69964 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_password_coordinator.h
+++ b/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_password_coordinator.h
@@ -34,8 +34,8 @@
 // Opens password suggestion confirmation alert.
 - (void)openPasswordSuggestion;
 
-// Opens the details of the given credential.
-- (void)openPasswordDetailsForCredential:
+// Opens the details of the given credential in edit mode.
+- (void)openPasswordDetailsInEditModeForCredential:
     (password_manager::CredentialUIEntry)credential;
 
 @end
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_password_coordinator.mm b/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_password_coordinator.mm
index 8ff17cb..9c4e462a 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_password_coordinator.mm
+++ b/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_password_coordinator.mm
@@ -144,12 +144,12 @@
   }];
 }
 
-- (void)openPasswordDetailsForCredential:
+- (void)openPasswordDetailsInEditModeForCredential:
     (password_manager::CredentialUIEntry)credential {
   __weak id<PasswordCoordinatorDelegate> weakDelegate = self.delegate;
 
   [self dismissIfNecessaryThenDoCompletion:^{
-    [weakDelegate openPasswordDetailsForCredential:credential];
+    [weakDelegate openPasswordDetailsInEditModeForCredential:credential];
   }];
 }
 
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_password_mediator.mm b/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_password_mediator.mm
index 1d9f251..319c0b7 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_password_mediator.mm
+++ b/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_password_mediator.mm
@@ -445,7 +445,7 @@
   __weak __typeof(self) weakSelf = self;
   UIAction* editAction = [actionFactory actionToEditWithBlock:^{
     [weakSelf.navigator
-        openPasswordDetailsForCredential:CredentialUIEntry(password)];
+        openPasswordDetailsInEditModeForCredential:CredentialUIEntry(password)];
   }];
 
   return editAction;
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/password_list_navigator.h b/ios/chrome/browser/ui/autofill/manual_fill/password_list_navigator.h
index 87dcacb4..8a9f9b7 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/password_list_navigator.h
+++ b/ios/chrome/browser/ui/autofill/manual_fill/password_list_navigator.h
@@ -24,8 +24,8 @@
 // Opens password suggestion.
 - (void)openPasswordSuggestion;
 
-// Opens the details of the given credential.
-- (void)openPasswordDetailsForCredential:
+// Opens the details of the given credential in edit mode.
+- (void)openPasswordDetailsInEditModeForCredential:
     (password_manager::CredentialUIEntry)credential;
 
 @end
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/password_view_controller_egtest.mm b/ios/chrome/browser/ui/autofill/manual_fill/password_view_controller_egtest.mm
index 3b64ccb..02de1893 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/password_view_controller_egtest.mm
+++ b/ios/chrome/browser/ui/autofill/manual_fill/password_view_controller_egtest.mm
@@ -1119,13 +1119,16 @@
   [[EarlGrey selectElementWithMatcher:OverflowMenuEditAction()]
       performAction:grey_tap()];
 
-  // Check that the details page opened.
-  [[EarlGrey selectElementWithMatcher:grey_accessibilityID(
-                                          kPasswordDetailsViewControllerID)]
-      assertWithMatcher:grey_sufficientlyVisible()];
+  // Edit the username.
+  [[EarlGrey selectElementWithMatcher:grey_text(base::SysUTF8ToNSString(
+                                          kExampleUsername))]
+      performAction:grey_replaceText(@"new username")];
 
-  // TODO(crbug.com/326406846): Check that the details page was opened in edit
-  // mode.
+  // Tap Done Button.
+  [[EarlGrey selectElementWithMatcher:NavigationBarDoneButton()]
+      performAction:grey_tap()];
+
+  // TODO(crbug.com/332956674): Check that the updated suggestion is visible.
 }
 
 // Tests that tapping the "Autofill Form" button fills the password form with
diff --git a/ios/chrome/browser/ui/browser_view/browser_coordinator.mm b/ios/chrome/browser/ui/browser_view/browser_coordinator.mm
index 19e025e..06a4cb2 100644
--- a/ios/chrome/browser/ui/browser_view/browser_coordinator.mm
+++ b/ios/chrome/browser/ui/browser_view/browser_coordinator.mm
@@ -3421,6 +3421,7 @@
   id<SettingsCommands> settingsHandler =
       HandlerForProtocol(_dispatcher, SettingsCommands);
   [settingsHandler showPasswordDetailsForCredential:credential
+                                         inEditMode:NO
                                    showCancelButton:YES];
 }
 
diff --git a/ios/chrome/browser/ui/bubble/bubble_presenter_egtest.mm b/ios/chrome/browser/ui/bubble/bubble_presenter_egtest.mm
index 70be800b..d6c38329 100644
--- a/ios/chrome/browser/ui/bubble/bubble_presenter_egtest.mm
+++ b/ios/chrome/browser/ui/bubble/bubble_presenter_egtest.mm
@@ -258,8 +258,9 @@
 #define MAYBE_testSwipeBackForwardIPHShowsOnNavigationAndHidesOnNavigation \
   FLAKY_testSwipeBackForwardIPHShowsOnNavigationAndHidesOnNavigation
 #else
+// TODO(crbug.com/341948849): This test fails on devices.
 #define MAYBE_testSwipeBackForwardIPHShowsOnNavigationAndHidesOnNavigation \
-  testSwipeBackForwardIPHShowsOnNavigationAndHidesOnNavigation
+  DISABLED_testSwipeBackForwardIPHShowsOnNavigationAndHidesOnNavigation
 #endif
 - (void)MAYBE_testSwipeBackForwardIPHShowsOnNavigationAndHidesOnNavigation {
   [self relaunchWithIPHFeatureForSafariSwitcher:@"IPH_iOSSwipeBackForward"];
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_coordinator.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_coordinator.mm
index c9ab7270..46fb6c3 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_coordinator.mm
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_coordinator.mm
@@ -450,6 +450,7 @@
 }
 
 - (void)didTapMagicStackEditButton {
+  base::RecordAction(base::UserMetricsAction("IOSMagicStackSettingsOpened"));
   _magicStackHalfSheetTableViewController =
       [[MagicStackHalfSheetTableViewController alloc] init];
 
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_metrics_constants.h b/ios/chrome/browser/ui/content_suggestions/content_suggestions_metrics_constants.h
index 94cdadee..b077f19 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_metrics_constants.h
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_metrics_constants.h
@@ -29,6 +29,7 @@
 extern const char kMagicStackModuleEngagementTabResumptionIndexHistogram[];
 extern const char kMagicStackModuleEngagementSafetyCheckIndexHistogram[];
 extern const char kMagicStackModuleEngagementParcelTrackingIndexHistogram[];
+extern const char kMagicStackModuleDisabledHistogram[];
 extern const char kContentNotificationSnackbarEventHistogram[];
 
 // The name of the histogram that records fetch time for the Segmentation
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_metrics_constants.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_metrics_constants.mm
index 2b4aae5..fa4eac7 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_metrics_constants.mm
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_metrics_constants.mm
@@ -37,6 +37,8 @@
     "IOS.MagicStack.Module.Click.SafetyCheck";
 const char kMagicStackModuleEngagementParcelTrackingIndexHistogram[] =
     "IOS.MagicStack.Module.Click.ParcelTracking";
+const char kMagicStackModuleDisabledHistogram[] =
+    "IOS.MagicStack.Module.Disabled";
 const char kContentNotificationSnackbarEventHistogram[] =
     "ContentNotifications.Promo.Snackbar.Event";
 
diff --git a/ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_collection_view.mm b/ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_collection_view.mm
index d4ba20d1..d72ead9 100644
--- a/ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_collection_view.mm
+++ b/ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_collection_view.mm
@@ -7,6 +7,7 @@
 #import "base/check.h"
 #import "base/debug/dump_without_crashing.h"
 #import "base/ios/block_types.h"
+#import "base/metrics/histogram_macros.h"
 #import "base/numerics/safe_conversions.h"
 #import "ios/chrome/browser/shared/ui/util/uikit_ui_util.h"
 #import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_tile_layout_util.h"
@@ -20,6 +21,15 @@
 #import "ios/chrome/browser/ui/content_suggestions/magic_stack/placeholder_config.h"
 #import "ios/chrome/browser/ui/ntp/metrics/home_metrics.h"
 
+namespace {
+
+// Constants const for users scrolling metrics.
+const char kMagicStackScrollToIndexHistogram[] =
+    "IOS.MagicStack.ScrollActionToIndex";
+const float kMaxModuleHistogramIndex = 50;
+
+}  // namespace
+
 typedef NSDiffableDataSourceSnapshot<NSString*, MagicStackModule*>
     MagicStackSnapshot;
 
@@ -312,8 +322,13 @@
 
   if (velocity <= -kMagicStackMinimumPaginationScrollVelocity) {
     closestPage--;
+
+    UMA_HISTOGRAM_EXACT_LINEAR(kMagicStackScrollToIndexHistogram, closestPage,
+                               kMaxModuleHistogramIndex);
   } else if (velocity >= kMagicStackMinimumPaginationScrollVelocity) {
     closestPage++;
+    UMA_HISTOGRAM_EXACT_LINEAR(kMagicStackScrollToIndexHistogram, closestPage,
+                               kMaxModuleHistogramIndex);
   }
   _magicStackPage = closestPage;
   return _magicStackPage * (moduleWidth + kMagicStackSpacing) -
diff --git a/ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_ranking_model.mm b/ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_ranking_model.mm
index 37e4e87..70be9b3 100644
--- a/ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_ranking_model.mm
+++ b/ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_ranking_model.mm
@@ -166,6 +166,8 @@
 #pragma mark - SetUpListMediatorAudience
 
 - (void)removeSetUpList {
+  UMA_HISTOGRAM_ENUMERATION(kMagicStackModuleDisabledHistogram,
+                            ContentSuggestionsModuleType::kCompactedSetUpList);
   DCHECK(IsIOSMagicStackCollectionViewEnabled());
   [self.delegate magicStackRankingModel:self
                           didRemoveItem:_setUpListMediator.setUpListConfigs[0]];
@@ -180,6 +182,8 @@
 #pragma mark - SafetyCheckMagicStackMediatorDelegate
 
 - (void)removeSafetyCheckModule {
+  UMA_HISTOGRAM_ENUMERATION(kMagicStackModuleDisabledHistogram,
+                            ContentSuggestionsModuleType::kSafetyCheck);
   if (IsIOSMagicStackCollectionViewEnabled()) {
     [self.delegate
         magicStackRankingModel:self
@@ -220,6 +224,8 @@
 }
 
 - (void)removeTabResumptionModule {
+  UMA_HISTOGRAM_ENUMERATION(kMagicStackModuleDisabledHistogram,
+                            ContentSuggestionsModuleType::kTabResumption);
   if (IsIOSMagicStackCollectionViewEnabled()) {
     [self.delegate magicStackRankingModel:self
                             didRemoveItem:_tabResumptionMediator.itemConfig];
@@ -261,6 +267,8 @@
 }
 
 - (void)parcelTrackingDisabled {
+  UMA_HISTOGRAM_ENUMERATION(kMagicStackModuleDisabledHistogram,
+                            ContentSuggestionsModuleType::kParcelTracking);
   if (IsIOSMagicStackCollectionViewEnabled()) {
     [self.delegate magicStackRankingModel:self
                             didRemoveItem:_parcelTrackingMediator
diff --git a/ios/chrome/browser/ui/content_suggestions/safety_check/utils.mm b/ios/chrome/browser/ui/content_suggestions/safety_check/utils.mm
index 3bebca3..6b5b655 100644
--- a/ios/chrome/browser/ui/content_suggestions/safety_check/utils.mm
+++ b/ios/chrome/browser/ui/content_suggestions/safety_check/utils.mm
@@ -91,6 +91,7 @@
     password_manager::CredentialUIEntry credential =
         compromised_credentials.front();
     [settingsHandler showPasswordDetailsForCredential:credential
+                                           inEditMode:NO
                                      showCancelButton:YES];
     return;
   }
diff --git a/ios/chrome/browser/ui/search_engine_choice/search_engine_choice_egtest.mm b/ios/chrome/browser/ui/search_engine_choice/search_engine_choice_egtest.mm
index de4ca08..7fbd7309 100644
--- a/ios/chrome/browser/ui/search_engine_choice/search_engine_choice_egtest.mm
+++ b/ios/chrome/browser/ui/search_engine_choice/search_engine_choice_egtest.mm
@@ -14,6 +14,15 @@
 #import "ios/testing/earl_grey/app_launch_manager.h"
 #import "ios/testing/earl_grey/earl_grey_test.h"
 
+namespace {
+
+// Microsoft search engine name.
+NSString* const kMicrosoftBingSearchEngineName = @"Microsoft Bing";
+// Google search engine name.
+NSString* const kGoogleSearchEngineName = @"Google";
+
+}  // namespace
+
 @interface SearchEngineTestCase : ChromeTestCase
 @end
 
@@ -35,13 +44,17 @@
 
 - (AppLaunchConfiguration)appConfigurationForTestCase {
   AppLaunchConfiguration config = [super appConfigurationForTestCase];
-  // Set the country to one that is eligible for the choice screen (in this
-  // case, France).
+  // Need to use `switches::kEeaListCountryOverride` as the country to list all
+  // the search engines. This is to make sure the more button appears.
   config.additional_args.push_back(
-      "--" + std::string(switches::kSearchEngineChoiceCountry) + "=FR");
+      "--" + std::string(switches::kSearchEngineChoiceCountry) + "=" +
+      switches::kEeaListCountryOverride);
   // Force the dialog to trigger also for existing users.
   config.additional_args.push_back(
-      "--enable-features=SearchEngineChoiceTrigger:for_tagged_profiles_only/"
+      std::string("--enable-features=") +
+      switches::kSearchEngineChoiceTrigger.name + ":" +
+      switches::kSearchEngineChoiceTriggerForTaggedProfilesOnly.name +
+      "/"
       "false");
   config.additional_args.push_back(
       "--" + std::string(switches::kForceSearchEngineChoiceScreen));
@@ -77,33 +90,20 @@
 // Tests that the Search Engine Choice screen is displayed, that the primary
 // button is correctly updated when the user selects a search engine then
 // scrolls down and that it correctly sets the default search engine.
-// TODO(crbug.com/329210226): Re-enable the test.
-- (void)FLAKY_testSearchEngineChoiceScreenSelectThenScroll {
+- (void)testSearchEngineChoiceScreenSelectThenScroll {
   // Checks that the choice screen is shown
   [SearchEngineChoiceEarlGreyUI verifySearchEngineChoiceScreenIsDisplayed];
   id<GREYMatcher> moreButtonMatcher =
       grey_accessibilityID(kSearchEngineMoreButtonIdentifier);
-  // The more button is not visible on iPads, only on iPhones.
-  // TODO(crbug.com/329579023): We need have a more reliable way to know if
-  // there is a more button or not instead of checking if the test is running
-  // on iPad or iPhone.
-  BOOL moreButtonVisible = ![ChromeEarlGrey isIPadIdiom];
-  if (moreButtonVisible) {
-    // Verifies that the primary button is initially the "More" button.
-    [[EarlGrey selectElementWithMatcher:moreButtonMatcher]
-        assertWithMatcher:grey_allOf(grey_enabled(), grey_notNil(), nil)];
-  }
   // Selects a search engine.
-  NSString* searchEngineToSelect = @"Bing";
+  NSString* searchEngineToSelect = kMicrosoftBingSearchEngineName;
   [SearchEngineChoiceEarlGreyUI
       selectSearchEngineCellWithName:searchEngineToSelect
                      scrollDirection:kGREYDirectionDown
                               amount:50];
-  if (moreButtonVisible) {
-    // Taps the primary button. This scrolls the table down to the bottom.
-    [[[EarlGrey selectElementWithMatcher:moreButtonMatcher]
-        assertWithMatcher:grey_notNil()] performAction:grey_tap()];
-  }
+  // Taps the primary button. This scrolls the table down to the bottom.
+  [[[EarlGrey selectElementWithMatcher:moreButtonMatcher]
+      assertWithMatcher:grey_notNil()] performAction:grey_tap()];
   // Verify that the "More" button has been removed.
   [[EarlGrey selectElementWithMatcher:moreButtonMatcher]
       assertWithMatcher:grey_nil()];
@@ -116,29 +116,17 @@
 // Tests that the Search Engine Choice screen is displayed, that the
 // primary button is correctly updated when the user scrolls down then selects a
 // search engine and that it correctly sets the default search engine.
-// TODO(crbug.com/329210226): Re-enable the test.
-- (void)FLAKY_testSearchEngineChoiceScreenScrollThenSelect {
+- (void)testSearchEngineChoiceScreenScrollThenSelect {
   // Checks that the choice screen is shown
   [SearchEngineChoiceEarlGreyUI verifySearchEngineChoiceScreenIsDisplayed];
   id<GREYMatcher> moreButtonMatcher =
       grey_accessibilityID(kSearchEngineMoreButtonIdentifier);
-  // The more button is not visible on iPads, only on iPhones.
-  // TODO(crbug.com/329579023): We need have a more reliable way to know if
-  // there is a more button or not instead of checking if the test is running
-  // on iPad or iPhone.
-  BOOL moreButtonVisible = ![ChromeEarlGrey isIPadIdiom];
-  if (moreButtonVisible) {
-    // Verifies that the primary button is initially the "More" button.
-    [[EarlGrey selectElementWithMatcher:moreButtonMatcher]
-        assertWithMatcher:grey_allOf(grey_enabled(), grey_notNil(), nil)];
-    // Taps the primary button. This scrolls the table down to the bottom.
-    [[[EarlGrey selectElementWithMatcher:moreButtonMatcher]
-        assertWithMatcher:grey_notNil()] performAction:grey_tap()];
-  } else {
-    // Verify that the more button is not visible.
-    [[EarlGrey selectElementWithMatcher:moreButtonMatcher]
-        assertWithMatcher:grey_nil()];
-  }
+  // Verifies that the "More" button is visible.
+  [[EarlGrey selectElementWithMatcher:moreButtonMatcher]
+      assertWithMatcher:grey_allOf(grey_enabled(), grey_notNil(), nil)];
+  // Taps the more button. This scrolls the table down to the bottom.
+  [[[EarlGrey selectElementWithMatcher:moreButtonMatcher]
+      assertWithMatcher:grey_notNil()] performAction:grey_tap()];
   // Verifies that the primary button is now the disabled "Set as Default"
   // button.
   id<GREYMatcher> primaryActionButtonMatcher =
@@ -148,7 +136,7 @@
                                    nil)];
 
   // Selects a search engine.
-  NSString* searchEngineToSelect = @"Bing";
+  NSString* searchEngineToSelect = kMicrosoftBingSearchEngineName;
   [SearchEngineChoiceEarlGreyUI
       selectSearchEngineCellWithName:searchEngineToSelect
                      scrollDirection:kGREYDirectionUp
@@ -166,7 +154,7 @@
   if ([ChromeEarlGrey isIPadIdiom]) {
     return;
   }
-  NSString* googleSearchEngineIdentifier = @"Google";
+  NSString* googleSearchEngineIdentifier = kGoogleSearchEngineName;
   [SearchEngineChoiceEarlGreyUI
       selectSearchEngineCellWithName:googleSearchEngineIdentifier
                      scrollDirection:kGREYDirectionDown
diff --git a/ios/chrome/browser/ui/search_engine_choice/search_engine_choice_view_controller.mm b/ios/chrome/browser/ui/search_engine_choice/search_engine_choice_view_controller.mm
index a1ef820..e37ff0f 100644
--- a/ios/chrome/browser/ui/search_engine_choice/search_engine_choice_view_controller.mm
+++ b/ios/chrome/browser/ui/search_engine_choice/search_engine_choice_view_controller.mm
@@ -73,9 +73,6 @@
   button.snippetText = element.snippetDescription;
   button.translatesAutoresizingMaskIntoConstraints = NO;
   button.searchEngineKeyword = element.keyword;
-  button.accessibilityIdentifier =
-      [NSString stringWithFormat:@"%@%@", kSnippetSearchEngineIdentifierPrefix,
-                                 element.name];
   return button;
 }
 
@@ -137,7 +134,6 @@
   SetConfigurationTitle(
       button, l10n_util::GetNSString(IDS_SEARCH_ENGINE_CHOICE_BUTTON_TITLE));
   button.translatesAutoresizingMaskIntoConstraints = NO;
-  button.accessibilityIdentifier = kSetAsDefaultSearchEngineIdentifier;
   // Add semantic group, so the user can skip all the search engine stack view,
   // and jump to the SetAsDefault button, using VoiceOver.
   button.accessibilityContainerType = UIAccessibilityContainerTypeSemanticGroup;
@@ -359,6 +355,8 @@
   _floatingSetAsDefaultButton = CreateSetAsDefaultButton();
   _floatingSetAsDefaultButton.translatesAutoresizingMaskIntoConstraints = NO;
   [_floatingSetAsDefaultButtonContainer addSubview:_floatingSetAsDefaultButton];
+  _floatingSetAsDefaultButton.accessibilityIdentifier =
+      kSetAsDefaultSearchEngineIdentifier;
   _floatingSetAsDefaultButtonContainer.accessibilityContainerType =
       UIAccessibilityContainerTypeSemanticGroup;
   [_floatingSetAsDefaultButton addTarget:self
@@ -371,6 +369,7 @@
   _morePillButton = CreateMorePillButton();
   _morePillButton.translatesAutoresizingMaskIntoConstraints = NO;
   [view addSubview:_morePillButton];
+  _morePillButton.accessibilityIdentifier = kSearchEngineMoreButtonIdentifier;
   [_morePillButton addTarget:self
                       action:@selector(moreButtonAction)
             forControlEvents:UIControlEventTouchUpInside];
diff --git a/ios/chrome/browser/ui/settings/autofill/autofill_credit_card_table_view_controller.mm b/ios/chrome/browser/ui/settings/autofill/autofill_credit_card_table_view_controller.mm
index e91322a..a1a3138 100644
--- a/ios/chrome/browser/ui/settings/autofill/autofill_credit_card_table_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/autofill/autofill_credit_card_table_view_controller.mm
@@ -52,6 +52,8 @@
 
 namespace {
 
+constexpr base::TimeDelta kUpdatePrefDelay = base::Seconds(0.3);
+
 enum SectionIdentifier : NSInteger {
   SectionIdentifierAutofillCardSwitch = kSectionIdentifierEnumZero,
   SectionIdentifierMandatoryReauthSwitch,
@@ -507,8 +509,20 @@
   [self setSwitchItemOn:[switchView isOn]
                itemType:ItemTypeAutofillCardSwitch
       sectionIdentifier:SectionIdentifierAutofillCardSwitch];
-  [self setAutofillCreditCardEnabled:[switchView isOn]];
-  self.addButtonInToolbar.enabled = [self isAutofillCreditCardEnabled];
+
+  // Delay updating the pref when VoiceOver is running to prevent a temporary
+  // focus shift due to simultaneous UI updates, see crbug.com/326923292.
+  if (UIAccessibilityIsVoiceOverRunning()) {
+    __weak __typeof(self) weakSelf = self;
+    base::SequencedTaskRunner::GetCurrentDefault()->PostDelayedTask(
+        FROM_HERE, base::BindOnce(^{
+          [weakSelf
+              updateAutofillCreditCardPrefAndToolbarForState:[switchView isOn]];
+        }),
+        kUpdatePrefDelay);
+  } else {
+    [self updateAutofillCreditCardPrefAndToolbarForState:[switchView isOn]];
+  }
 }
 
 - (void)mandatoryReauthSwitchChanged:(UISwitch*)switchView {
@@ -856,6 +870,13 @@
       /*opt_in=*/!mandatoryReauthEnabled, flow_event);
 }
 
+// Updates the Autofill Credit Card pref and the view controller's toolbar
+// according to the provided `enabled` state.
+- (void)updateAutofillCreditCardPrefAndToolbarForState:(BOOL)enabled {
+  [self setAutofillCreditCardEnabled:enabled];
+  self.addButtonInToolbar.enabled = [self isAutofillCreditCardEnabled];
+}
+
 #pragma mark - AutofillAddCreditCardCoordinatorDelegate
 
 - (void)autofillAddCreditCardCoordinatorWantsToBeStopped:
diff --git a/ios/chrome/browser/ui/settings/password/password_details/password_details_coordinator.h b/ios/chrome/browser/ui/settings/password/password_details/password_details_coordinator.h
index e6d5f605..673c3bf 100644
--- a/ios/chrome/browser/ui/settings/password/password_details/password_details_coordinator.h
+++ b/ios/chrome/browser/ui/settings/password/password_details/password_details_coordinator.h
@@ -51,6 +51,9 @@
 // Delegate.
 @property(nonatomic, weak) id<PasswordDetailsCoordinatorDelegate> delegate;
 
+// Whether the coordinator's view controller should be opened in edit mode.
+@property(nonatomic, assign) BOOL openInEditMode;
+
 // Determine if we need to setup a cancel button on the navigation's left bar
 // button.
 @property(nonatomic) BOOL showCancelButton;
diff --git a/ios/chrome/browser/ui/settings/password/password_details/password_details_coordinator.mm b/ios/chrome/browser/ui/settings/password/password_details/password_details_coordinator.mm
index fc70f35..d71d7f2 100644
--- a/ios/chrome/browser/ui/settings/password/password_details/password_details_coordinator.mm
+++ b/ios/chrome/browser/ui/settings/password/password_details/password_details_coordinator.mm
@@ -178,6 +178,9 @@
   self.viewController.snackbarCommandsHandler = HandlerForProtocol(
       self.browser->GetCommandDispatcher(), SnackbarCommands);
   self.viewController.reauthModule = self.reauthenticationModule;
+  if (self.openInEditMode) {
+    [self.viewController editButtonPressed];
+  }
   if (self.showCancelButton) {
     [self.viewController setupLeftCancelButton];
   }
diff --git a/ios/chrome/browser/ui/settings/settings_navigation_controller.h b/ios/chrome/browser/ui/settings/settings_navigation_controller.h
index 9611a2a..7643663 100644
--- a/ios/chrome/browser/ui/settings/settings_navigation_controller.h
+++ b/ios/chrome/browser/ui/settings/settings_navigation_controller.h
@@ -122,6 +122,7 @@
                                        delegate
                              credential:
                                  (password_manager::CredentialUIEntry)credential
+                             inEditMode:(BOOL)editMode
                        showCancelButton:(BOOL)showCancelButton;
 
 // Creates and displays a new UIViewController for user to report an issue.
diff --git a/ios/chrome/browser/ui/settings/settings_navigation_controller.mm b/ios/chrome/browser/ui/settings/settings_navigation_controller.mm
index db6d0b8..67714c0 100644
--- a/ios/chrome/browser/ui/settings/settings_navigation_controller.mm
+++ b/ios/chrome/browser/ui/settings/settings_navigation_controller.mm
@@ -340,6 +340,7 @@
                                        delegate
                              credential:
                                  (password_manager::CredentialUIEntry)credential
+                             inEditMode:(BOOL)editMode
                        showCancelButton:(BOOL)showCancelButton {
   SettingsNavigationController* navigationController =
       [[SettingsNavigationController alloc]
@@ -347,6 +348,7 @@
                              browser:browser
                             delegate:delegate];
   [navigationController showPasswordDetailsForCredential:credential
+                                              inEditMode:editMode
                                         showCancelButton:showCancelButton];
 
   return navigationController;
@@ -830,6 +832,7 @@
 
 - (void)showPasswordDetailsForCredential:
             (password_manager::CredentialUIEntry)credential
+                              inEditMode:(BOOL)editMode
                         showCancelButton:(BOOL)showCancelButton {
   // TODO(crbug.com/40067451): Switch back to DCHECK if the number of reports is
   // low.
@@ -842,6 +845,7 @@
                                            BuildReauthenticationModule()
                                context:DetailsContext::kOutsideSettings];
   self.passwordDetailsCoordinator.delegate = self;
+  self.passwordDetailsCoordinator.openInEditMode = editMode;
   self.passwordDetailsCoordinator.showCancelButton = showCancelButton;
   [self.passwordDetailsCoordinator start];
 }
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/tab_groups/tab_group_header.mm b/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/tab_groups/tab_group_header.mm
index 0ee97624..f747aa4 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/tab_groups/tab_group_header.mm
+++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/tab_groups/tab_group_header.mm
@@ -5,7 +5,6 @@
 #import "ios/chrome/browser/ui/tab_switcher/tab_grid/grid/tab_groups/tab_group_header.h"
 
 namespace {
-constexpr CGFloat kTitleVerticalMargin = 10;
 constexpr CGFloat kDotTitleSeparationMargin = 8;
 constexpr CGFloat kColoredDotSize = 20;
 }  // namespace
@@ -35,10 +34,8 @@
       [_coloredDotView.leadingAnchor
           constraintEqualToAnchor:self.leadingAnchor],
       [_titleView.trailingAnchor constraintEqualToAnchor:self.trailingAnchor],
-      [_titleView.topAnchor constraintEqualToAnchor:self.topAnchor
-                                           constant:kTitleVerticalMargin],
-      [_titleView.bottomAnchor constraintEqualToAnchor:self.bottomAnchor
-                                              constant:kTitleVerticalMargin],
+      [_titleView.topAnchor constraintEqualToAnchor:self.topAnchor],
+      [_titleView.bottomAnchor constraintEqualToAnchor:self.bottomAnchor],
     ]];
   }
   return self;
diff --git a/ios/testing/earl_grey/base_earl_grey_test_case.mm b/ios/testing/earl_grey/base_earl_grey_test_case.mm
index 10b0a7b..66ed1c9 100644
--- a/ios/testing/earl_grey/base_earl_grey_test_case.mm
+++ b/ios/testing/earl_grey/base_earl_grey_test_case.mm
@@ -38,6 +38,7 @@
 + (void)setUp {
   NSArray<NSString*>* blockedURLs = @[
     @".*app-measurement\\.com.*",
+    @".*app-analytics-services\\.com.*",
   ];
   [[GREYConfiguration sharedConfiguration]
           setValue:blockedURLs
diff --git a/ios_internal b/ios_internal
index 9b0057e..4a88d53 160000
--- a/ios_internal
+++ b/ios_internal
@@ -1 +1 @@
-Subproject commit 9b0057e70bdd2d28c72ae0d461dc8539dee69b6e
+Subproject commit 4a88d53072d3477a8f490768dc5fa35e3f6434a7
diff --git a/media/filters/chunk_demuxer.h b/media/filters/chunk_demuxer.h
index 6798951..8570b78 100644
--- a/media/filters/chunk_demuxer.h
+++ b/media/filters/chunk_demuxer.h
@@ -328,6 +328,7 @@
   // per the MSE specification. App could use a back-off and retry strategy or
   // otherwise alter their behavior to attempt to buffer media for further
   // playback.
+  // TODO(b/341763066): use base::span instead of a ptr+size.
   [[nodiscard]] bool AppendToParseBuffer(const std::string& id,
                                          const uint8_t* data,
                                          size_t length);
diff --git a/media/filters/chunk_demuxer_unittest.cc b/media/filters/chunk_demuxer_unittest.cc
index 445d4ba..b8e4799 100644
--- a/media/filters/chunk_demuxer_unittest.cc
+++ b/media/filters/chunk_demuxer_unittest.cc
@@ -13,6 +13,7 @@
 #include <utility>
 
 #include "base/command_line.h"
+#include "base/containers/heap_array.h"
 #include "base/functional/bind.h"
 #include "base/functional/callback_helpers.h"
 #include "base/memory/raw_ptr.h"
@@ -237,8 +238,7 @@
   void CreateInitSegment(int stream_flags,
                          bool is_audio_encrypted,
                          bool is_video_encrypted,
-                         std::unique_ptr<uint8_t[]>* buffer,
-                         int* size) {
+                         base::HeapArray<uint8_t>* buffer) {
     bool has_audio = (stream_flags & HAS_AUDIO) != 0;
     bool has_video = (stream_flags & HAS_VIDEO) != 0;
     scoped_refptr<DecoderBuffer> ebml_header;
@@ -291,12 +291,12 @@
       }
     }
 
-    *size = ebml_header->size() + info->size() + kTracksHeaderSize +
-            tracks_element_size;
+    size_t size = ebml_header->size() + info->size() + kTracksHeaderSize +
+                  tracks_element_size;
 
-    buffer->reset(new uint8_t[*size]);
+    *buffer = base::HeapArray<uint8_t>::Uninit(size);
 
-    uint8_t* buf = buffer->get();
+    uint8_t* buf = buffer->data();
     memcpy(buf, ebml_header->data(), ebml_header->size());
     buf += ebml_header->size();
 
@@ -391,13 +391,15 @@
   }
 #endif
 
-  bool AppendData(const uint8_t* data, size_t length) {
-    return AppendData(kSourceId, data, length);
+  bool AppendData(base::span<const uint8_t> data) {
+    return AppendData(kSourceId, data);
   }
 
   bool AppendCluster(const std::string& source_id,
                      std::unique_ptr<Cluster> cluster) {
-    return AppendData(source_id, cluster->data(), cluster->size());
+    return AppendData(
+        source_id, base::make_span(cluster->data(),
+                                   static_cast<size_t>(cluster->bytes_used())));
   }
 
   bool AppendCluster(std::unique_ptr<Cluster> cluster) {
@@ -636,11 +638,10 @@
   }
 
   bool AppendData(const std::string& source_id,
-                  const uint8_t* data,
-                  size_t length) {
+                  base::span<const uint8_t> data) {
     EXPECT_CALL(host_, OnBufferedTimeRangesChanged(_)).Times(AnyNumber());
 
-    if (!demuxer_->AppendToParseBuffer(source_id, data, length)) {
+    if (!demuxer_->AppendToParseBuffer(source_id, data.data(), data.size())) {
       return false;
     }
 
@@ -656,20 +657,18 @@
     return result == StreamParser::ParseStatus::kSuccess;
   }
 
-  bool AppendDataInPieces(const uint8_t* data, size_t length) {
-    return AppendDataInPieces(data, length, 7);
+  bool AppendDataInPieces(base::span<const uint8_t> data) {
+    return AppendDataInPieces(data, 7);
   }
 
-  bool AppendDataInPieces(const uint8_t* data,
-                          size_t length,
-                          size_t piece_size) {
-    const uint8_t* start = data;
-    const uint8_t* end = data + length;
+  bool AppendDataInPieces(base::span<const uint8_t> data, size_t piece_size) {
+    size_t start = 0;
+    size_t end = data.size();
     while (start < end) {
-      size_t append_size = std::min(piece_size,
-                                    static_cast<size_t>(end - start));
-      if (!AppendData(start, append_size))
+      size_t append_size = std::min(piece_size, end - start);
+      if (!AppendData(data.subspan(start, append_size))) {
         return false;
+      }
       start += append_size;
     }
     return true;
@@ -689,22 +688,20 @@
                                           int stream_flags,
                                           bool is_audio_encrypted,
                                           bool is_video_encrypted) {
-    std::unique_ptr<uint8_t[]> info_tracks;
-    int info_tracks_size = 0;
-    CreateInitSegment(stream_flags,
-                      is_audio_encrypted, is_video_encrypted,
-                      &info_tracks, &info_tracks_size);
-    return AppendData(source_id, info_tracks.get(), info_tracks_size);
+    base::HeapArray<uint8_t> info_tracks;
+    CreateInitSegment(stream_flags, is_audio_encrypted, is_video_encrypted,
+                      &info_tracks);
+    return AppendData(source_id, info_tracks);
   }
 
   void AppendGarbage() {
     // Fill up an array with gibberish.
     int garbage_cluster_size = 10;
-    std::unique_ptr<uint8_t[]> garbage_cluster(
-        new uint8_t[garbage_cluster_size]);
+    auto garbage_cluster =
+        base::HeapArray<uint8_t>::Uninit(garbage_cluster_size);
     for (int i = 0; i < garbage_cluster_size; ++i)
       garbage_cluster[i] = i;
-    ASSERT_FALSE(AppendData(garbage_cluster.get(), garbage_cluster_size));
+    ASSERT_FALSE(AppendData(garbage_cluster));
   }
 
   PipelineStatusCallback CreateInitDoneCallback(
@@ -870,7 +867,7 @@
     // Expect duration adjustment since actual duration differs slightly from
     // duration in the init segment
     EXPECT_CALL(host_, SetDuration(base::Milliseconds(2768)));
-    EXPECT_TRUE(AppendData(bear1->data(), bear1->size()));
+    EXPECT_TRUE(AppendData(base::make_span(bear1->data(), bear1->size())));
     // Last audio frame has timestamp 2721 and duration 24 (estimated from max
     // seen so far for audio track).
     // Last video frame has timestamp 2703 and duration 33 (from TrackEntry
@@ -884,21 +881,21 @@
     // generated from media/test/data/bear-640x360.webm and
     // media/test/data/bear-320x240.webm respectively.
     EXPECT_CALL(*this, InitSegmentReceivedMock(_));
-    EXPECT_TRUE(AppendData(bear2->data(), 4340));
+    EXPECT_TRUE(AppendData(base::make_span(bear2->data(), 4340u)));
 
     // Append a media segment that goes from [0.527000, 1.014000).
     EXPECT_MEDIA_LOG(WebMSimpleBlockDurationEstimated(24));
     EXPECT_MEDIA_LOG(TrimmedSpliceOverlap(527000, 524000, 20000));
-    EXPECT_TRUE(AppendData(bear2->data() + 55290, 18785));
+    EXPECT_TRUE(AppendData(base::make_span(bear2->data() + 55290, 18785u)));
     CheckExpectedRanges("{ [0,2736) }");
 
     // Append initialization segment for bear1 and buffer [779-1197)
     // segment.
     EXPECT_CALL(*this, InitSegmentReceivedMock(_));
-    EXPECT_TRUE(AppendData(bear1->data(), 4370));
+    EXPECT_TRUE(AppendData(base::make_span(bear1->data(), 4370u)));
     EXPECT_MEDIA_LOG(WebMSimpleBlockDurationEstimated(24));
     EXPECT_MEDIA_LOG(TrimmedSpliceOverlap(779000, 759000, 3000));
-    EXPECT_TRUE(AppendData(bear1->data() + 72737, 28183));
+    EXPECT_TRUE(AppendData(base::make_span(bear1->data() + 72737, 28183u)));
     CheckExpectedRanges("{ [0,2736) }");
 
     MarkEndOfStream(PIPELINE_OK);
@@ -1233,7 +1230,8 @@
     scoped_refptr<DecoderBuffer> buffer = ReadTestDataFile(filename);
     EXPECT_CALL(*this, InitSegmentReceivedMock(_));
 
-    EXPECT_TRUE(AppendDataInPieces(buffer->data(), buffer->size(), 512));
+    EXPECT_TRUE(AppendDataInPieces(
+        base::make_span(buffer->data(), buffer->size()), 512));
 
     // Verify that the timestamps on the first few packets match what we
     // expect.
@@ -1567,11 +1565,12 @@
   std::unique_ptr<Cluster> cluster_a(GenerateCluster(0, 6));
 
   // Split the cluster into two appends at an arbitrary point near the end.
-  int first_append_size = cluster_a->size() - 11;
-  int second_append_size = cluster_a->size() - first_append_size;
+  size_t first_append_size = cluster_a->bytes_used() - 11;
+  size_t second_append_size = cluster_a->bytes_used() - first_append_size;
 
   // Append the first part of the cluster.
-  ASSERT_TRUE(AppendData(cluster_a->data(), first_append_size));
+  ASSERT_TRUE(
+      AppendData(base::make_span(cluster_a->data(), first_append_size)));
 
   ExpectRead(DemuxerStream::AUDIO, 0);
   ExpectRead(DemuxerStream::VIDEO, 0);
@@ -1580,8 +1579,8 @@
   Seek(base::Seconds(5));
 
   // Append the rest of the cluster.
-  ASSERT_TRUE(
-      AppendData(cluster_a->data() + first_append_size, second_append_size));
+  ASSERT_TRUE(AppendData(base::make_span(cluster_a->data() + first_append_size,
+                                         second_append_size)));
 
   // Append the new cluster and verify that only the blocks
   // in the new cluster are returned.
@@ -1592,17 +1591,15 @@
 // Test the case where AppendToParseBuffer() and RunSegmentParserLoop() are
 // called before ChunkDemuxer::Initialize().
 TEST_F(ChunkDemuxerTest, AppendToParseBufferBeforeInit) {
-  std::unique_ptr<uint8_t[]> info_tracks;
-  int info_tracks_size = 0;
-  CreateInitSegment(HAS_AUDIO | HAS_VIDEO, false, false, &info_tracks,
-                    &info_tracks_size);
+  base::HeapArray<uint8_t> info_tracks;
+  CreateInitSegment(HAS_AUDIO | HAS_VIDEO, false, false, &info_tracks);
   // TODO(crbug.com/40244241): If it's found this actually never happens in
   // production, via instrumentation, and the underlying code gets a DCHECK or
   // CHECK added to fail if called before Init(), this test case will need to be
   // changed. For now, the demuxer silently allows the append to succeed, but
   // any RunSegmentParserLoop() will fail if it's still before Init().
-  ASSERT_TRUE(demuxer_->AppendToParseBuffer(kSourceId, info_tracks.get(),
-                                            info_tracks_size));
+  ASSERT_TRUE(demuxer_->AppendToParseBuffer(kSourceId, info_tracks.data(),
+                                            info_tracks.size()));
 
   ASSERT_EQ(StreamParser::ParseStatus::kFailed,
             demuxer_->RunSegmentParserLoop(kSourceId,
@@ -1717,7 +1714,8 @@
   // Verify that AppendData() can still accept more data.
   std::unique_ptr<Cluster> cluster_c(GenerateCluster(45, 2));
   EXPECT_MEDIA_LOG(TrimmedSpliceOverlap(45000, 28000, 6000));
-  ASSERT_TRUE(AppendData(cluster_c->data(), cluster_c->size()));
+  ASSERT_TRUE(AppendData(base::make_span(
+      cluster_c->data(), static_cast<size_t>(cluster_c->bytes_used()))));
   Seek(base::Milliseconds(45));
   CheckExpectedBuffers(audio_stream, "45K");
   CheckExpectedBuffers(video_stream, "45K");
@@ -1745,7 +1743,8 @@
 
   // Verify that AppendData() ignores data after the error.
   std::unique_ptr<Cluster> cluster_b(GenerateCluster(20, 2));
-  ASSERT_FALSE(AppendData(cluster_b->data(), cluster_b->size()));
+  ASSERT_FALSE(AppendData(base::make_span(
+      cluster_b->data(), static_cast<size_t>(cluster_b->bytes_used()))));
 }
 
 TEST_F(ChunkDemuxerTest, BeforeClusterTimecode) {
@@ -1795,7 +1794,8 @@
 
   // Verify that AppendData() ignores data after the error.
   std::unique_ptr<Cluster> cluster_b(GenerateCluster(6, 2));
-  ASSERT_FALSE(AppendData(cluster_b->data(), cluster_b->size()));
+  ASSERT_FALSE(AppendData(base::make_span(
+      cluster_b->data(), static_cast<size_t>(cluster_b->bytes_used()))));
 }
 
 TEST_F(ChunkDemuxerTest, PerStreamMonotonicallyIncreasingTimestamps) {
@@ -2058,30 +2058,29 @@
 
   ASSERT_EQ(AddId(), ChunkDemuxer::kOk);
 
-  std::unique_ptr<uint8_t[]> info_tracks;
-  int info_tracks_size = 0;
-  CreateInitSegment(HAS_AUDIO | HAS_VIDEO,
-                    false, false, &info_tracks, &info_tracks_size);
+  base::HeapArray<uint8_t> info_tracks;
+  CreateInitSegment(HAS_AUDIO | HAS_VIDEO, false, false, &info_tracks);
 
   std::unique_ptr<Cluster> cluster_a(kDefaultFirstCluster());
   std::unique_ptr<Cluster> cluster_b(kDefaultSecondCluster());
 
-  size_t buffer_size = info_tracks_size + cluster_a->size() + cluster_b->size();
-  std::unique_ptr<uint8_t[]> buffer(new uint8_t[buffer_size]);
-  uint8_t* dst = buffer.get();
-  memcpy(dst, info_tracks.get(), info_tracks_size);
-  dst += info_tracks_size;
+  size_t buffer_size =
+      info_tracks.size() + cluster_a->bytes_used() + cluster_b->bytes_used();
+  auto buffer = base::HeapArray<uint8_t>::Uninit(buffer_size);
+  uint8_t* dst = buffer.data();
+  memcpy(dst, info_tracks.data(), info_tracks.size());
+  dst += info_tracks.size();
 
-  memcpy(dst, cluster_a->data(), cluster_a->size());
-  dst += cluster_a->size();
+  memcpy(dst, cluster_a->data(), cluster_a->bytes_used());
+  dst += cluster_a->bytes_used();
 
-  memcpy(dst, cluster_b->data(), cluster_b->size());
-  dst += cluster_b->size();
+  memcpy(dst, cluster_b->data(), cluster_b->bytes_used());
+  dst += cluster_b->bytes_used();
 
   ExpectInitMediaLogs(HAS_AUDIO | HAS_VIDEO);
   EXPECT_CALL(*this, InitSegmentReceivedMock(_));
   EXPECT_FALSE(DidProgress());
-  ASSERT_TRUE(AppendDataInPieces(buffer.get(), buffer_size));
+  ASSERT_TRUE(AppendDataInPieces(buffer));
   EXPECT_TRUE(DidProgress());
 
   GenerateExpectedReads(0, 9);
@@ -2212,14 +2211,15 @@
 
   // Append data one byte at a time until one or both reads complete.
   int i = 0;
-  for (; i < cluster->size() && !(audio_read_done || video_read_done); ++i) {
-    ASSERT_TRUE(AppendData(cluster->data() + i, 1));
+  for (; i < cluster->bytes_used() && !(audio_read_done || video_read_done);
+       ++i) {
+    ASSERT_TRUE(AppendData(base::make_span(cluster->data() + i, 1u)));
     base::RunLoop().RunUntilIdle();
   }
 
   EXPECT_TRUE(audio_read_done || video_read_done);
   EXPECT_GT(i, 0);
-  EXPECT_LT(i, cluster->size());
+  EXPECT_LT(i, cluster->bytes_used());
 
   audio_read_done = false;
   video_read_done = false;
@@ -2233,8 +2233,9 @@
   EXPECT_FALSE(video_read_done);
 
   // Append the remaining data.
-  ASSERT_LT(i, cluster->size());
-  ASSERT_TRUE(AppendData(cluster->data() + i, cluster->size() - i));
+  ASSERT_LT(i, cluster->bytes_used());
+  ASSERT_TRUE(AppendData(base::make_span(
+      cluster->data() + i, static_cast<size_t>(cluster->bytes_used()) - i)));
 
   base::RunLoop().RunUntilIdle();
 
@@ -2252,7 +2253,7 @@
 
   EXPECT_MEDIA_LOG(StreamParsingFailed());
   uint8_t tmp = 0;
-  ASSERT_FALSE(AppendData(&tmp, 1));
+  ASSERT_FALSE(AppendData(base::make_span(&tmp, 1u)));
 }
 
 TEST_F(ChunkDemuxerTest, AVHeadersWithAudioOnlyType) {
@@ -3268,7 +3269,8 @@
 
   std::unique_ptr<Cluster> cluster = GenerateCluster(0, 2);
   // Append only part of the cluster data.
-  ASSERT_TRUE(AppendData(cluster->data(), cluster->size() - 13));
+  ASSERT_TRUE(AppendData(
+      base::make_span(cluster->data(), cluster->bytes_used() - 13u)));
 
   // Confirm we're in the middle of parsing a media segment.
   ASSERT_TRUE(demuxer_->IsParsingMediaSegment(kSourceId));
@@ -3329,8 +3331,9 @@
   while (appended_bytes < buffer->size()) {
     size_t cur_chunk_size =
         std::min(chunk_size, buffer->size() - appended_bytes);
-    ASSERT_TRUE(
-        AppendData(kSourceId, buffer->data() + appended_bytes, cur_chunk_size));
+    ASSERT_TRUE(AppendData(
+        kSourceId,
+        base::make_span(buffer->data() + appended_bytes, cur_chunk_size)));
     appended_bytes += cur_chunk_size;
   }
 
@@ -3393,8 +3396,9 @@
   while (appended_bytes < buffer->size()) {
     size_t cur_chunk_size =
         std::min(chunk_size, buffer->size() - appended_bytes);
-    ASSERT_TRUE(
-        AppendData(kSourceId, buffer->data() + appended_bytes, cur_chunk_size));
+    ASSERT_TRUE(AppendData(
+        kSourceId,
+        base::make_span(buffer->data() + appended_bytes, cur_chunk_size)));
     appended_bytes += cur_chunk_size;
   }
 
@@ -3488,7 +3492,7 @@
   EXPECT_MEDIA_LOG(WebMSimpleBlockDurationEstimated(23)).Times(2);
   for (size_t i = 0; i < sizeof(kBuffer); i++) {
     DVLOG(3) << "Appending and testing index " << i;
-    ASSERT_TRUE(AppendData(kBuffer + i, 1));
+    ASSERT_TRUE(AppendData(base::make_span(kBuffer + i, 1u)));
     bool expected_return_value = kExpectedReturnValues[i];
     EXPECT_EQ(expected_return_value,
               demuxer_->IsParsingMediaSegment(kSourceId));
@@ -3549,7 +3553,8 @@
 
 TEST_F(ChunkDemuxerTest, ZeroLengthAppend) {
   ASSERT_TRUE(InitDemuxer(HAS_AUDIO | HAS_VIDEO));
-  ASSERT_TRUE(AppendData(NULL, 0));
+  base::span<uint8_t> data;
+  ASSERT_TRUE(AppendData(data.subspan(0)));
 }
 
 TEST_F(ChunkDemuxerTest, AppendAfterEndOfStream) {
@@ -4039,7 +4044,8 @@
   ExpectInitMediaLogs(HAS_AUDIO);
   EXPECT_CALL(*this, InitSegmentReceivedMock(_));
   EXPECT_MEDIA_LOG(WebMSimpleBlockDurationEstimated(2));
-  ASSERT_TRUE(AppendDataInPieces(buffer->data(), buffer->size(), 128));
+  ASSERT_TRUE(
+      AppendDataInPieces(base::make_span(buffer->data(), buffer->size()), 128));
 
   DemuxerStream* stream = GetStream(DemuxerStream::AUDIO);
   CheckExpectedBuffers(stream, "50KP 50K 62K 86K 109K 122K 125K 128K");
@@ -4070,7 +4076,8 @@
   ExpectInitMediaLogs(HAS_AUDIO);
   EXPECT_CALL(*this, InitSegmentReceivedMock(_));
   EXPECT_MEDIA_LOG(WebMSimpleBlockDurationEstimated(2));
-  ASSERT_TRUE(AppendDataInPieces(buffer->data(), buffer->size(), 512));
+  ASSERT_TRUE(
+      AppendDataInPieces(base::make_span(buffer->data(), buffer->size()), 512));
   CheckExpectedRanges("{ }");
 
   DemuxerStream* stream = GetStream(DemuxerStream::AUDIO);
@@ -4083,7 +4090,8 @@
   EXPECT_MEDIA_LOG(WebMSimpleBlockDurationEstimated(22));
   EXPECT_CALL(host_, SetDuration(_)).Times(AnyNumber());
   ASSERT_TRUE(SetTimestampOffset(kSourceId, duration_1));
-  ASSERT_TRUE(AppendDataInPieces(buffer2->data(), buffer2->size(), 512));
+  ASSERT_TRUE(AppendDataInPieces(
+      base::make_span(buffer2->data(), buffer2->size()), 512));
   CheckExpectedRanges("{ [2768,5542) }");
 
   Seek(duration_1);
@@ -4215,11 +4223,13 @@
 
   // Add two clusters separated by Cues in a single Append() call.
   std::unique_ptr<Cluster> cluster = GenerateCluster(0, 0, 4, true);
-  std::vector<uint8_t> data(cluster->data(), cluster->data() + cluster->size());
+  std::vector<uint8_t> data(cluster->data(),
+                            cluster->data() + cluster->bytes_used());
   data.insert(data.end(), kCuesHeader, kCuesHeader + sizeof(kCuesHeader));
   cluster = GenerateCluster(46, 66, 5, true);
-  data.insert(data.end(), cluster->data(), cluster->data() + cluster->size());
-  ASSERT_TRUE(AppendData(&*data.begin(), data.size()));
+  data.insert(data.end(), cluster->data(),
+              cluster->data() + cluster->bytes_used());
+  ASSERT_TRUE(AppendData(base::make_span(&*data.begin(), data.size())));
 
   CheckExpectedRanges("{ [0,115) }");
 }
@@ -4228,7 +4238,7 @@
   ASSERT_TRUE(InitDemuxer(HAS_AUDIO | HAS_VIDEO));
 
   ASSERT_TRUE(AppendCluster(GenerateCluster(0, 0, 4)));
-  ASSERT_TRUE(AppendData(kCuesHeader, sizeof(kCuesHeader)));
+  ASSERT_TRUE(AppendData(base::make_span(kCuesHeader, sizeof(kCuesHeader))));
   ASSERT_TRUE(AppendCluster(GenerateCluster(46, 66, 5)));
   CheckExpectedRanges("{ [0,115) }");
 }
@@ -4488,7 +4498,7 @@
   ASSERT_EQ(kVideoTrackNum, 1);
   int video_start = 0;
   bool found = false;
-  while (video_start < cluster->size() - 10) {
+  while (video_start < cluster->bytes_used() - 10) {
     if (cluster->data()[video_start] == 0xA3 &&
         cluster->data()[video_start + 9] == 0x81) {
       found = true;
@@ -4499,17 +4509,21 @@
 
   ASSERT_TRUE(found);
   ASSERT_GT(video_start, 0);
-  ASSERT_LT(video_start, cluster->size() - 3);
+  ASSERT_LT(video_start, cluster->bytes_used() - 3);
 
-  ASSERT_TRUE(AppendData(kSourceId, cluster->data(), video_start));
+  ASSERT_TRUE(AppendData(
+      kSourceId,
+      base::make_span(cluster->data(), static_cast<size_t>(video_start))));
   CheckExpectedRanges(DemuxerStream::AUDIO, "{ [0,30) }");
   CheckExpectedRanges(DemuxerStream::VIDEO, "{ }");
 
   demuxer_->Remove(kSourceId, base::TimeDelta(), base::Milliseconds(30));
 
   // Append the remainder of the cluster
-  ASSERT_TRUE(AppendData(kSourceId, cluster->data() + video_start,
-                         cluster->size() - video_start));
+  ASSERT_TRUE(AppendData(
+      kSourceId, base::make_span(cluster->data() + video_start,
+                                 static_cast<size_t>(cluster->bytes_used()) -
+                                     video_start)));
 
   CheckExpectedRanges(DemuxerStream::AUDIO, "{ [30,90) }");
   CheckExpectedRanges(DemuxerStream::VIDEO, "{ [0,91) }");
@@ -4607,8 +4621,8 @@
   EXPECT_CALL(*this, InitSegmentReceivedMock(_)).Times(2);
   EXPECT_MEDIA_LOG(SegmentMissingFrames("1")).Times(1);
 
-  EXPECT_TRUE(AppendData(kId1, data1->data(), data1->size()));
-  EXPECT_TRUE(AppendData(kId2, data2->data(), data2->size()));
+  EXPECT_TRUE(AppendData(kId1, base::make_span(data1->data(), data1->size())));
+  EXPECT_TRUE(AppendData(kId2, base::make_span(data2->data(), data2->size())));
   CheckExpectedRanges(kId1, "{ [0,12007) }");
   CheckExpectedRanges(kId2, "{ [0,10007) }");
 }
@@ -4929,7 +4943,8 @@
   EXPECT_MEDIA_LOG(SkippingSpliceTooLittleOverlap(2116888, 6));
   EXPECT_MEDIA_LOG(SkippingSpliceTooLittleOverlap(2441966, 6));
 
-  EXPECT_TRUE(AppendData(kPrimary, data->data(), data->size()));
+  EXPECT_TRUE(
+      AppendData(kPrimary, base::make_span(data->data(), data->size())));
 
   CheckExpectedRanges(kPrimary, "{ [1466,2267) }");
 }
diff --git a/media/filters/win/media_foundation_audio_decoder.cc b/media/filters/win/media_foundation_audio_decoder.cc
index eac3fd8..076714fe 100644
--- a/media/filters/win/media_foundation_audio_decoder.cc
+++ b/media/filters/win/media_foundation_audio_decoder.cc
@@ -2,12 +2,16 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "media/filters/win/media_foundation_audio_decoder.h"
+
 #include <mfapi.h>
 #include <mferror.h>
 #include <stdint.h>
 #include <wmcodecdsp.h>
 
 #include "base/auto_reset.h"
+#include "base/containers/span.h"
+#include "base/containers/span_writer.h"
 #include "base/functional/bind.h"
 #include "base/logging.h"
 #include "base/task/bind_post_task.h"
@@ -21,7 +25,6 @@
 #include "media/base/timestamp_constants.h"
 #include "media/base/win/mf_helpers.h"
 #include "media/base/win/mf_initializer.h"
-#include "media/filters/win/media_foundation_audio_decoder.h"
 
 namespace media {
 
@@ -551,7 +554,11 @@
     audio_buffer =
         AudioBuffer::CreateBuffer(kSampleFormatF32, channel_layout_,
                                   channel_count_, sample_rate_, frames, pool_);
-    base::SpanWriter<uint8_t> channel_data = audio_buffer->channel_data();
+    auto channel_data = base::SpanWriter<uint8_t>(
+        // TODO(crbug.com/40284755): channel_data() should be an array of spans,
+        // not unbounded pointers. This span is constructed unsoundly.
+        UNSAFE_BUFFERS(base::span(audio_buffer->channel_data()[0u],
+                                  frames * channel_count_ * 4u)));
     for (uint64_t i = 0; i < frames; i++) {
       for (uint64_t ch = 0; ch < channel_count_; ch++) {
         auto a = static_cast<int8_t>(destination[0u]);
diff --git a/media/formats/mp2t/mp2t_stream_parser_unittest.cc b/media/formats/mp2t/mp2t_stream_parser_unittest.cc
index 8a202b1..2283c106 100644
--- a/media/formats/mp2t/mp2t_stream_parser_unittest.cc
+++ b/media/formats/mp2t/mp2t_stream_parser_unittest.cc
@@ -4,6 +4,8 @@
 
 #include "media/formats/mp2t/mp2t_stream_parser.h"
 
+#include <openssl/aes.h>
+#include <openssl/evp.h>
 #include <stddef.h>
 #include <stdint.h>
 
@@ -11,9 +13,7 @@
 #include <memory>
 #include <string>
 
-#include <openssl/aes.h>
-#include <openssl/evp.h>
-
+#include "base/containers/heap_array.h"
 #include "base/functional/bind.h"
 #include "base/functional/callback_helpers.h"
 #include "base/logging.h"
@@ -88,11 +88,10 @@
                               reinterpret_cast<const uint8_t*>(iv.data()), 0),
             1);
   EVP_CIPHER_CTX_set_padding(ctx.get(), 0);
-  const size_t output_size = input_size;
-  std::unique_ptr<char[]> output(new char[output_size]);
+  auto output = base::HeapArray<char>::Uninit(input_size);
   uint8_t* in_ptr = const_cast<uint8_t*>(input);
-  uint8_t* out_ptr = reinterpret_cast<uint8_t*>(output.get());
-  size_t bytes_remaining = output_size;
+  uint8_t* out_ptr = reinterpret_cast<uint8_t*>(output.data());
+  size_t bytes_remaining = output.size();
 
   while (bytes_remaining) {
     int unused;
@@ -115,7 +114,7 @@
     }
   }
 
-  result.assign(output.get(), output_size);
+  result.assign(output.begin(), output.end());
   return result;
 }
 
diff --git a/media/formats/webm/cluster_builder.cc b/media/formats/webm/cluster_builder.cc
index 7290268..f441de4 100644
--- a/media/formats/webm/cluster_builder.cc
+++ b/media/formats/webm/cluster_builder.cc
@@ -62,8 +62,8 @@
   kInitialBufferSize = 32768,
 };
 
-Cluster::Cluster(std::unique_ptr<uint8_t[]> data, int size)
-    : data_(std::move(data)), size_(size) {}
+Cluster::Cluster(base::HeapArray<uint8_t> data, int bytes_used)
+    : data_(std::move(data)), bytes_used_(bytes_used) {}
 Cluster::~Cluster() = default;
 
 ClusterBuilder::ClusterBuilder() { Reset(); }
@@ -75,7 +75,7 @@
   cluster_timecode_ = cluster_timecode;
 
   // Write the timecode into the header.
-  uint8_t* buf = buffer_.get() + kClusterTimecodeOffset;
+  uint8_t* buf = buffer_.data() + kClusterTimecodeOffset;
   for (int i = 7; i >= 0; --i) {
     buf[i] = cluster_timecode & 0xff;
     cluster_timecode >>= 8;
@@ -88,11 +88,12 @@
                                     const uint8_t* data,
                                     int size) {
   int block_size = size + 4;
-  int bytes_needed = sizeof(kSimpleBlockHeader) + block_size;
-  if (bytes_needed > (buffer_size_ - bytes_used_))
+  size_t bytes_needed = sizeof(kSimpleBlockHeader) + block_size;
+  if (bytes_needed > (buffer_.size() - bytes_used_)) {
     ExtendBuffer(bytes_needed);
+  }
 
-  uint8_t* buf = buffer_.get() + bytes_used_;
+  uint8_t* buf = buffer_.data() + bytes_used_;
   int block_offset = bytes_used_;
   memcpy(buf, kSimpleBlockHeader, sizeof(kSimpleBlockHeader));
   UpdateUInt64(block_offset + kSimpleBlockSizeOffset, block_size);
@@ -133,7 +134,7 @@
                                            const uint8_t* data,
                                            int size) {
   int block_size = size + 4;
-  int bytes_needed = block_size;
+  size_t bytes_needed = block_size;
   if (include_block_duration) {
     bytes_needed += sizeof(kBlockGroupHeader);
   } else {
@@ -145,10 +146,11 @@
 
   int block_group_size = bytes_needed - 9;
 
-  if (bytes_needed > (buffer_size_ - bytes_used_))
+  if (bytes_needed > (buffer_.size() - bytes_used_)) {
     ExtendBuffer(bytes_needed);
+  }
 
-  uint8_t* buf = buffer_.get() + bytes_used_;
+  uint8_t* buf = buffer_.data() + bytes_used_;
   int block_group_offset = bytes_used_;
   if (include_block_duration) {
     memcpy(buf, kBlockGroupHeader, sizeof(kBlockGroupHeader));
@@ -226,29 +228,27 @@
 }
 
 void ClusterBuilder::Reset() {
-  buffer_size_ = kInitialBufferSize;
-  buffer_.reset(new uint8_t[buffer_size_]);
-  memcpy(buffer_.get(), kClusterHeader, sizeof(kClusterHeader));
+  buffer_ = base::HeapArray<uint8_t>::Uninit(kInitialBufferSize);
+  memcpy(buffer_.data(), kClusterHeader, sizeof(kClusterHeader));
   bytes_used_ = sizeof(kClusterHeader);
   cluster_timecode_ = -1;
 }
 
-void ClusterBuilder::ExtendBuffer(int bytes_needed) {
-  int new_buffer_size = 2 * buffer_size_;
+void ClusterBuilder::ExtendBuffer(size_t bytes_needed) {
+  size_t new_buffer_size = 2 * buffer_.size();
 
   while ((new_buffer_size - bytes_used_) < bytes_needed)
     new_buffer_size *= 2;
 
-  std::unique_ptr<uint8_t[]> new_buffer(new uint8_t[new_buffer_size]);
+  auto new_buffer = base::HeapArray<uint8_t>::Uninit(new_buffer_size);
 
-  memcpy(new_buffer.get(), buffer_.get(), bytes_used_);
+  memcpy(new_buffer.data(), buffer_.data(), bytes_used_);
   buffer_ = std::move(new_buffer);
-  buffer_size_ = new_buffer_size;
 }
 
 void ClusterBuilder::UpdateUInt64(int offset, int64_t value) {
-  DCHECK_LE(offset + 7, buffer_size_);
-  uint8_t* buf = buffer_.get() + offset;
+  DCHECK_LE(offset + 7u, buffer_.size());
+  uint8_t* buf = buffer_.data() + offset;
 
   // Fill the last 7 bytes of size field in big-endian order.
   for (int i = 7; i > 0; i--) {
diff --git a/media/formats/webm/cluster_builder.h b/media/formats/webm/cluster_builder.h
index ad6a042..d73de36 100644
--- a/media/formats/webm/cluster_builder.h
+++ b/media/formats/webm/cluster_builder.h
@@ -9,25 +9,29 @@
 
 #include <memory>
 
+#include "base/containers/heap_array.h"
+
 namespace media {
 
 class Cluster {
  public:
   Cluster() = delete;
 
-  Cluster(std::unique_ptr<uint8_t[]> data, int size);
+  // The size of the `bytes_used` might be less size of `data`.
+  Cluster(base::HeapArray<uint8_t> data, int bytes_used);
 
   Cluster(const Cluster&) = delete;
   Cluster& operator=(const Cluster&) = delete;
 
   ~Cluster();
 
-  const uint8_t* data() const { return data_.get(); }
-  int size() const { return size_; }
+  // TODO(frs): This should be changed to return a span.
+  const uint8_t* data() const { return data_.data(); }
+  int bytes_used() const { return bytes_used_; }
 
  private:
-  std::unique_ptr<uint8_t[]> data_;
-  int size_;
+  base::HeapArray<uint8_t> data_;
+  const int bytes_used_;
 };
 
 class ClusterBuilder {
@@ -72,7 +76,7 @@
                              const uint8_t* data,
                              int size);
   void Reset();
-  void ExtendBuffer(int bytes_needed);
+  void ExtendBuffer(size_t bytes_needed);
   void UpdateUInt64(int offset, int64_t value);
   void WriteBlock(uint8_t* buf,
                   int track_num,
@@ -81,9 +85,8 @@
                   const uint8_t* data,
                   int size);
 
-  std::unique_ptr<uint8_t[]> buffer_;
-  int buffer_size_;
-  int bytes_used_;
+  base::HeapArray<uint8_t> buffer_;
+  size_t bytes_used_;
   int64_t cluster_timecode_;
 };
 
diff --git a/media/formats/webm/webm_cluster_parser_unittest.cc b/media/formats/webm/webm_cluster_parser_unittest.cc
index 29470e0..3043ebb8 100644
--- a/media/formats/webm/webm_cluster_parser_unittest.cc
+++ b/media/formats/webm/webm_cluster_parser_unittest.cc
@@ -390,16 +390,17 @@
           WebMSimpleBlockDurationEstimated(kExpectedVideoEstimationInMs));
     }
 
-    int result = parser_->Parse(cluster->data(), parse_full_cluster ?
-                                cluster->size() : cluster->size() - 1);
+    int result = parser_->Parse(
+        cluster->data(),
+        parse_full_cluster ? cluster->bytes_used() : cluster->bytes_used() - 1);
     if (parse_full_cluster) {
       DVLOG(1) << "Verifying parse result of full cluster of "
                << blocks_in_cluster << " blocks";
-      EXPECT_EQ(cluster->size(), result);
+      EXPECT_EQ(cluster->bytes_used(), result);
     } else {
       DVLOG(1) << "Verifying parse result of cluster of "
                << blocks_in_cluster << " blocks with last block incomplete";
-      EXPECT_GT(cluster->size(), result);
+      EXPECT_GT(cluster->bytes_used(), result);
       EXPECT_LT(0, result);
     }
 
@@ -418,16 +419,16 @@
 
   // Send slightly less than the full cluster so all but the last block is
   // parsed.
-  int result = parser_->Parse(cluster->data(), cluster->size() - 1);
+  int result = parser_->Parse(cluster->data(), cluster->bytes_used() - 1);
   EXPECT_GT(result, 0);
-  EXPECT_LT(result, cluster->size());
+  EXPECT_LT(result, cluster->bytes_used());
 
   ASSERT_TRUE(VerifyBuffers(parser_, kDefaultBlockInfo, block_count - 1));
   parser_->Reset();
 
   // Now parse a whole cluster to verify that all the blocks will get parsed.
-  result = parser_->Parse(cluster->data(), cluster->size());
-  EXPECT_EQ(cluster->size(), result);
+  result = parser_->Parse(cluster->data(), cluster->bytes_used());
+  EXPECT_EQ(cluster->bytes_used(), result);
   ASSERT_TRUE(VerifyBuffers(parser_, kDefaultBlockInfo, block_count));
 }
 
@@ -436,8 +437,8 @@
   std::unique_ptr<Cluster> cluster(
       CreateCluster(0, kDefaultBlockInfo, block_count));
 
-  int result = parser_->Parse(cluster->data(), cluster->size());
-  EXPECT_EQ(cluster->size(), result);
+  int result = parser_->Parse(cluster->data(), cluster->bytes_used());
+  EXPECT_EQ(cluster->bytes_used(), result);
   ASSERT_TRUE(VerifyBuffers(parser_, kDefaultBlockInfo, block_count));
 }
 
@@ -447,7 +448,7 @@
       CreateCluster(0, kDefaultBlockInfo, block_count));
 
   const uint8_t* data = cluster->data();
-  int size = cluster->size();
+  int size = cluster->bytes_used();
   int default_parse_size = 3;
   int parse_size = std::min(default_parse_size, size);
 
@@ -520,8 +521,8 @@
   int block_count = std::size(kBlockInfo);
   std::unique_ptr<Cluster> cluster(CreateCluster(0, kBlockInfo, block_count));
 
-  int result = parser_->Parse(cluster->data(), cluster->size());
-  EXPECT_EQ(cluster->size(), result);
+  int result = parser_->Parse(cluster->data(), cluster->bytes_used());
+  EXPECT_EQ(cluster->bytes_used(), result);
   ASSERT_TRUE(VerifyBuffers(parser_, kBlockInfo, block_count));
 }
 
@@ -555,8 +556,8 @@
 
   EXPECT_MEDIA_LOG(WebMSimpleBlockDurationEstimated(23));
   EXPECT_MEDIA_LOG(WebMSimpleBlockDurationEstimated(34));
-  int result = parser_->Parse(cluster->data(), cluster->size());
-  EXPECT_EQ(cluster->size(), result);
+  int result = parser_->Parse(cluster->data(), cluster->bytes_used());
+  EXPECT_EQ(cluster->bytes_used(), result);
   ASSERT_TRUE(VerifyBuffers(parser_, kOutputBlockInfo, output_block_count));
 }
 
@@ -571,8 +572,8 @@
   EXPECT_MEDIA_LOG(WebMSimpleBlockDurationEstimated(
       WebMClusterParser::kDefaultVideoBufferDurationInMs));
 
-  int result = parser_->Parse(cluster->data(), cluster->size());
-  EXPECT_EQ(cluster->size(), result);
+  int result = parser_->Parse(cluster->data(), cluster->bytes_used());
+  EXPECT_EQ(cluster->bytes_used(), result);
   StreamParser::BufferQueueMap buffers;
   parser_->GetBuffers(&buffers);
   EXPECT_EQ(1UL, buffers[kVideoTrackNum].size());
@@ -588,7 +589,7 @@
       std::string(), "video_key_id", AudioCodec::kUnknown));
 
   EXPECT_MEDIA_LOG(HasSubstr("Failed to extract decrypt config"));
-  int result = parser_->Parse(cluster->data(), cluster->size());
+  int result = parser_->Parse(cluster->data(), cluster->bytes_used());
   EXPECT_EQ(-1, result);
 }
 
@@ -640,16 +641,16 @@
   // parsed. Though all the blocks are simple blocks, none should be held aside
   // for duration estimation prior to end of cluster detection because all the
   // tracks have DefaultDurations.
-  int result = parser_->Parse(cluster->data(), cluster->size() - 1);
+  int result = parser_->Parse(cluster->data(), cluster->bytes_used() - 1);
   EXPECT_GT(result, 0);
-  EXPECT_LT(result, cluster->size());
+  EXPECT_LT(result, cluster->bytes_used());
   ASSERT_TRUE(VerifyBuffers(parser_, kBlockInfo, block_count - 1));
 
   parser_->Reset();
 
   // Now parse a whole cluster to verify that all the blocks will get parsed.
-  result = parser_->Parse(cluster->data(), cluster->size());
-  EXPECT_EQ(cluster->size(), result);
+  result = parser_->Parse(cluster->data(), cluster->bytes_used());
+  EXPECT_EQ(cluster->bytes_used(), result);
   ASSERT_TRUE(VerifyBuffers(parser_, kBlockInfo, block_count));
 }
 
@@ -682,9 +683,9 @@
   // both missing from the result (parser should hold them aside for duration
   // estimation prior to end of cluster detection in the absence of
   // DefaultDurations.)
-  int result = parser_->Parse(cluster1->data(), cluster1->size() - 1);
+  int result = parser_->Parse(cluster1->data(), cluster1->bytes_used() - 1);
   EXPECT_GT(result, 0);
-  EXPECT_LT(result, cluster1->size());
+  EXPECT_LT(result, cluster1->bytes_used());
   ASSERT_TRUE(VerifyBuffers(parser_, kBlockInfo1, block_count1 - 3));
   StreamParser::BufferQueueMap buffers;
   parser_->GetBuffers(&buffers);
@@ -698,8 +699,8 @@
       WebMSimpleBlockDurationEstimated(kExpectedAudioEstimationInMs));
   EXPECT_MEDIA_LOG(
       WebMSimpleBlockDurationEstimated(kExpectedVideoEstimationInMs));
-  result = parser_->Parse(cluster1->data(), cluster1->size());
-  EXPECT_EQ(cluster1->size(), result);
+  result = parser_->Parse(cluster1->data(), cluster1->bytes_used());
+  EXPECT_EQ(cluster1->bytes_used(), result);
   ASSERT_TRUE(VerifyBuffers(parser_, kBlockInfo1, block_count1));
 
   // Verify that the estimated frame duration is tracked across clusters for
@@ -718,8 +719,8 @@
       WebMSimpleBlockDurationEstimated(kExpectedAudioEstimationInMs));
   EXPECT_MEDIA_LOG(
       WebMSimpleBlockDurationEstimated(kExpectedVideoEstimationInMs));
-  result = parser_->Parse(cluster2->data(), cluster2->size());
-  EXPECT_EQ(cluster2->size(), result);
+  result = parser_->Parse(cluster2->data(), cluster2->bytes_used());
+  EXPECT_EQ(cluster2->bytes_used(), result);
   ASSERT_TRUE(VerifyBuffers(parser_, kBlockInfo2, block_count2));
 }
 
@@ -754,9 +755,9 @@
   // both missing from the result (parser should hold them aside for duration
   // estimation prior to end of cluster detection in the absence of
   // DefaultDurations.)
-  int result = parser_->Parse(cluster1->data(), cluster1->size() - 1);
+  int result = parser_->Parse(cluster1->data(), cluster1->bytes_used() - 1);
   EXPECT_GT(result, 0);
-  EXPECT_LT(result, cluster1->size());
+  EXPECT_LT(result, cluster1->bytes_used());
   ASSERT_TRUE(VerifyBuffers(parser_, kBlockInfo1, block_count1 - 3));
   StreamParser::BufferQueueMap buffers;
   parser_->GetBuffers(&buffers);
@@ -770,8 +771,8 @@
       WebMSimpleBlockDurationEstimated(kExpectedAudioEstimationInMs));
   EXPECT_MEDIA_LOG(
       WebMSimpleBlockDurationEstimated(kExpectedVideoEstimationInMs));
-  result = parser_->Parse(cluster1->data(), cluster1->size());
-  EXPECT_EQ(cluster1->size(), result);
+  result = parser_->Parse(cluster1->data(), cluster1->bytes_used());
+  EXPECT_EQ(cluster1->bytes_used(), result);
   ASSERT_TRUE(VerifyBuffers(parser_, kBlockInfo1, block_count1));
 
   // Verify that the estimated frame duration is tracked across clusters for
@@ -790,8 +791,8 @@
       WebMSimpleBlockDurationEstimated(kExpectedAudioEstimationInMs));
   EXPECT_MEDIA_LOG(
       WebMSimpleBlockDurationEstimated(kExpectedVideoEstimationInMs));
-  result = parser_->Parse(cluster2->data(), cluster2->size());
-  EXPECT_EQ(cluster2->size(), result);
+  result = parser_->Parse(cluster2->data(), cluster2->bytes_used());
+  EXPECT_EQ(cluster2->bytes_used(), result);
   ASSERT_TRUE(VerifyBuffers(parser_, kBlockInfo2, block_count2));
 }
 
@@ -827,16 +828,16 @@
   // Send slightly less than the full cluster so all but the last block is
   // parsed. None should be held aside for duration estimation prior to end of
   // cluster detection because all the tracks have DefaultDurations.
-  int result = parser_->Parse(cluster->data(), cluster->size() - 1);
+  int result = parser_->Parse(cluster->data(), cluster->bytes_used() - 1);
   EXPECT_GT(result, 0);
-  EXPECT_LT(result, cluster->size());
+  EXPECT_LT(result, cluster->bytes_used());
   ASSERT_TRUE(VerifyBuffers(parser_, kBlockInfo, block_count - 1));
 
   parser_->Reset();
 
   // Now parse a whole cluster to verify that all the blocks will get parsed.
-  result = parser_->Parse(cluster->data(), cluster->size());
-  EXPECT_EQ(cluster->size(), result);
+  result = parser_->Parse(cluster->data(), cluster->bytes_used());
+  EXPECT_EQ(cluster->bytes_used(), result);
   ASSERT_TRUE(VerifyBuffers(parser_, kBlockInfo, block_count));
 }
 
@@ -865,16 +866,16 @@
   // Send slightly less than the full cluster so all but the last block is
   // parsed. None should be held aside for duration estimation prior to end of
   // cluster detection because all blocks have BlockDurations.
-  int result = parser_->Parse(cluster->data(), cluster->size() - 1);
+  int result = parser_->Parse(cluster->data(), cluster->bytes_used() - 1);
   EXPECT_GT(result, 0);
-  EXPECT_LT(result, cluster->size());
+  EXPECT_LT(result, cluster->bytes_used());
   ASSERT_TRUE(VerifyBuffers(parser_, kBlockInfo, block_count - 1));
 
   parser_->Reset();
 
   // Now parse a whole cluster to verify that all the blocks will get parsed.
-  result = parser_->Parse(cluster->data(), cluster->size());
-  EXPECT_EQ(cluster->size(), result);
+  result = parser_->Parse(cluster->data(), cluster->bytes_used());
+  EXPECT_EQ(cluster->bytes_used(), result);
   ASSERT_TRUE(VerifyBuffers(parser_, kBlockInfo, block_count));
 }
 
@@ -910,9 +911,9 @@
   // the second video is held back still (not yet at end of cluster) and the
   // first audio is held back still (no second block parsed fully yet and not
   // yet at end of cluster).
-  int result = parser_->Parse(cluster->data(), cluster->size() - 1);
+  int result = parser_->Parse(cluster->data(), cluster->bytes_used() - 1);
   EXPECT_GT(result, 0);
-  EXPECT_LT(result, cluster->size());
+  EXPECT_LT(result, cluster->bytes_used());
   ASSERT_TRUE(VerifyBuffers(parser_, kBlockInfo, block_count - 3));
 
   parser_->Reset();
@@ -923,8 +924,8 @@
   // the cluster, hence the expected order of MEDIA_LOGs here.
   EXPECT_MEDIA_LOG(WebMSimpleBlockDurationEstimated(23));
   EXPECT_MEDIA_LOG(WebMSimpleBlockDurationEstimated(33));
-  result = parser_->Parse(cluster->data(), cluster->size());
-  EXPECT_EQ(cluster->size(), result);
+  result = parser_->Parse(cluster->data(), cluster->bytes_used());
+  EXPECT_EQ(cluster->bytes_used(), result);
   ASSERT_TRUE(VerifyBuffers(parser_, kBlockInfo, block_count));
 }
 
@@ -950,8 +951,8 @@
       WebMClusterParser::kDefaultAudioBufferDurationInMs));
   EXPECT_MEDIA_LOG(WebMSimpleBlockDurationEstimated(
       WebMClusterParser::kDefaultVideoBufferDurationInMs));
-  int result = parser_->Parse(cluster->data(), cluster->size());
-  EXPECT_EQ(cluster->size(), result);
+  int result = parser_->Parse(cluster->data(), cluster->bytes_used());
+  EXPECT_EQ(cluster->bytes_used(), result);
   ASSERT_TRUE(VerifyBuffers(parser_, kBlockInfo, block_count));
 }
 
@@ -966,8 +967,8 @@
 
   int block_count = std::size(kBlockInfo);
   std::unique_ptr<Cluster> cluster(CreateCluster(0, kBlockInfo, block_count));
-  int result = parser_->Parse(cluster->data(), cluster->size());
-  EXPECT_EQ(cluster->size(), result);
+  int result = parser_->Parse(cluster->data(), cluster->bytes_used());
+  EXPECT_EQ(cluster->bytes_used(), result);
   ASSERT_TRUE(VerifyBuffers(parser_, kBlockInfo, block_count));
 }
 
@@ -994,8 +995,8 @@
       EXPECT_MEDIA_LOG(OpusPacketDurationTooHigh(duration_ms));
     }
 
-    int result = parser_->Parse(cluster->data(), cluster->size());
-    EXPECT_EQ(cluster->size(), result);
+    int result = parser_->Parse(cluster->data(), cluster->bytes_used());
+    EXPECT_EQ(cluster->bytes_used(), result);
     ASSERT_TRUE(VerifyBuffers(parser_, kBlockInfo, block_count));
 
     // Fail early if any iteration fails to meet the logging expectations.
@@ -1036,8 +1037,8 @@
     int block_count = std::size(block_infos);
     std::unique_ptr<Cluster> cluster(
         CreateCluster(0, block_infos, block_count));
-    int result = parser_->Parse(cluster->data(), cluster->size());
-    EXPECT_EQ(cluster->size(), result);
+    int result = parser_->Parse(cluster->data(), cluster->bytes_used());
+    EXPECT_EQ(cluster->bytes_used(), result);
 
     // BlockInfo duration will be used to verify buffer duration, so changing
     // duration to be that of the Opus packet to verify it was preferred.
@@ -1075,8 +1076,8 @@
 
   int block_count = std::size(kBlockInfo);
   std::unique_ptr<Cluster> cluster(CreateCluster(0, kBlockInfo, block_count));
-  int result = parser_->Parse(cluster->data(), cluster->size());
-  EXPECT_EQ(cluster->size(), result);
+  int result = parser_->Parse(cluster->data(), cluster->bytes_used());
+  EXPECT_EQ(cluster->bytes_used(), result);
 
   // Will verify that duration of buffer matches that of BlockDuration.
   ASSERT_TRUE(VerifyBuffers(parser_, kBlockInfo, block_count));
diff --git a/media/formats/webm/webm_parser_unittest.cc b/media/formats/webm/webm_parser_unittest.cc
index ae30e90..ccb246b 100644
--- a/media/formats/webm/webm_parser_unittest.cc
+++ b/media/formats/webm/webm_parser_unittest.cc
@@ -227,7 +227,8 @@
   CreateClusterExpectations(kBlockCount, true, &client_);
 
   WebMListParser parser(kWebMIdCluster, &client_);
-  EXPECT_EQ(cluster->size(), parser.Parse(cluster->data(), cluster->size()));
+  EXPECT_EQ(cluster->bytes_used(),
+            parser.Parse(cluster->data(), cluster->bytes_used()));
   EXPECT_TRUE(parser.IsParsingComplete());
 }
 
@@ -236,7 +237,7 @@
   CreateClusterExpectations(kBlockCount, true, &client_);
 
   const uint8_t* data = cluster->data();
-  int size = cluster->size();
+  int size = cluster->bytes_used();
   int default_parse_size = 3;
   WebMListParser parser(kWebMIdCluster, &client_);
   int parse_size = std::min(default_parse_size, size);
@@ -278,15 +279,16 @@
 
   // Send slightly less than the full cluster so all but the last block is
   // parsed.
-  int result = parser.Parse(cluster->data(), cluster->size() - 1);
+  int result = parser.Parse(cluster->data(), cluster->bytes_used() - 1);
   EXPECT_GT(result, 0);
-  EXPECT_LT(result, cluster->size());
+  EXPECT_LT(result, cluster->bytes_used());
   EXPECT_FALSE(parser.IsParsingComplete());
 
   parser.Reset();
 
   // Now parse a whole cluster to verify that all the blocks will get parsed.
-  EXPECT_EQ(cluster->size(), parser.Parse(cluster->data(), cluster->size()));
+  EXPECT_EQ(cluster->bytes_used(),
+            parser.Parse(cluster->data(), cluster->bytes_used()));
   EXPECT_TRUE(parser.IsParsingComplete());
 }
 
diff --git a/net/http/transport_security_state_static.pins b/net/http/transport_security_state_static.pins
index d6336c5..749c069 100644
--- a/net/http/transport_security_state_static.pins
+++ b/net/http/transport_security_state_static.pins
@@ -43,9 +43,9 @@
 #   hash function for preloaded entries again (we have already done so once).
 #
 
-# Last updated: 2024-05-20 12:54 UTC
+# Last updated: 2024-05-21 12:56 UTC
 PinsListTimestamp
-1716209668
+1716296168
 
 TestSPKI
 sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
diff --git a/net/http/transport_security_state_static_pins.json b/net/http/transport_security_state_static_pins.json
index 36d452de..f3fe1e9 100644
--- a/net/http/transport_security_state_static_pins.json
+++ b/net/http/transport_security_state_static_pins.json
@@ -31,7 +31,7 @@
 // the 'static_spki_hashes' and 'bad_static_spki_hashes' fields in 'pinsets'
 // refer to, and the timestamp at which the pins list was last updated.
 //
-// Last updated: 2024-05-20 12:54 UTC
+// Last updated: 2024-05-21 12:56 UTC
 //
 {
   "pinsets": [
diff --git a/services/network/masked_domain_list/url_matcher_with_bypass.cc b/services/network/masked_domain_list/url_matcher_with_bypass.cc
index 2bad719..e528b48 100644
--- a/services/network/masked_domain_list/url_matcher_with_bypass.cc
+++ b/services/network/masked_domain_list/url_matcher_with_bypass.cc
@@ -112,7 +112,8 @@
   for (const auto& [partition_key, partitioned_domains] :
        PartitionDomains(domains)) {
     net::SchemeHostPortMatcher matcher;
-    for (auto domain : domains) {
+    for (auto domain : partitioned_domains) {
+      DCHECK(domain.ends_with(partition_key));
       AddRulesToMatcher(&matcher, domain, !HasSubdomainCoverage(domain));
     }
 
diff --git a/services/webnn/dml/graph_impl_dml.cc b/services/webnn/dml/graph_impl_dml.cc
index 83eaa3f..01c788d 100644
--- a/services/webnn/dml/graph_impl_dml.cc
+++ b/services/webnn/dml/graph_impl_dml.cc
@@ -169,7 +169,6 @@
 base::expected<void, mojom::ErrorPtr> CreateUnexpectedError(
     mojom::Error::Code error_code,
     const std::string& error_message) {
-  DLOG(ERROR) << error_message;
   return base::unexpected(CreateError(error_code, error_message));
 }
 
@@ -1913,8 +1912,6 @@
       // Spec issue tracked on
       // https://github.com/webmachinelearning/webnn/issues/180.
       if (dilations[0] != 1 || dilations[1] != 1) {
-        DLOG(ERROR)
-            << "Dilations are not supported for average pooling operator.";
         return base::unexpected(CreateError(
             mojom::Error::Code::kNotSupportedError,
             "Dilations are not supported for average pooling operator."));
@@ -4488,7 +4485,6 @@
 void HandleGraphCreationFailure(
     const std::string& error_message,
     mojom::WebNNContext::CreateGraphCallback callback) {
-  DLOG(ERROR) << error_message;
   std::move(callback).Run(CreateGraphResult::NewError(
       CreateError(mojom::Error::Code::kUnknownError, error_message)));
 }
@@ -4502,7 +4498,6 @@
     mojom::WebNNContext::CreateGraphCallback callback) {
   DLOG(ERROR) << error_message << " " << logging::SystemErrorCodeToString(hr);
   if (hr == E_OUTOFMEMORY) {
-    DLOG(ERROR) << "No enough memory resources are available.";
     std::move(callback).Run(CreateGraphResult::NewError(CreateError(
         mojom::Error::Code::kUnknownError,
         error_message + " No enough memory resources are available.")));
@@ -5499,7 +5494,6 @@
       }
       default: {
         std::string error_message = NotSupportedOperatorError(*operation);
-        DLOG(ERROR) << error_message;
         create_operator_result = base::unexpected(CreateError(
             mojom::Error::Code::kNotSupportedError, std::move(error_message)));
       }
@@ -5562,7 +5556,6 @@
 void GraphImplDml::HandleComputationFailure(
     const std::string& error_message,
     mojom::WebNNGraph::ComputeCallback callback) {
-  DLOG(ERROR) << error_message;
   compute_resources_.reset();
   std::move(callback).Run(ComputeResult::NewError(
       CreateError(mojom::Error::Code::kUnknownError, error_message)));
@@ -5575,7 +5568,6 @@
   DLOG(ERROR) << error_message << " " << logging::SystemErrorCodeToString(hr);
   compute_resources_.reset();
   if (hr == E_OUTOFMEMORY) {
-    DLOG(ERROR) << "No enough memory resources are available.";
     std::move(callback).Run(ComputeResult::NewError(CreateError(
         mojom::Error::Code::kUnknownError,
         error_message + " No enough memory resources are available.")));
diff --git a/services/webnn/dml/utils.cc b/services/webnn/dml/utils.cc
index ece35b3..d9d8233 100644
--- a/services/webnn/dml/utils.cc
+++ b/services/webnn/dml/utils.cc
@@ -252,6 +252,7 @@
 
 mojom::ErrorPtr CreateError(mojom::Error::Code error_code,
                             const std::string& error_message) {
+  LOG(ERROR) << "[WebNN] CreateError: " << error_message;
   return mojom::Error::New(error_code, kBackendName + error_message);
 }
 
diff --git a/services/webnn/webnn_context_provider_impl.cc b/services/webnn/webnn_context_provider_impl.cc
index b890e79..397d5242c 100644
--- a/services/webnn/webnn_context_provider_impl.cc
+++ b/services/webnn/webnn_context_provider_impl.cc
@@ -227,7 +227,6 @@
       std::move(callback).Run(mojom::CreateContextResult::NewError(
           dml::CreateError(mojom::Error::Code::kUnknownError,
                            "Failed to create a WebNN context.")));
-      LOG(ERROR) << "[WebNN] Failed to open the command recorder.";
       return;
     }
 
diff --git a/styleguide/java/java.md b/styleguide/java/java.md
index 4cbd026..d4ee486 100644
--- a/styleguide/java/java.md
+++ b/styleguide/java/java.md
@@ -345,13 +345,11 @@
 * Use real dependencies when feasible and fast. Use Mockito’s `@Mock` most
   of the time, but write fakes for frequently used dependencies.
 
-* Do not use Robolectric Shadows for Chromium code.
-  * Shadows make code harder to refactor.
-  * Prefer to refactor code to make it more testable.
-  * When you really need to use a test double for a static method, add a
-    `setFooForTesting() [...]` method to make the test contract explicit.
-    * Use [`ResettersForTesting.register()`] from within `ForTesting()`
-      methods to ensure that state is reset between tests.
+* Do not use Robolectric Shadows for Chromium code. Instead, use
+  `setForTesting()` methods so that it is clear that test hooks exist.
+  * When `setForTesting()` methods alter global state, use
+    [`ResettersForTesting.register()`] to ensure that the state is reset
+    between tests. Omit resetting them via `@After` methods.
 
 * Use Robolectric when possible (when tests do not require native). Other
   times, use on-device tests with one of the following annotations:
diff --git a/testing/android/junit/BUILD.gn b/testing/android/junit/BUILD.gn
index 1579acd..a0287b8 100644
--- a/testing/android/junit/BUILD.gn
+++ b/testing/android/junit/BUILD.gn
@@ -10,7 +10,6 @@
   annotation_processor_deps =
       [ "//third_party/android_deps:auto_service_processor" ]
   sources = [
-    "java/src/org/chromium/testing/local/Allowlist.java",
     "java/src/org/chromium/testing/local/ChromiumAndroidConfigurer.java",
     "java/src/org/chromium/testing/local/ConfigFilter.java",
     "java/src/org/chromium/testing/local/CustomShadowApplicationPackageManager.java",
@@ -41,6 +40,5 @@
     "javatests/src/org/chromium/testing/local/GtestLoggerTest.java",
     "javatests/src/org/chromium/testing/local/PackageFilterTest.java",
     "javatests/src/org/chromium/testing/local/RunnerFilterTest.java",
-    "javatests/src/org/chromium/testing/local/TestListComputerTest.java",
   ]
 }
diff --git a/testing/android/junit/java/src/org/chromium/testing/local/Allowlist.java b/testing/android/junit/java/src/org/chromium/testing/local/Allowlist.java
deleted file mode 100644
index efd8624..0000000
--- a/testing/android/junit/java/src/org/chromium/testing/local/Allowlist.java
+++ /dev/null
@@ -1,86 +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.
-
-package org.chromium.testing.local;
-
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * An prefix-matching allowlist implementation.
- *
- * <pre>
- * - Rules are defined in a file with one rule per-line.
- * - # Comments are allowed.
- * - Rules begin with either: "+" or "-" as a prefix (allow vs deny), and
- *   are followed by a prefix to match.
- * - Rules are evaluated in order.
- * - If no rules match, the default is to allow.
- * </pre>
- */
-class Allowlist {
-
-    private static class Rule {
-        final boolean mAllow;
-        final String mPrefix;
-
-        Rule(boolean allow, String prefix) {
-            mAllow = allow;
-            mPrefix = prefix;
-        }
-    }
-
-    private final String mFilename;
-    private final List<Rule> mRules;
-
-    private Allowlist(String filename, List<Rule> rules) {
-        mFilename = filename;
-        mRules = rules;
-    }
-
-    public static Allowlist allowAll() {
-        return new Allowlist("<Allow-all>", Collections.emptyList());
-    }
-
-    public static Allowlist fromLines(String filename, List<String> lines) {
-        ArrayList<Rule> rules = new ArrayList<>();
-        for (String line : lines) {
-            line = line.strip();
-            char firstChar = line.isEmpty() ? '#' : line.charAt(0);
-            if (firstChar == '#') {
-                continue;
-            }
-            if (firstChar != '+' && firstChar != '-') {
-                throw new RuntimeException("Expected line to start with + or -: " + line);
-            }
-            String prefix = line.substring(1);
-            if (prefix.isEmpty()) {
-                throw new RuntimeException("Found empty prefix: " + line);
-            }
-            rules.add(new Rule(firstChar == '+', prefix));
-        }
-        return new Allowlist(filename, rules);
-    }
-
-    public static Allowlist fromFile(Path path) throws IOException {
-        return fromLines(path.toString(), Files.readAllLines(path));
-    }
-
-    public String getFilename() {
-        return mFilename;
-    }
-
-    public boolean allow(String className) {
-        for (Rule r : mRules) {
-            if (className.startsWith(r.mPrefix)) {
-                return r.mAllow;
-            }
-        }
-        return true;
-    }
-}
diff --git a/testing/android/junit/java/src/org/chromium/testing/local/JunitTestArgParser.java b/testing/android/junit/java/src/org/chromium/testing/local/JunitTestArgParser.java
index 7715e28e..97b620e 100644
--- a/testing/android/junit/java/src/org/chromium/testing/local/JunitTestArgParser.java
+++ b/testing/android/junit/java/src/org/chromium/testing/local/JunitTestArgParser.java
@@ -4,8 +4,6 @@
 
 package org.chromium.testing.local;
 
-import java.io.IOException;
-import java.nio.file.Path;
 import java.util.HashSet;
 import java.util.Set;
 
@@ -14,7 +12,6 @@
     final Set<String> mPackageFilters = new HashSet<>();
     final Set<Class<?>> mRunnerFilters = new HashSet<>();
     final Set<String> mGtestFilters = new HashSet<>();
-    Allowlist mShadowsAllowlist = Allowlist.allowAll();
     boolean mListTests;
     String mJsonConfig;
     String mJsonOutput;
@@ -38,8 +35,6 @@
                         parsed.mJsonOutput = args[++i];
                     } else if ("json-config".equals(argName)) {
                         parsed.mJsonConfig = args[++i];
-                    } else if ("shadows-allowlist".equals(argName)) {
-                        parsed.mShadowsAllowlist = Allowlist.fromFile(Path.of(args[++i]));
                     } else {
                         System.out.println("Ignoring flag: \"" + argName + "\"");
                     }
@@ -49,9 +44,6 @@
                 } catch (ClassNotFoundException e) {
                     System.err.println("Class not found. (" + e + ")");
                     System.exit(1);
-                } catch (IOException e) {
-                    e.printStackTrace();
-                    System.exit(1);
                 }
             } else {
                 System.out.println("Ignoring argument: \"" + args[i] + "\"");
diff --git a/testing/android/junit/java/src/org/chromium/testing/local/JunitTestMain.java b/testing/android/junit/java/src/org/chromium/testing/local/JunitTestMain.java
index 52c01de..dec5423 100644
--- a/testing/android/junit/java/src/org/chromium/testing/local/JunitTestMain.java
+++ b/testing/android/junit/java/src/org/chromium/testing/local/JunitTestMain.java
@@ -100,7 +100,7 @@
     private static Result listTestMain(JunitTestArgParser parser)
             throws FileNotFoundException, JSONException {
         JUnitCore core = new JUnitCore();
-        TestListComputer computer = new TestListComputer(parser.mShadowsAllowlist);
+        TestListComputer computer = new TestListComputer();
         Class[] classes = findClassesFromClasspath();
         Request testRequest = Request.classes(computer, classes);
         for (String packageFilter : parser.mPackageFilters) {
diff --git a/testing/android/junit/java/src/org/chromium/testing/local/TestListComputer.java b/testing/android/junit/java/src/org/chromium/testing/local/TestListComputer.java
index 5bc96ec0..03bc8d6 100644
--- a/testing/android/junit/java/src/org/chromium/testing/local/TestListComputer.java
+++ b/testing/android/junit/java/src/org/chromium/testing/local/TestListComputer.java
@@ -34,13 +34,8 @@
 
 class TestListComputer extends Computer {
     private final List<Description> mDescriptions = new ArrayList<>();
-    private final Allowlist mShadowsAllowlist;
 
-    TestListComputer(Allowlist shadowsAllowlist) {
-        mShadowsAllowlist = shadowsAllowlist;
-    }
-
-    private String computeConfig(
+    private static String computeConfig(
             Description description,
             Set<String> instrumentedPackages,
             Set<String> instrumentedClasses) {
@@ -69,9 +64,6 @@
                 if (className.isEmpty()) {
                     className = annotation.value().getName();
                 }
-                if (!mShadowsAllowlist.allow(className)) {
-                    throwShadowException(clazz.getName(), className);
-                }
                 instrumentedClasses.add(className);
             }
         }
@@ -89,21 +81,6 @@
         return looperMode + sdkSuffix;
     }
 
-    private void throwShadowException(String shadowClass, String shadowingClass) {
-        String msg =
-                """
-
-            Found non-allowlisted Robolectric shadow: %s (shadowing %s).
-            Please limit usage of shadows to non-application code by adding explicit test stubbing \
-            logic via set*ForTesting() methods.
-
-            See: https://chromium.googlesource.com/chromium/src/+/main/styleguide/java/java.md#testing
-            Used allowlist: %s
-            """
-                        .formatted(shadowClass, shadowingClass, mShadowsAllowlist.getFilename());
-        throw new RuntimeException(msg);
-    }
-
     private class TestListRunner extends Runner implements Filterable {
         private final Runner mRunner;
 
@@ -170,12 +147,6 @@
     }
 
     public void writeJson(File outputFile) throws FileNotFoundException, JSONException {
-        try (PrintStream stream = new PrintStream(new FileOutputStream(outputFile))) {
-            stream.print(createJson());
-        }
-    }
-
-    JSONObject createJson() throws JSONException {
         var instrumentedPackages = new TreeSet<String>();
         var instrumentedClasses = new TreeSet<String>();
         JSONObject root = new JSONObject();
@@ -200,6 +171,9 @@
         for (String s : instrumentedClasses) {
             arr.put(s);
         }
-        return root;
+
+        try (PrintStream stream = new PrintStream(new FileOutputStream(outputFile))) {
+            stream.print(root);
+        }
     }
 }
diff --git a/testing/android/junit/javatests/src/org/chromium/testing/local/TestListComputerTest.java b/testing/android/junit/javatests/src/org/chromium/testing/local/TestListComputerTest.java
deleted file mode 100644
index 79dcfea..0000000
--- a/testing/android/junit/javatests/src/org/chromium/testing/local/TestListComputerTest.java
+++ /dev/null
@@ -1,60 +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.
-
-package org.chromium.testing.local;
-
-import org.json.JSONException;
-import org.junit.Assert;
-import org.junit.Test;
-import org.junit.runner.JUnitCore;
-import org.junit.runner.Request;
-import org.junit.runner.RunWith;
-import org.junit.runners.BlockJUnit4ClassRunner;
-import org.robolectric.annotation.Config;
-import org.robolectric.annotation.Implements;
-
-import java.util.Collections;
-import java.util.List;
-
-/** Unit tests for RunnerFilter. */
-@RunWith(BlockJUnit4ClassRunner.class)
-public class TestListComputerTest {
-
-    @Implements(TestListComputerTest.class)
-    public static class Shadow {}
-
-    @RunWith(BlockJUnit4ClassRunner.class)
-    @Config(shadows = {Shadow.class})
-    public static class FakeTestClass {
-        @Test
-        public void someTest() {}
-    }
-
-    private static void doTest(boolean allowClass) throws JSONException {
-        JUnitCore core = new JUnitCore();
-        TestListComputer computer =
-                new TestListComputer(
-                        Allowlist.fromLines(
-                                "mockfile",
-                                allowClass ? Collections.emptyList() : List.of("-org")));
-        Class[] classes = {FakeTestClass.class};
-        core.run(Request.classes(computer, classes));
-        String expected =
-                """
-            {"configs":{"PAUSED":{"org.chromium.testing.local.TestListComputerTest$FakeTestClass":\
-            ["someTest"]}},"instrumentedPackages":[],"instrumentedClasses":\
-            ["org.chromium.testing.local.TestListComputerTest"]}""";
-        Assert.assertEquals(expected, computer.createJson().toString());
-    }
-
-    @Test
-    public void testAllowed() throws Exception {
-        doTest(true);
-    }
-
-    @Test(expected = RuntimeException.class)
-    public void testNotAllowed() throws Exception {
-        doTest(false);
-    }
-}
diff --git a/testing/android/junit/shadows-allowlist.txt b/testing/android/junit/shadows-allowlist.txt
deleted file mode 100644
index 390eef4..0000000
--- a/testing/android/junit/shadows-allowlist.txt
+++ /dev/null
@@ -1,91 +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.
-#
-# Allowlist of classes that can be shadowed (by @Shadow).
-# Please do not add to this list. See:
-# https://chromium.googlesource.com/chromium/src/+/main/styleguide/java/java.md#testing
-
-# TODO(https://crbug.com/341267427): Reduce this list.
-+com.google.android.apps.chrome.rlz.RevenueStatsInternal
-+org.chromium.base.ApplicationStatus
-+org.chromium.base.PathUtils
-+org.chromium.base.SysUtils
-+org.chromium.base.task.AsyncTask
-+org.chromium.base.task.PostTask
-+org.chromium.base.version_info.VersionInfo
-+org.chromium.chrome.browser.content.WebContentsFactory
-+org.chromium.chrome.browser.contextmenu.ContextMenuHeaderCoordinator
-+org.chromium.chrome.browser.contextualsearch.ContextualSearchSelectionController
-+org.chromium.chrome.browser.device.DeviceConditions
-+org.chromium.chrome.browser.dragdrop.ChromeDragAndDropBrowserDelegate$ClipDataItemBuilder
-+org.chromium.chrome.browser.externalnav.ExternalNavigationDelegateImpl
-+org.chromium.chrome.browser.firstrun.FirstRunUtils
-+org.chromium.chrome.browser.homepage.HomepagePolicyManager
-+org.chromium.chrome.browser.incognito.IncognitoUtils
-+org.chromium.chrome.browser.init.ChromeBrowserInitializer
-+org.chromium.chrome.browser.lens.LensController
-+org.chromium.chrome.browser.lens.LensPolicyUtils
-+org.chromium.chrome.browser.metrics.UmaSessionStats
-+org.chromium.chrome.browser.multiwindow.MultiInstanceManagerApi31
-+org.chromium.chrome.browser.night_mode.WebContentsDarkModeController
-+org.chromium.chrome.browser.omnibox.geo.GeolocationHeader
-+org.chromium.chrome.browser.omnibox.geo.GeolocationTracker
-+org.chromium.chrome.browser.omnibox.geo.PlatformNetworksManager
-+org.chromium.chrome.browser.omnibox.geo.VisibleNetworksTracker
-+org.chromium.chrome.browser.omnibox.styles.OmniboxResourceProvider
-+org.chromium.chrome.browser.omnibox.suggestions.CachedZeroSuggestionsManager
-+org.chromium.chrome.browser.omnibox.UrlBarData
-+org.chromium.chrome.browser.omnibox.voice.VoiceRecognitionUtil
-+org.chromium.chrome.browser.partnercustomizations.CustomizationProviderDelegateUpstreamImpl
-+org.chromium.chrome.browser.partnercustomizations.PartnerBrowserCustomizations
-+org.chromium.chrome.browser.profiles.Profile
-+org.chromium.chrome.browser.profiles.ProfileManager
-+org.chromium.chrome.browser.profiles.ProfileManagerUtils
-+org.chromium.chrome.browser.rlz.RevenueStats
-+org.chromium.chrome.browser.search_engines.TemplateUrlServiceFactory
-+org.chromium.chrome.browser.searchwidget.SearchActivityUtils
-+org.chromium.chrome.browser.share.android_share_sheet.AndroidShareSheetController
-+org.chromium.chrome.browser.share.link_to_text.LinkToTextCoordinator
-+org.chromium.chrome.browser.share.long_screenshots.LongScreenshotsCoordinator
-+org.chromium.chrome.browser.share.qrcode.QrCodeDialog
-+org.chromium.chrome.browser.share.ShareHelper
-+org.chromium.chrome.browser.share.ShareHelper$ChooserActionHelper
-+org.chromium.chrome.browser.share.share_sheet.ShareSheetCoordinator
-+org.chromium.chrome.browser.share.share_sheet.ShareSheetPropertyModelBuilder
-+org.chromium.chrome.browser.tab.SadTab
-+org.chromium.chrome.browser.tab.TabBrowserControlsOffsetHelper
-+org.chromium.chrome.browser.tab.TabBuilder
-+org.chromium.chrome.browser.tab.TabUtils
-+org.chromium.chrome.browser.tab.TrustedCdn
-+org.chromium.chrome.browser.toolbar.adaptive.OptionalNewTabButtonController$Delegate
-+org.chromium.chrome.browser.ui.edge_to_edge.EdgeToEdgeControllerFactory
-+org.chromium.chrome.browser.ui.hats.MessageSurveyUiDelegate
-+org.chromium.chrome.browser.util.AndroidTaskUtils
-+org.chromium.chrome.browser.webapps.WebApkShareTargetUtil
-+org.chromium.components.browser_ui.bottomsheet.BottomSheetControllerProvider
-+org.chromium.components.browser_ui.share.ShareImageFileUtils
-+org.chromium.components.browser_ui.styles.SemanticColorUtils
-+org.chromium.components.browser_ui.widget.ContextMenuDialog
-+org.chromium.components.browser_ui.widget.InsetsRectProvider$BoundingRectHelper
-+org.chromium.components.embedder_support.util.UrlUtilities
-+org.chromium.components.media_router.caf.CastMediaSource
-+org.chromium.components.stylus_handwriting.DirectWritingSettingsHelper
-+org.chromium.components.url_formatter.UrlFormatter
-+org.chromium.components.user_prefs.UserPrefs
-+org.chromium.content_public.browser.WebContentsStatics
-+org.chromium.gms.ChromiumPlayServicesAvailability
-+org.chromium.ui.base.MimeTypeUtils
-+org.chromium.ui.display.DisplayAndroid
-+org.chromium.ui.display.DisplayAndroidManager
-+org.chromium.ui.display.DisplayUtil
-+org.chromium.ui.resources.dynamics.CaptureUtils
-+org.chromium.ui.util.ColorUtils
-+org.chromium.ui.util.WindowInsetsUtils
-+org.chromium.ui.widget.AnchoredPopupWindow
-+org.chromium.url.Origin
-+org.chromium.webapk.shell_apk.LaunchHostBrowserSelector
-
-# Disallow new shadows of chrome-authored code.
--com.google.android.apps.chrome
--org.chromium
diff --git a/testing/buildbot/chromium.chromiumos.json b/testing/buildbot/chromium.chromiumos.json
index f890096e..febd34e 100644
--- a/testing/buildbot/chromium.chromiumos.json
+++ b/testing/buildbot/chromium.chromiumos.json
@@ -5487,9 +5487,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v127.0.6492.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v127.0.6493.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 127.0.6492.0",
+        "description": "Run with ash-chrome version 127.0.6493.0",
         "isolate_profile_data": true,
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -5499,8 +5499,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v127.0.6492.0",
-              "revision": "version:127.0.6492.0"
+              "location": "lacros_version_skew_tests_v127.0.6493.0",
+              "revision": "version:127.0.6493.0"
             }
           ],
           "dimensions": {
@@ -5643,9 +5643,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.filter;../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v127.0.6492.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v127.0.6493.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 127.0.6492.0",
+        "description": "Run with ash-chrome version 127.0.6493.0",
         "isolate_profile_data": true,
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -5655,8 +5655,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v127.0.6492.0",
-              "revision": "version:127.0.6492.0"
+              "location": "lacros_version_skew_tests_v127.0.6493.0",
+              "revision": "version:127.0.6493.0"
             }
           ],
           "dimensions": {
diff --git a/testing/buildbot/chromium.coverage.json b/testing/buildbot/chromium.coverage.json
index b233cf8..d8cb011 100644
--- a/testing/buildbot/chromium.coverage.json
+++ b/testing/buildbot/chromium.coverage.json
@@ -19664,9 +19664,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v127.0.6492.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v127.0.6493.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 127.0.6492.0",
+        "description": "Run with ash-chrome version 127.0.6493.0",
         "isolate_profile_data": true,
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -19676,8 +19676,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v127.0.6492.0",
-              "revision": "version:127.0.6492.0"
+              "location": "lacros_version_skew_tests_v127.0.6493.0",
+              "revision": "version:127.0.6493.0"
             }
           ],
           "dimensions": {
@@ -19820,9 +19820,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.filter;../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v127.0.6492.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v127.0.6493.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 127.0.6492.0",
+        "description": "Run with ash-chrome version 127.0.6493.0",
         "isolate_profile_data": true,
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -19832,8 +19832,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v127.0.6492.0",
-              "revision": "version:127.0.6492.0"
+              "location": "lacros_version_skew_tests_v127.0.6493.0",
+              "revision": "version:127.0.6493.0"
             }
           ],
           "dimensions": {
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index dc1e152f..ab8192a 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -41840,9 +41840,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v127.0.6492.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v127.0.6493.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 127.0.6492.0",
+        "description": "Run with ash-chrome version 127.0.6493.0",
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
@@ -41851,8 +41851,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v127.0.6492.0",
-              "revision": "version:127.0.6492.0"
+              "location": "lacros_version_skew_tests_v127.0.6493.0",
+              "revision": "version:127.0.6493.0"
             }
           ],
           "dimensions": {
@@ -41990,9 +41990,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.filter;../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v127.0.6492.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v127.0.6493.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 127.0.6492.0",
+        "description": "Run with ash-chrome version 127.0.6493.0",
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
@@ -42001,8 +42001,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v127.0.6492.0",
-              "revision": "version:127.0.6492.0"
+              "location": "lacros_version_skew_tests_v127.0.6493.0",
+              "revision": "version:127.0.6493.0"
             }
           ],
           "dimensions": {
@@ -43339,9 +43339,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v127.0.6492.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v127.0.6493.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 127.0.6492.0",
+        "description": "Run with ash-chrome version 127.0.6493.0",
         "isolate_profile_data": true,
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -43351,8 +43351,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v127.0.6492.0",
-              "revision": "version:127.0.6492.0"
+              "location": "lacros_version_skew_tests_v127.0.6493.0",
+              "revision": "version:127.0.6493.0"
             }
           ],
           "dimensions": {
@@ -43495,9 +43495,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.filter;../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v127.0.6492.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v127.0.6493.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 127.0.6492.0",
+        "description": "Run with ash-chrome version 127.0.6493.0",
         "isolate_profile_data": true,
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -43507,8 +43507,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v127.0.6492.0",
-              "revision": "version:127.0.6492.0"
+              "location": "lacros_version_skew_tests_v127.0.6493.0",
+              "revision": "version:127.0.6493.0"
             }
           ],
           "dimensions": {
@@ -44820,9 +44820,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v127.0.6492.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v127.0.6493.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 127.0.6492.0",
+        "description": "Run with ash-chrome version 127.0.6493.0",
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
@@ -44831,8 +44831,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v127.0.6492.0",
-              "revision": "version:127.0.6492.0"
+              "location": "lacros_version_skew_tests_v127.0.6493.0",
+              "revision": "version:127.0.6493.0"
             }
           ],
           "dimensions": {
@@ -44970,9 +44970,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.filter;../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v127.0.6492.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v127.0.6493.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 127.0.6492.0",
+        "description": "Run with ash-chrome version 127.0.6493.0",
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
@@ -44981,8 +44981,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v127.0.6492.0",
-              "revision": "version:127.0.6492.0"
+              "location": "lacros_version_skew_tests_v127.0.6493.0",
+              "revision": "version:127.0.6493.0"
             }
           ],
           "dimensions": {
diff --git a/testing/buildbot/chromium.memory.json b/testing/buildbot/chromium.memory.json
index 6b627aa..4dd268f 100644
--- a/testing/buildbot/chromium.memory.json
+++ b/testing/buildbot/chromium.memory.json
@@ -15763,12 +15763,12 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v127.0.6492.0/test_ash_chrome",
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v127.0.6493.0/test_ash_chrome",
           "--test-launcher-print-test-stdio=always",
           "--combine-ash-logs-on-bots",
           "--asan-symbolize-output"
         ],
-        "description": "Run with ash-chrome version 127.0.6492.0",
+        "description": "Run with ash-chrome version 127.0.6493.0",
         "isolate_profile_data": true,
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -15778,8 +15778,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v127.0.6492.0",
-              "revision": "version:127.0.6492.0"
+              "location": "lacros_version_skew_tests_v127.0.6493.0",
+              "revision": "version:127.0.6493.0"
             }
           ],
           "dimensions": {
@@ -15939,12 +15939,12 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.filter;../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v127.0.6492.0/test_ash_chrome",
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v127.0.6493.0/test_ash_chrome",
           "--test-launcher-print-test-stdio=always",
           "--combine-ash-logs-on-bots",
           "--asan-symbolize-output"
         ],
-        "description": "Run with ash-chrome version 127.0.6492.0",
+        "description": "Run with ash-chrome version 127.0.6493.0",
         "isolate_profile_data": true,
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -15954,8 +15954,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v127.0.6492.0",
-              "revision": "version:127.0.6492.0"
+              "location": "lacros_version_skew_tests_v127.0.6493.0",
+              "revision": "version:127.0.6493.0"
             }
           ],
           "dimensions": {
diff --git a/testing/buildbot/variants.pyl b/testing/buildbot/variants.pyl
index d11961e..12c7f96 100644
--- a/testing/buildbot/variants.pyl
+++ b/testing/buildbot/variants.pyl
@@ -267,16 +267,16 @@
   },
   'LACROS_VERSION_SKEW_CANARY': {
     'identifier': 'Lacros version skew testing ash canary',
-    'description': 'Run with ash-chrome version 127.0.6492.0',
+    'description': 'Run with ash-chrome version 127.0.6493.0',
     'args': [
-      '--ash-chrome-path-override=../../lacros_version_skew_tests_v127.0.6492.0/test_ash_chrome',
+      '--ash-chrome-path-override=../../lacros_version_skew_tests_v127.0.6493.0/test_ash_chrome',
     ],
     'swarming': {
       'cipd_packages': [
         {
           'cipd_package': 'chromium/testing/linux-ash-chromium/x86_64/ash.zip',
-          'location': 'lacros_version_skew_tests_v127.0.6492.0',
-          'revision': 'version:127.0.6492.0',
+          'location': 'lacros_version_skew_tests_v127.0.6493.0',
+          'revision': 'version:127.0.6493.0',
         },
       ],
     },
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 5d3617e..3d54f35 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -12887,21 +12887,6 @@
             ]
         }
     ],
-    "NotificationGesturesUpdate": [
-        {
-            "platforms": [
-                "chromeos"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "NotificationGesturesUpdate"
-                    ]
-                }
-            ]
-        }
-    ],
     "NotificationOneTapUnsubscribe": [
         {
             "platforms": [
diff --git a/third_party/angle b/third_party/angle
index bfc643a..b257da8 160000
--- a/third_party/angle
+++ b/third_party/angle
@@ -1 +1 @@
-Subproject commit bfc643a4c33fc83531d5226c2491a7f3da1760f1
+Subproject commit b257da865ba588df53b6151299174bbc00a8082f
diff --git a/third_party/blink/public/mojom/permissions_policy/document_policy_feature.mojom b/third_party/blink/public/mojom/permissions_policy/document_policy_feature.mojom
index 82a16dc..70fe9a0 100644
--- a/third_party/blink/public/mojom/permissions_policy/document_policy_feature.mojom
+++ b/third_party/blink/public/mojom/permissions_policy/document_policy_feature.mojom
@@ -8,7 +8,7 @@
 enum DocumentPolicyFeature {
   kDefault = 0,
   // Controls access to font-display attribute in @font-face CSS rule
-  kFontDisplay = 1,
+  // kFontDisplay = 1, // Removed.
   // Takes a parameter, |bpp|, i.e. byte-per-pixel ratio, that images
   // needs to obey.
   // kLosslessImagesMaxBpp = 2, // Removed.
diff --git a/third_party/blink/renderer/bindings/core/v8/v8_initializer.cc b/third_party/blink/renderer/bindings/core/v8/v8_initializer.cc
index b4af8b6..a445d41 100644
--- a/third_party/blink/renderer/bindings/core/v8/v8_initializer.cc
+++ b/third_party/blink/renderer/bindings/core/v8/v8_initializer.cc
@@ -263,20 +263,6 @@
   ExecutionContext* context = ExecutionContext::From(script_state);
 
   v8::Local<v8::Value> exception = data.GetValue();
-  if (V8PerIsolateData::From(isolate)->HasInstance(
-          DOMException::GetStaticWrapperTypeInfo(), exception)) {
-    // Try to get the stack & location from a wrapped DOMException object.
-    CHECK(exception->IsObject());
-    auto private_error = V8PrivateProperty::GetSymbol(
-        isolate, kPrivatePropertyDOMExceptionError);
-    v8::Local<v8::Value> error;
-    if (private_error.GetOrUndefined(exception.As<v8::Object>())
-            .ToLocal(&error) &&
-        !error->IsUndefined()) {
-      exception = error;
-    }
-  }
-
   String error_message;
   SanitizeScriptErrors sanitize_script_errors = SanitizeScriptErrors::kSanitize;
   std::unique_ptr<SourceLocation> location;
diff --git a/third_party/blink/renderer/bindings/core/v8/v8_throw_dom_exception.cc b/third_party/blink/renderer/bindings/core/v8/v8_throw_dom_exception.cc
index 5ae18ae6..17ed149 100644
--- a/third_party/blink/renderer/bindings/core/v8/v8_throw_dom_exception.cc
+++ b/third_party/blink/renderer/bindings/core/v8/v8_throw_dom_exception.cc
@@ -13,39 +13,12 @@
 
 namespace blink {
 
-// extern
-const V8PrivateProperty::SymbolKey kPrivatePropertyDOMExceptionError;
-
 // static
 void V8ThrowDOMException::Init() {
   ExceptionState::SetCreateDOMExceptionFunction(
       V8ThrowDOMException::CreateOrEmpty);
 }
 
-namespace {
-
-void DomExceptionStackGetter(v8::Local<v8::Name> name,
-                             const v8::PropertyCallbackInfo<v8::Value>& info) {
-  v8::Isolate* isolate = info.GetIsolate();
-  v8::Local<v8::Value> value;
-  if (info.Data()
-          .As<v8::Object>()
-          ->Get(isolate->GetCurrentContext(), V8AtomicString(isolate, "stack"))
-          .ToLocal(&value)) {
-    bindings::V8SetReturnValue(info, value);
-  }
-}
-
-void DomExceptionStackSetter(v8::Local<v8::Name> name,
-                             v8::Local<v8::Value> value,
-                             const v8::PropertyCallbackInfo<void>& info) {
-  [[maybe_unused]] v8::Maybe<bool> unused = info.Data().As<v8::Object>()->Set(
-      info.GetIsolate()->GetCurrentContext(),
-      V8AtomicString(info.GetIsolate(), "stack"), value);
-}
-
-}  // namespace
-
 v8::Local<v8::Value> V8ThrowDOMException::CreateOrEmpty(
     v8::Isolate* isolate,
     DOMExceptionCode exception_code,
@@ -80,32 +53,13 @@
   if (isolate->IsExecutionTerminating())
     return v8::Local<v8::Value>();
 
-  auto current_context = isolate->GetCurrentContext();
-
   // We use the isolate's current context here because we are creating an
   // exception object.
+  auto current_context = isolate->GetCurrentContext();
   v8::Local<v8::Object> exception_obj =
-      ToV8Traits<DOMException>::ToV8(
-          ScriptState::From(isolate, current_context), dom_exception)
+      dom_exception->ToV8(ScriptState::From(isolate, current_context))
           .As<v8::Object>();
-
-  // Attach an Error object to the DOMException. This is then lazily used to get
-  // the stack value.
-  v8::Local<v8::Value> error =
-      v8::Exception::Error(V8String(isolate, dom_exception->message()));
-
-  // The context passed to SetNativeDataProperty is used to create an error
-  // object if needed, and so should be the isolate's current context.
-  exception_obj
-      ->SetNativeDataProperty(current_context, V8AtomicString(isolate, "stack"),
-                              DomExceptionStackGetter, DomExceptionStackSetter,
-                              error)
-      .ToChecked();
-
-  auto private_error =
-      V8PrivateProperty::GetSymbol(isolate, kPrivatePropertyDOMExceptionError);
-  private_error.Set(exception_obj, error);
-
+  v8::Exception::CaptureStackTrace(current_context, exception_obj);
   return exception_obj;
 }
 
diff --git a/third_party/blink/renderer/bindings/core/v8/v8_throw_dom_exception.h b/third_party/blink/renderer/bindings/core/v8/v8_throw_dom_exception.h
index 78e90b25..c2c320223 100644
--- a/third_party/blink/renderer/bindings/core/v8/v8_throw_dom_exception.h
+++ b/third_party/blink/renderer/bindings/core/v8/v8_throw_dom_exception.h
@@ -50,8 +50,6 @@
   static v8::Local<v8::Value> AttachStackProperty(v8::Isolate*, DOMException*);
 };
 
-extern const V8PrivateProperty::SymbolKey kPrivatePropertyDOMExceptionError;
-
 }  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_V8_THROW_DOM_EXCEPTION_H_
diff --git a/third_party/blink/renderer/build/scripts/core/css/templates/css_property_names.h.tmpl b/third_party/blink/renderer/build/scripts/core/css/templates/css_property_names.h.tmpl
index e043a30..5ded9c3 100644
--- a/third_party/blink/renderer/build/scripts/core/css/templates/css_property_names.h.tmpl
+++ b/third_party/blink/renderer/build/scripts/core/css/templates/css_property_names.h.tmpl
@@ -72,7 +72,7 @@
 static_assert(CSSPropertyID::kColorScheme == kFirstHighPriorityCSSProperty);
 static_assert(CSSPropertyID::kZoom == kLastHighPriorityCSSProperty);
 static_assert((static_cast<int>(kLastHighPriorityCSSProperty) -
-               static_cast<int>(kFirstHighPriorityCSSProperty)) == 40,
+               static_cast<int>(kFirstHighPriorityCSSProperty)) == 41,
               "There should a low number of high-priority properties");
 
 inline int GetCSSPropertyIDIndex(CSSPropertyID id) {
diff --git a/third_party/blink/renderer/core/css/css_properties.json5 b/third_party/blink/renderer/core/css/css_properties.json5
index 1de587b..b3a5393 100644
--- a/third_party/blink/renderer/core/css/css_properties.json5
+++ b/third_party/blink/renderer/core/css/css_properties.json5
@@ -5660,12 +5660,20 @@
       field_group: "*",
       field_template: "external",
       include_paths: ["third_party/blink/renderer/core/style/text_size_adjust.h"],
+      style_builder_custom_functions: ["initial", "inherit", "value"],
       default_value: "TextSizeAdjust::AdjustAuto()",
       getter: "GetTextSizeAdjust",
+      // When `NewTextSizeAdjust` is enabled, this affects font-size during
+      // style building and needs to apply before it.
+      priority: 2,
       type_name: "TextSizeAdjust",
       converter: "ConvertTextSizeAdjust",
       keywords: ["none", "auto"],
       typedom_types: ["Keyword", "Percentage"],
+      // When `NewTextSizeAdjust` is enabled, this affects font-size during
+      // style building. Changes to anything related to fonts require that we
+      // call style.UpdateFont(), which the incremental path does not do.
+      supports_incremental_style: false,
       invalidate: ["layout", "paint"],
     },
     {
diff --git a/third_party/blink/renderer/core/css/cssom/css_numeric_value.cc b/third_party/blink/renderer/core/css/cssom/css_numeric_value.cc
index ace4d04..74404ff 100644
--- a/third_party/blink/renderer/core/css/cssom/css_numeric_value.cc
+++ b/third_party/blink/renderer/core/css/cssom/css_numeric_value.cc
@@ -337,10 +337,21 @@
 CSSNumericValue* CSSNumericValue::FromCSSValue(const CSSPrimitiveValue& value) {
   if (value.IsCalculated()) {
     const auto& math_function = To<CSSMathFunctionValue>(value);
-    if (math_function.HasAnchorFunctions()) {
+    // We don't currently have a spec or implementation for a typed OM
+    // representation of anchor functions or sizing keywords (in calc-size()).
+    // So we should not attempt to produce such a representation.  Do this
+    // exactly for anchor functions, but handle sizing keywords by rejecting
+    // any calc-size() function (even if it doesn't have sizing keywords),
+    // since the use of sizing keywords is the main use of such functions.
+    auto is_calc_size = [](const CSSMathExpressionNode* expression) {
+      const auto* operation = DynamicTo<CSSMathExpressionOperation>(expression);
+      return operation && operation->IsCalcSize();
+    };
+    const CSSMathExpressionNode* expression = math_function.ExpressionNode();
+    if (math_function.HasAnchorFunctions() || is_calc_size(expression)) {
       return nullptr;
     }
-    return CalcToNumericValue(*math_function.ExpressionNode());
+    return CalcToNumericValue(*expression);
   }
   return CSSUnitValue::FromCSSValue(To<CSSNumericLiteralValue>(value));
 }
diff --git a/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc b/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc
index 8b0089b..d51bfc5 100644
--- a/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc
+++ b/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc
@@ -10962,6 +10962,21 @@
       To<CSSIdentifierValue>(value).ConvertTo<blink::WritingMode>());
 }
 
+void TextSizeAdjust::ApplyInitial(StyleResolverState& state) const {
+  state.SetTextSizeAdjust(ComputedStyleInitialValues::InitialTextSizeAdjust());
+}
+
+void TextSizeAdjust::ApplyInherit(StyleResolverState& state) const {
+  state.SetTextSizeAdjust(state.ParentStyle()->GetTextSizeAdjust());
+}
+
+void TextSizeAdjust::ApplyValue(StyleResolverState& state,
+                                const CSSValue& value,
+                                ValueMode) const {
+  state.SetTextSizeAdjust(
+      StyleBuilderConverter::ConvertTextSizeAdjust(state, value));
+}
+
 const CSSValue* X::ParseSingleValue(CSSParserTokenStream& stream,
                                     const CSSParserContext& context,
                                     const CSSParserLocalContext&) const {
diff --git a/third_party/blink/renderer/core/css/remote_font_face_source.cc b/third_party/blink/renderer/core/css/remote_font_face_source.cc
index b933ea2f..1367119 100644
--- a/third_party/blink/renderer/core/css/remote_font_face_source.cc
+++ b/third_party/blink/renderer/core/css/remote_font_face_source.cc
@@ -7,7 +7,6 @@
 #include "base/metrics/histogram_functions.h"
 #include "base/task/single_thread_task_runner.h"
 #include "third_party/blink/public/common/features.h"
-#include "third_party/blink/public/mojom/permissions_policy/permissions_policy_feature.mojom-blink.h"
 #include "third_party/blink/public/platform/task_type.h"
 #include "third_party/blink/public/platform/web_effective_connection_type.h"
 #include "third_party/blink/renderer/core/css/css_custom_font_data.h"
@@ -151,11 +150,7 @@
     scoped_refptr<base::SingleThreadTaskRunner> task_runner)
     : face_(css_font_face),
       font_selector_(font_selector),
-      // No need to report the violation here since the font is not loaded yet
-      display_(
-          GetFontDisplayWithDocumentPolicyCheck(display,
-                                                font_selector,
-                                                ReportOptions::kDoNotReport)),
+      display_(display),
       phase_(kNoLimitExceeded),
       is_intervention_triggered_(ShouldTriggerWebFontsIntervention()),
       finished_before_document_rendering_begin_(false),
@@ -292,8 +287,7 @@
   if (IsLoaded()) {
     return;
   }
-  display_ = GetFontDisplayWithDocumentPolicyCheck(
-      display, font_selector_, ReportOptions::kReportOnFailure);
+  display_ = display;
   UpdatePeriod();
 }
 
@@ -316,20 +310,6 @@
   return changed;
 }
 
-FontDisplay RemoteFontFaceSource::GetFontDisplayWithDocumentPolicyCheck(
-    FontDisplay display,
-    const FontSelector* font_selector,
-    ReportOptions report_option) const {
-  ExecutionContext* context = font_selector->GetExecutionContext();
-  if (display != FontDisplay::kFallback && display != FontDisplay::kOptional &&
-      context && context->IsWindow() &&
-      !context->IsFeatureEnabled(
-          mojom::blink::DocumentPolicyFeature::kFontDisplay, report_option)) {
-    return FontDisplay::kOptional;
-  }
-  return display;
-}
-
 bool RemoteFontFaceSource::ShouldTriggerWebFontsIntervention() {
   if (!IsA<LocalDOMWindow>(font_selector_->GetExecutionContext())) {
     return false;
diff --git a/third_party/blink/renderer/core/css/remote_font_face_source.h b/third_party/blink/renderer/core/css/remote_font_face_source.h
index 75ce3c5..5e8b6b7 100644
--- a/third_party/blink/renderer/core/css/remote_font_face_source.h
+++ b/third_party/blink/renderer/core/css/remote_font_face_source.h
@@ -148,9 +148,6 @@
   bool UpdatePeriod() override;
   bool ShouldTriggerWebFontsIntervention();
   bool IsLowPriorityLoadingAllowedForRemoteFont() const override;
-  FontDisplay GetFontDisplayWithDocumentPolicyCheck(FontDisplay,
-                                                    const FontSelector*,
-                                                    ReportOptions) const;
 
   // Our owning font face.
   Member<CSSFontFace> face_;
diff --git a/third_party/blink/renderer/core/css/resolver/font_builder.cc b/third_party/blink/renderer/core/css/resolver/font_builder.cc
index 8bf0b40..c0b80a0 100644
--- a/third_party/blink/renderer/core/css/resolver/font_builder.cc
+++ b/third_party/blink/renderer/core/css/resolver/font_builder.cc
@@ -53,6 +53,14 @@
   Set(PropertySetFlag::kWritingMode);
 }
 
+void FontBuilder::DidChangeTextSizeAdjust() {
+  // When `NewTextSizeAdjust` is enabled, text-size-adjust affects font-size
+  // during style building, and needs to invalidate the font description.
+  if (RuntimeEnabledFeatures::NewTextSizeAdjustEnabled()) {
+    Set(PropertySetFlag::kTextSizeAdjust);
+  }
+}
+
 FontFamily FontBuilder::StandardFontFamily() const {
   const AtomicString& standard_font_family = StandardFontFamilyName();
   return FontFamily(standard_font_family,
@@ -287,17 +295,26 @@
 }
 
 float FontBuilder::GetComputedSizeFromSpecifiedSize(
-    FontDescription& font_description,
-    float effective_zoom,
+    const FontDescription& font_description,
+    const ComputedStyleBuilder& builder,
     float specified_size) {
   DCHECK(document_);
-  float zoom_factor = effective_zoom;
+  float zoom_factor = builder.EffectiveZoom();
   // Apply the text zoom factor preference. The preference is exposed in
   // accessibility settings in Chrome for Android to improve readability.
   if (LocalFrame* frame = document_->GetFrame()) {
     zoom_factor *= frame->TextZoomFactor();
   }
 
+  if (!builder.GetTextSizeAdjust().IsAuto()) {
+    if (RuntimeEnabledFeatures::NewTextSizeAdjustEnabled()) {
+      Settings* settings = document_->GetSettings();
+      if (settings && settings->GetTextAutosizingEnabled()) {
+        zoom_factor *= builder.GetTextSizeAdjust().Multiplier();
+      }
+    }
+  }
+
   return FontSizeFunctions::GetComputedSizeFromSpecifiedSize(
       document_, zoom_factor, font_description.IsAbsoluteSize(),
       specified_size);
@@ -397,8 +414,7 @@
 void FontBuilder::UpdateComputedSize(FontDescription& font_description,
                                      const ComputedStyleBuilder& builder) {
   float computed_size = GetComputedSizeFromSpecifiedSize(
-      font_description, builder.EffectiveZoom(),
-      font_description.SpecifiedSize());
+      font_description, builder, font_description.SpecifiedSize());
   computed_size = TextAutosizer::ComputeAutosizedFontSize(
       computed_size, builder.TextAutosizingMultiplier(),
       builder.EffectiveZoom());
@@ -585,7 +601,8 @@
       description.SetVariantEmoji(font_description_.VariantEmoji());
     }
   }
-  if (!modified && !IsSet(PropertySetFlag::kEffectiveZoom)) {
+  if (!modified && !IsSet(PropertySetFlag::kEffectiveZoom) &&
+      !IsSet(PropertySetFlag::kTextSizeAdjust)) {
     return false;
   }
 
diff --git a/third_party/blink/renderer/core/css/resolver/font_builder.h b/third_party/blink/renderer/core/css/resolver/font_builder.h
index eeb0063..0a6407bd 100644
--- a/third_party/blink/renderer/core/css/resolver/font_builder.h
+++ b/third_party/blink/renderer/core/css/resolver/font_builder.h
@@ -53,6 +53,7 @@
   void DidChangeEffectiveZoom();
   void DidChangeTextOrientation();
   void DidChangeWritingMode();
+  void DidChangeTextSizeAdjust();
 
   FontFamily StandardFontFamily() const;
   AtomicString StandardFontFamilyName() const;
@@ -176,8 +177,8 @@
   void UpdateComputedSize(FontDescription&, const ComputedStyleBuilder&);
   void UpdateAdjustedSize(FontDescription&, FontSelector*);
 
-  float GetComputedSizeFromSpecifiedSize(FontDescription&,
-                                         float effective_zoom,
+  float GetComputedSizeFromSpecifiedSize(const FontDescription&,
+                                         const ComputedStyleBuilder&,
                                          float specified_size);
 
   FontSelector* FontSelectorFromTreeScope(const TreeScope* tree_scope);
@@ -218,6 +219,8 @@
     kTextOrientation,
     kWritingMode,
 
+    kTextSizeAdjust,
+
     kNumFlags,
   };
 
diff --git a/third_party/blink/renderer/core/css/resolver/style_builder_converter.cc b/third_party/blink/renderer/core/css/resolver/style_builder_converter.cc
index f319b76..1ed2855 100644
--- a/third_party/blink/renderer/core/css/resolver/style_builder_converter.cc
+++ b/third_party/blink/renderer/core/css/resolver/style_builder_converter.cc
@@ -72,6 +72,7 @@
 #include "third_party/blink/renderer/core/css/style_engine.h"
 #include "third_party/blink/renderer/core/css_value_keywords.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
+#include "third_party/blink/renderer/core/frame/settings.h"
 #include "third_party/blink/renderer/core/frame/web_feature.h"
 #include "third_party/blink/renderer/core/style/computed_style_constants.h"
 #include "third_party/blink/renderer/core/style/coord_box_offset_path_operation.h"
@@ -1867,6 +1868,15 @@
   if (LocalFrame* frame = state.GetDocument().GetFrame()) {
     multiplier *= frame->TextZoomFactor();
   }
+
+  if (!state.StyleBuilder().GetTextSizeAdjust().IsAuto()) {
+    if (RuntimeEnabledFeatures::NewTextSizeAdjustEnabled()) {
+      Settings* settings = state.GetDocument().GetSettings();
+      if (settings && settings->GetTextAutosizingEnabled()) {
+        multiplier *= state.StyleBuilder().GetTextSizeAdjust().Multiplier();
+      }
+    }
+  }
   return state.CssToLengthConversionData().CopyWithAdjustedZoom(multiplier);
 }
 
diff --git a/third_party/blink/renderer/core/css/resolver/style_cascade_test.cc b/third_party/blink/renderer/core/css/resolver/style_cascade_test.cc
index ee0b19d..ea470143 100644
--- a/third_party/blink/renderer/core/css/resolver/style_cascade_test.cc
+++ b/third_party/blink/renderer/core/css/resolver/style_cascade_test.cc
@@ -44,6 +44,7 @@
 #include "third_party/blink/renderer/core/css/style_engine.h"
 #include "third_party/blink/renderer/core/css/style_sheet_contents.h"
 #include "third_party/blink/renderer/core/execution_context/security_context.h"
+#include "third_party/blink/renderer/core/frame/settings.h"
 #include "third_party/blink/renderer/core/html/html_element.h"
 #include "third_party/blink/renderer/core/style/computed_style.h"
 #include "third_party/blink/renderer/core/style_property_shorthand.h"
@@ -3105,6 +3106,33 @@
   EXPECT_EQ("10px", cascade.ComputedValue("height"));
 }
 
+TEST_F(StyleCascadeTest, InitialTextSizeAdjust) {
+  GetDocument().GetSettings()->SetTextAutosizingEnabled(true);
+  ScopedNewTextSizeAdjustForTest scoped_feature(true);
+
+  TestCascade cascade(GetDocument());
+  cascade.Add("font-size:10px");
+  cascade.Add("line-height:20px");
+  cascade.Apply();
+
+  EXPECT_EQ("10px", cascade.ComputedValue("font-size"));
+  EXPECT_EQ("20px", cascade.ComputedValue("line-height"));
+}
+
+TEST_F(StyleCascadeTest, NonInitialTextSizeAdjust) {
+  GetDocument().GetSettings()->SetTextAutosizingEnabled(true);
+  ScopedNewTextSizeAdjustForTest scoped_feature(true);
+
+  TestCascade cascade(GetDocument());
+  cascade.Add("font-size:10px");
+  cascade.Add("line-height:20px");
+  cascade.Add("text-size-adjust:200%");
+  cascade.Apply();
+
+  EXPECT_EQ("20px", cascade.ComputedValue("font-size"));
+  EXPECT_EQ("40px", cascade.ComputedValue("line-height"));
+}
+
 TEST_F(StyleCascadeTest, DoesNotDependOnCascadeAffectingProperty) {
   TestCascade cascade(GetDocument());
   cascade.Add("width:10px");
diff --git a/third_party/blink/renderer/core/css/resolver/style_resolver_state.cc b/third_party/blink/renderer/core/css/resolver/style_resolver_state.cc
index 5a96c7de..fdebfe3 100644
--- a/third_party/blink/renderer/core/css/resolver/style_resolver_state.cc
+++ b/third_party/blink/renderer/core/css/resolver/style_resolver_state.cc
@@ -274,6 +274,20 @@
   font_builder_.DidChangeWritingMode();
 }
 
+void StyleResolverState::SetTextSizeAdjust(
+    TextSizeAdjust new_text_size_adjust) {
+  if (StyleBuilder().GetTextSizeAdjust() == new_text_size_adjust) {
+    return;
+  }
+  StyleBuilder().SetTextSizeAdjust(new_text_size_adjust);
+  // When `NewTextSizeAdjust` is enabled, text-size-adjust affects font-size
+  // during style building.
+  if (RuntimeEnabledFeatures::NewTextSizeAdjustEnabled()) {
+    UpdateLengthConversionData();
+    font_builder_.DidChangeTextSizeAdjust();
+  }
+}
+
 void StyleResolverState::SetTextOrientation(ETextOrientation text_orientation) {
   if (StyleBuilder().GetTextOrientation() != text_orientation) {
     StyleBuilder().SetTextOrientation(text_orientation);
diff --git a/third_party/blink/renderer/core/css/resolver/style_resolver_state.h b/third_party/blink/renderer/core/css/resolver/style_resolver_state.h
index b4814cb..c8d78842 100644
--- a/third_party/blink/renderer/core/css/resolver/style_resolver_state.h
+++ b/third_party/blink/renderer/core/css/resolver/style_resolver_state.h
@@ -186,6 +186,7 @@
   void SetZoom(float);
   void SetEffectiveZoom(float);
   void SetWritingMode(WritingMode);
+  void SetTextSizeAdjust(TextSizeAdjust);
   void SetTextOrientation(ETextOrientation);
   void SetPositionAnchor(ScopedCSSName*);
   void SetInsetAreaOffsets(const std::optional<InsetAreaOffsets>&);
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc
index 01111bef..25e730f 100644
--- a/third_party/blink/renderer/core/dom/document.cc
+++ b/third_party/blink/renderer/core/dom/document.cc
@@ -5995,15 +5995,15 @@
   scripted_animation_controller_->EnqueuePerFrameEvent(overscroll_event);
 }
 
-void Document::EnqueueSnapChangedEvent(Node* target,
-                                       Member<Node>& block_target,
-                                       Member<Node>& inline_target) {
-  Event* snapchanged_event = SnapEvent::Create(
-      event_type_names::kSnapchanged,
+void Document::EnqueueScrollSnapChangeEvent(Node* target,
+                                            Member<Node>& block_target,
+                                            Member<Node>& inline_target) {
+  Event* scrollsnapchange_event = SnapEvent::Create(
+      event_type_names::kScrollsnapchange,
       (target->IsDocumentNode() ? Event::Bubbles::kYes : Event::Bubbles::kNo),
       block_target, inline_target);
-  snapchanged_event->SetTarget(target);
-  scripted_animation_controller_->EnqueuePerFrameEvent(snapchanged_event);
+  scrollsnapchange_event->SetTarget(target);
+  scripted_animation_controller_->EnqueuePerFrameEvent(scrollsnapchange_event);
 }
 
 void Document::EnqueueSnapChangingEvent(Node* target,
diff --git a/third_party/blink/renderer/core/dom/document.h b/third_party/blink/renderer/core/dom/document.h
index 51f66da..54128453 100644
--- a/third_party/blink/renderer/core/dom/document.h
+++ b/third_party/blink/renderer/core/dom/document.h
@@ -1523,9 +1523,9 @@
   void EnqueueVisualViewportScrollEvent();
   void EnqueueVisualViewportScrollEndEvent();
   void EnqueueVisualViewportResizeEvent();
-  void EnqueueSnapChangedEvent(Node* target,
-                               Member<Node>& block_target,
-                               Member<Node>& inline_target);
+  void EnqueueScrollSnapChangeEvent(Node* target,
+                                    Member<Node>& block_target,
+                                    Member<Node>& inline_target);
   void EnqueueSnapChangingEvent(Node* target,
                                 Member<Node>& block_target,
                                 Member<Node>& inline_target);
diff --git a/third_party/blink/renderer/core/dom/events/event_target.h b/third_party/blink/renderer/core/dom/events/event_target.h
index 269c9d6..fb70e97 100644
--- a/third_party/blink/renderer/core/dom/events/event_target.h
+++ b/third_party/blink/renderer/core/dom/events/event_target.h
@@ -307,7 +307,7 @@
   DEFINE_ATTRIBUTE_EVENT_LISTENER(selectionchange, kSelectionchange)
   DEFINE_ATTRIBUTE_EVENT_LISTENER(selectstart, kSelectstart)
   DEFINE_ATTRIBUTE_EVENT_LISTENER(slotchange, kSlotchange)
-  DEFINE_ATTRIBUTE_EVENT_LISTENER(snapchanged, kSnapchanged)
+  DEFINE_ATTRIBUTE_EVENT_LISTENER(scrollsnapchange, kScrollsnapchange)
   DEFINE_ATTRIBUTE_EVENT_LISTENER(snapchanging, kSnapchanging)
   DEFINE_ATTRIBUTE_EVENT_LISTENER(stalled, kStalled)
   DEFINE_ATTRIBUTE_EVENT_LISTENER(submit, kSubmit)
diff --git a/third_party/blink/renderer/core/dom/events/event_target_test.cc b/third_party/blink/renderer/core/dom/events/event_target_test.cc
index 4cb789ca..4bf5b43c 100644
--- a/third_party/blink/renderer/core/dom/events/event_target_test.cc
+++ b/third_party/blink/renderer/core/dom/events/event_target_test.cc
@@ -180,12 +180,12 @@
   EXPECT_TRUE(GetDocument().IsUseCounted(WebFeature::kSnapEvent));
 }
 
-TEST_F(EventTargetTest, UseCountSnapchanged) {
+TEST_F(EventTargetTest, UseCountScrollsnapchange) {
   EXPECT_FALSE(GetDocument().IsUseCounted(WebFeature::kSnapEvent));
   GetDocument().GetSettings()->SetScriptEnabled(true);
   ClassicScript::CreateUnspecifiedScript(R"HTML(
     const element = document.createElement('div');
-    element.addEventListener('snapchanged', () => {});
+    element.addEventListener('scrollsnapchange', () => {});
   )HTML")
       ->RunScript(GetDocument().domWindow());
   EXPECT_TRUE(GetDocument().IsUseCounted(WebFeature::kSnapEvent));
diff --git a/third_party/blink/renderer/core/dom/global_event_handlers.idl b/third_party/blink/renderer/core/dom/global_event_handlers.idl
index 1174519..01c8809 100644
--- a/third_party/blink/renderer/core/dom/global_event_handlers.idl
+++ b/third_party/blink/renderer/core/dom/global_event_handlers.idl
@@ -93,7 +93,7 @@
   attribute EventHandler onseeking;
   attribute EventHandler onselect;
   attribute EventHandler onslotchange;
-  [RuntimeEnabled=CSSSnapChangedEvent] attribute EventHandler onsnapchanged;
+  [RuntimeEnabled=CSSScrollSnapChangeEvent] attribute EventHandler onscrollsnapchange;
   [RuntimeEnabled=CSSSnapChangingEvent] attribute EventHandler onsnapchanging;
   //attribute EventHandler onsort;
   attribute EventHandler onstalled;
diff --git a/third_party/blink/renderer/core/events/event_type_names.json5 b/third_party/blink/renderer/core/events/event_type_names.json5
index 8ad0efa..147a1dc 100644
--- a/third_party/blink/renderer/core/events/event_type_names.json5
+++ b/third_party/blink/renderer/core/events/event_type_names.json5
@@ -295,7 +295,7 @@
     "signalingstatechange",
     "sinkchange",
     "slotchange",
-    "snapchanged",
+    "scrollsnapchange",
     "snapchanging",
     "soundend",
     "soundstart",
diff --git a/third_party/blink/renderer/core/events/event_util.cc b/third_party/blink/renderer/core/events/event_util.cc
index 2acec76..3f5319d0 100644
--- a/third_party/blink/renderer/core/events/event_util.cc
+++ b/third_party/blink/renderer/core/events/event_util.cc
@@ -79,7 +79,7 @@
 
 bool IsSnapEventType(const AtomicString& event_type) {
   return event_type == event_type_names::kSnapchanging ||
-         event_type == event_type_names::kSnapchanged;
+         event_type == event_type_names::kScrollsnapchange;
 }
 
 }  // namespace event_util
diff --git a/third_party/blink/renderer/core/frame/root_frame_viewport.cc b/third_party/blink/renderer/core/frame/root_frame_viewport.cc
index 5ea1ab0..d790eee 100644
--- a/third_party/blink/renderer/core/frame/root_frame_viewport.cc
+++ b/third_party/blink/renderer/core/frame/root_frame_viewport.cc
@@ -748,8 +748,8 @@
   ScrollableArea::Trace(visitor);
 }
 
-void RootFrameViewport::UpdateSnappedTargetsAndEnqueueSnapChanged() {
-  LayoutViewport().UpdateSnappedTargetsAndEnqueueSnapChanged();
+void RootFrameViewport::UpdateSnappedTargetsAndEnqueueScrollSnapChange() {
+  LayoutViewport().UpdateSnappedTargetsAndEnqueueScrollSnapChange();
 }
 
 std::optional<cc::TargetSnapAreaElementIds>
diff --git a/third_party/blink/renderer/core/frame/root_frame_viewport.h b/third_party/blink/renderer/core/frame/root_frame_viewport.h
index fc649415..f411007 100644
--- a/third_party/blink/renderer/core/frame/root_frame_viewport.h
+++ b/third_party/blink/renderer/core/frame/root_frame_viewport.h
@@ -133,7 +133,7 @@
   void SetSnapContainerDataNeedsUpdate(bool) override;
   std::optional<gfx::PointF> GetSnapPositionAndSetTarget(
       const cc::SnapSelectionStrategy& strategy) override;
-  void UpdateSnappedTargetsAndEnqueueSnapChanged() override;
+  void UpdateSnappedTargetsAndEnqueueScrollSnapChange() override;
   std::optional<cc::TargetSnapAreaElementIds> GetSnapchangingTargetIds()
       const override;
   void SetSnapchangingTargetIds(
diff --git a/third_party/blink/renderer/core/frame/settings.json5 b/third_party/blink/renderer/core/frame/settings.json5
index 09a94ff..64e0ec7 100644
--- a/third_party/blink/renderer/core/frame/settings.json5
+++ b/third_party/blink/renderer/core/frame/settings.json5
@@ -1203,6 +1203,8 @@
       name: "bypassCSP",
       initial: false,
     },
+    // Enables automatic adjustments of text size on mobile. This enables the
+    // text autosizer, and controls whether text-size-adjust can apply.
     {
       name: "textAutosizingEnabled",
       initial: false,
diff --git a/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc b/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc
index 275b1f6..18711e1 100644
--- a/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc
+++ b/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc
@@ -1303,7 +1303,7 @@
   }
   if (ScrollableArea* scrollable_area =
           ScrollableArea::GetForScrolling(target_node->GetLayoutBox())) {
-    scrollable_area->UpdateSnappedTargetsAndEnqueueSnapChanged();
+    scrollable_area->UpdateSnappedTargetsAndEnqueueScrollSnapChange();
     scrollable_area->SetImplSnapStrategy(nullptr);
   }
 
@@ -3882,7 +3882,7 @@
     bool has_handlers) {
   widget_base_->widget_input_handler_manager()
       ->input_event_queue()
-      ->HasPointerRawUpdateEventHandlers(has_handlers);
+      ->SetHasPointerRawUpdateEventHandlers(has_handlers);
 }
 
 void WebFrameWidgetImpl::SetNeedsLowLatencyInput(bool needs_low_latency) {
diff --git a/third_party/blink/renderer/core/html/forms/html_data_list_element.cc b/third_party/blink/renderer/core/html/forms/html_data_list_element.cc
index 514a2d6f..9ed37491 100644
--- a/third_party/blink/renderer/core/html/forms/html_data_list_element.cc
+++ b/third_party/blink/renderer/core/html/forms/html_data_list_element.cc
@@ -149,6 +149,7 @@
       if (auto* option = select->SelectedOption()) {
         option->Focus(FocusParams(FocusTrigger::kScript));
       }
+      select->PseudoStateChanged(CSSSelector::kPseudoOpen);
     }
   }
 }
@@ -165,6 +166,7 @@
       // MenuListSelectType::ManuallyAssignSlots changes behavior based on
       // whether the popover is opened or closed.
       select->GetShadowRoot()->SetNeedsAssignmentRecalc();
+      select->PseudoStateChanged(CSSSelector::kPseudoOpen);
     }
   }
 }
diff --git a/third_party/blink/renderer/core/html/html_attribute_names.json5 b/third_party/blink/renderer/core/html/html_attribute_names.json5
index 58c3d1d7..fd5f3e1 100644
--- a/third_party/blink/renderer/core/html/html_attribute_names.json5
+++ b/third_party/blink/renderer/core/html/html_attribute_names.json5
@@ -262,7 +262,7 @@
     "onselectionchange",
     "onshow",
     "onslotchange",
-    "onsnapchanged",
+    "onscrollsnapchange",
     "onsnapchanging",
     "onstalled",
     "onstorage",
diff --git a/third_party/blink/renderer/core/html/html_element.cc b/third_party/blink/renderer/core/html/html_element.cc
index ccd055c8..8d72b677 100644
--- a/third_party/blink/renderer/core/html/html_element.cc
+++ b/third_party/blink/renderer/core/html/html_element.cc
@@ -578,8 +578,8 @@
        event_type_names::kSelectstart, nullptr},
       {html_names::kOnslotchangeAttr, kNoWebFeature,
        event_type_names::kSlotchange, nullptr},
-      {html_names::kOnsnapchangedAttr, kNoWebFeature,
-       event_type_names::kSnapchanged, nullptr},
+      {html_names::kOnscrollsnapchangeAttr, kNoWebFeature,
+       event_type_names::kScrollsnapchange, nullptr},
       {html_names::kOnsnapchangingAttr, kNoWebFeature,
        event_type_names::kSnapchanging, nullptr},
       {html_names::kOnstalledAttr, kNoWebFeature, event_type_names::kStalled,
diff --git a/third_party/blink/renderer/core/intersection_observer/intersection_observer_controller.h b/third_party/blink/renderer/core/intersection_observer/intersection_observer_controller.h
index 1c69c5b..16b1a3f 100644
--- a/third_party/blink/renderer/core/intersection_observer/intersection_observer_controller.h
+++ b/third_party/blink/renderer/core/intersection_observer/intersection_observer_controller.h
@@ -77,11 +77,10 @@
 
  private:
   // IntersectionObserver's with a connected explicit root in this document.
-  HeapHashSet<WeakMember<IntersectionObserver>>
-      tracked_explicit_root_observers_;
+  HeapHashSet<Member<IntersectionObserver>> tracked_explicit_root_observers_;
   // IntersectionObservations with an implicit root and connected target in this
   // document.
-  HeapHashSet<WeakMember<IntersectionObservation>>
+  HeapHashSet<Member<IntersectionObservation>>
       tracked_implicit_root_observations_;
   // IntersectionObservers for which this is the execution context of the
   // callback, and with unsent notifications.
diff --git a/third_party/blink/renderer/core/layout/text_autosizer.cc b/third_party/blink/renderer/core/layout/text_autosizer.cc
index 2b8062c..0b78b69 100644
--- a/third_party/blink/renderer/core/layout/text_autosizer.cc
+++ b/third_party/blink/renderer/core/layout/text_autosizer.cc
@@ -1204,15 +1204,23 @@
   DCHECK(layout_object);
   const ComputedStyle& current_style = layout_object->StyleRef();
   if (!current_style.GetTextSizeAdjust().IsAuto()) {
-    // The accessibility font scale factor is applied by the autosizer so we
-    // need to apply that scale factor on top of the text-size-adjust
-    // multiplier. Only apply the accessibility factor if the autosizer has
-    // determined a multiplier should be applied so that text-size-adjust:none
-    // does not cause a multiplier to be applied when it wouldn't be otherwise.
-    bool should_apply_accessibility_font_scale_factor = multiplier > 1;
-    multiplier = current_style.GetTextSizeAdjust().Multiplier();
-    if (should_apply_accessibility_font_scale_factor)
-      multiplier *= page_info_.accessibility_font_scale_factor_;
+    if (RuntimeEnabledFeatures::NewTextSizeAdjustEnabled()) {
+      // Non-auto values of text-size-adjust should fully disable automatic
+      // text size adjustment, including the accessibility font scale factor.
+      multiplier = 1;
+    } else {
+      // The accessibility font scale factor is applied by the autosizer so we
+      // need to apply that scale factor on top of the text-size-adjust
+      // multiplier. Only apply the accessibility factor if the autosizer has
+      // determined a multiplier should be applied so that text-size-adjust:none
+      // does not cause a multiplier to be applied when it wouldn't be
+      // otherwise.
+      bool should_apply_accessibility_font_scale_factor = multiplier > 1;
+      multiplier = current_style.GetTextSizeAdjust().Multiplier();
+      if (should_apply_accessibility_font_scale_factor) {
+        multiplier *= page_info_.accessibility_font_scale_factor_;
+      }
+    }
   } else if (multiplier < 1) {
     // Unlike text-size-adjust, the text autosizer should only inflate fonts.
     multiplier = 1;
diff --git a/third_party/blink/renderer/core/layout/text_autosizer_test.cc b/third_party/blink/renderer/core/layout/text_autosizer_test.cc
index 92a7fbe..cc9207c 100644
--- a/third_party/blink/renderer/core/layout/text_autosizer_test.cc
+++ b/third_party/blink/renderer/core/layout/text_autosizer_test.cc
@@ -34,8 +34,12 @@
   float device_scale_factor_;
 };
 
-class TextAutosizerTest : public RenderingTest {
+class TextAutosizerTest : public RenderingTest,
+                          public testing::WithParamInterface<bool>,
+                          private ScopedNewTextSizeAdjustForTest {
  public:
+  TextAutosizerTest() : ScopedNewTextSizeAdjustForTest(GetParam()) {}
+
   RenderingTestChromeClient& GetChromeClient() const override {
     return GetTextAutosizerClient();
   }
@@ -62,7 +66,9 @@
   }
 };
 
-TEST_F(TextAutosizerTest, SimpleParagraph) {
+INSTANTIATE_TEST_SUITE_P(All, TextAutosizerTest, testing::Bool());
+
+TEST_P(TextAutosizerTest, SimpleParagraph) {
   SetBodyInnerHTML(R"HTML(
     <style>
       html { font-size: 16px; }
@@ -87,7 +93,7 @@
                   autosized->GetLayoutObject()->StyleRef().ComputedFontSize());
 }
 
-TEST_F(TextAutosizerTest, TextSizeAdjustDisablesAutosizing) {
+TEST_P(TextAutosizerTest, TextSizeAdjustDisablesAutosizing) {
   SetBodyInnerHTML(R"HTML(
     <style>
       html { font-size: 16px; }
@@ -135,7 +141,7 @@
   EXPECT_FLOAT_EQ(16.f, text_size_adjust100->StyleRef().ComputedFontSize());
 }
 
-TEST_F(TextAutosizerTest, ParagraphWithChangingTextSizeAdjustment) {
+TEST_P(TextAutosizerTest, ParagraphWithChangingTextSizeAdjustment) {
   SetBodyInnerHTML(R"HTML(
     <style>
       html { font-size: 16px; }
@@ -189,7 +195,7 @@
       40.f, autosized_div->GetLayoutObject()->StyleRef().ComputedFontSize());
 }
 
-TEST_F(TextAutosizerTest, ZeroTextSizeAdjustment) {
+TEST_P(TextAutosizerTest, ZeroTextSizeAdjustment) {
   SetBodyInnerHTML(R"HTML(
     <style>
       html { font-size: 16px; }
@@ -211,7 +217,7 @@
   EXPECT_FLOAT_EQ(0.f, text_size_adjust_zero->StyleRef().ComputedFontSize());
 }
 
-TEST_F(TextAutosizerTest, NegativeTextSizeAdjustment) {
+TEST_P(TextAutosizerTest, NegativeTextSizeAdjustment) {
   SetBodyInnerHTML(
       "<style>"
       "  html { font-size: 16px; }"
@@ -235,7 +241,7 @@
                   text_size_adjust_negative->StyleRef().ComputedFontSize());
 }
 
-TEST_F(TextAutosizerTest, TextSizeAdjustmentPixelUnits) {
+TEST_P(TextAutosizerTest, TextSizeAdjustmentPixelUnits) {
   SetBodyInnerHTML(
       "<style>"
       "  html { font-size: 16px; }"
@@ -258,7 +264,7 @@
   EXPECT_FLOAT_EQ(40.f, text_size_adjust_pixels->StyleRef().ComputedFontSize());
 }
 
-TEST_F(TextAutosizerTest, NestedTextSizeAdjust) {
+TEST_P(TextAutosizerTest, NestedTextSizeAdjust) {
   SetBodyInnerHTML(R"HTML(
     <style>
       html { font-size: 16px; }
@@ -295,7 +301,7 @@
   EXPECT_FLOAT_EQ(8.48f, text_size_adjust_b->StyleRef().ComputedFontSize());
 }
 
-TEST_F(TextAutosizerTest, PrefixedTextSizeAdjustIsAlias) {
+TEST_P(TextAutosizerTest, PrefixedTextSizeAdjustIsAlias) {
   SetBodyInnerHTML(R"HTML(
     <style>
       html { font-size: 16px; }
@@ -318,7 +324,7 @@
       .5f, text_size_adjust->StyleRef().GetTextSizeAdjust().Multiplier());
 }
 
-TEST_F(TextAutosizerTest, AccessibilityFontScaleFactor) {
+TEST_P(TextAutosizerTest, AccessibilityFontScaleFactor) {
   GetDocument().GetSettings()->SetAccessibilityFontScaleFactor(1.5);
   SetBodyInnerHTML(R"HTML(
     <style>
@@ -344,7 +350,13 @@
                   autosized->GetLayoutObject()->StyleRef().ComputedFontSize());
 }
 
-TEST_F(TextAutosizerTest, AccessibilityFontScaleFactorWithTextSizeAdjustNone) {
+TEST_P(TextAutosizerTest, AccessibilityFontScaleFactorWithTextSizeAdjustNone) {
+  if (RuntimeEnabledFeatures::NewTextSizeAdjustEnabled()) {
+    // Non-auto values of text-size-adjust should disable all automatic font
+    // scale adjustment.
+    return;
+  }
+
   GetDocument().GetSettings()->SetAccessibilityFontScaleFactor(1.5);
   SetBodyInnerHTML(R"HTML(
     <style>
@@ -389,7 +401,7 @@
       16.f, not_autosized->GetLayoutObject()->StyleRef().ComputedFontSize());
 }
 
-TEST_F(TextAutosizerTest, ChangingAccessibilityFontScaleFactor) {
+TEST_P(TextAutosizerTest, ChangingAccessibilityFontScaleFactor) {
   GetDocument().GetSettings()->SetAccessibilityFontScaleFactor(1);
   SetBodyInnerHTML(R"HTML(
     <style>
@@ -425,7 +437,13 @@
                   autosized->GetLayoutObject()->StyleRef().ComputedFontSize());
 }
 
-TEST_F(TextAutosizerTest, TextSizeAdjustDoesNotDisableAccessibility) {
+TEST_P(TextAutosizerTest, TextSizeAdjustDoesNotDisableAccessibility) {
+  if (RuntimeEnabledFeatures::NewTextSizeAdjustEnabled()) {
+    // Non-auto values of text-size-adjust should disable all automatic font
+    // scale adjustment.
+    return;
+  }
+
   GetDocument().GetSettings()->SetAccessibilityFontScaleFactor(1.5);
   SetBodyInnerHTML(R"HTML(
     <style>
@@ -492,7 +510,7 @@
 }
 
 // https://crbug.com/646237
-TEST_F(TextAutosizerTest, DISABLED_TextSizeAdjustWithoutNeedingAutosizing) {
+TEST_P(TextAutosizerTest, DISABLED_TextSizeAdjustWithoutNeedingAutosizing) {
   GetDocument().GetSettings()->SetTextAutosizingWindowSizeOverride(
       gfx::Size(800, 600));
   SetBodyInnerHTML(R"HTML(
@@ -512,7 +530,7 @@
       1.5f, text_size_adjust->StyleRef().GetTextSizeAdjust().Multiplier());
 }
 
-TEST_F(TextAutosizerTest, DeviceScaleAdjustmentWithViewport) {
+TEST_P(TextAutosizerTest, DeviceScaleAdjustmentWithViewport) {
   SetBodyInnerHTML(R"HTML(
     <meta name='viewport' content='width=800'>
     <style>
@@ -555,7 +573,7 @@
                   autosized->GetLayoutObject()->StyleRef().ComputedFontSize());
 }
 
-TEST_F(TextAutosizerTest, ChangingSuperClusterFirstText) {
+TEST_P(TextAutosizerTest, ChangingSuperClusterFirstText) {
   SetBodyInnerHTML(R"HTML(
     <meta name='viewport' content='width=800'>
     <style>
@@ -598,7 +616,7 @@
   EXPECT_FLOAT_EQ(28.f, short_text->StyleRef().ComputedFontSize());
 }
 
-TEST_F(TextAutosizerTest, ChangingSuperClusterSecondText) {
+TEST_P(TextAutosizerTest, ChangingSuperClusterSecondText) {
   SetBodyInnerHTML(R"HTML(
     <meta name='viewport' content='width=800'>
     <style>
@@ -641,7 +659,7 @@
   EXPECT_FLOAT_EQ(28.f, short_text->StyleRef().ComputedFontSize());
 }
 
-TEST_F(TextAutosizerTest, AddingSuperCluster) {
+TEST_P(TextAutosizerTest, AddingSuperCluster) {
   SetBodyInnerHTML(R"HTML(
     <meta name='viewport' content='width=800'>
     <style>
@@ -686,7 +704,7 @@
   EXPECT_FLOAT_EQ(28.f, short_text->StyleRef().ComputedFontSize());
 }
 
-TEST_F(TextAutosizerTest, ChangingInheritedClusterTextInsideSuperCluster) {
+TEST_P(TextAutosizerTest, ChangingInheritedClusterTextInsideSuperCluster) {
   SetBodyInnerHTML(R"HTML(
     <meta name='viewport' content='width=800'>
     <style>
@@ -731,7 +749,7 @@
   EXPECT_FLOAT_EQ(28.f, short_text->StyleRef().ComputedFontSize());
 }
 
-TEST_F(TextAutosizerTest, AutosizeInnerContentOfRuby) {
+TEST_P(TextAutosizerTest, AutosizeInnerContentOfRuby) {
   SetBodyInnerHTML(R"HTML(
     <meta name='viewport' content='width=800'>
     <style>
@@ -786,7 +804,7 @@
                   ruby_block->GetLayoutObject()->StyleRef().ComputedFontSize());
 }
 
-TEST_F(TextAutosizerTest, ResizeAndGlyphOverflowChanged) {
+TEST_P(TextAutosizerTest, ResizeAndGlyphOverflowChanged) {
   GetDocument().GetSettings()->SetTextAutosizingWindowSizeOverride(
       gfx::Size(360, 640));
   Element* html = GetDocument().body()->parentElement();
@@ -828,7 +846,7 @@
   UpdateAllLifecyclePhasesForTest();
 }
 
-TEST_F(TextAutosizerTest, narrowContentInsideNestedWideBlock) {
+TEST_P(TextAutosizerTest, narrowContentInsideNestedWideBlock) {
   Element* html = GetDocument().body()->parentElement();
   html->setInnerHTML(
       "<head>"
@@ -864,7 +882,7 @@
                   content->GetLayoutObject()->StyleRef().ComputedFontSize());
 }
 
-TEST_F(TextAutosizerTest, LayoutViewWidthProvider) {
+TEST_P(TextAutosizerTest, LayoutViewWidthProvider) {
   Element* html = GetDocument().body()->parentElement();
   html->setInnerHTML(
       "<head>"
@@ -905,7 +923,7 @@
                   content->GetLayoutObject()->StyleRef().ComputedFontSize());
 }
 
-TEST_F(TextAutosizerTest, MultiColumns) {
+TEST_P(TextAutosizerTest, MultiColumns) {
   Element* html = GetDocument().body()->parentElement();
   html->setInnerHTML(
       "<head>"
@@ -939,7 +957,7 @@
                   target->GetLayoutObject()->StyleRef().ComputedFontSize());
 }
 
-TEST_F(TextAutosizerTest, MultiColumns2) {
+TEST_P(TextAutosizerTest, MultiColumns2) {
   Element* html = GetDocument().body()->parentElement();
   html->setInnerHTML(
       "<head>"
@@ -985,7 +1003,7 @@
                   target2->GetLayoutObject()->StyleRef().ComputedFontSize());
 }
 
-TEST_F(TextAutosizerTest, ScaledbyDSF) {
+TEST_P(TextAutosizerTest, ScaledbyDSF) {
   const float device_scale = 3;
   set_device_scale_factor(device_scale);
   SetBodyInnerHTML(R"HTML(
@@ -1013,7 +1031,7 @@
                   target->GetLayoutObject()->StyleRef().ComputedFontSize());
 }
 
-TEST_F(TextAutosizerTest, ClusterHasNotEnoughTextToAutosizeForZoomDSF) {
+TEST_P(TextAutosizerTest, ClusterHasNotEnoughTextToAutosizeForZoomDSF) {
   SetBodyInnerHTML(R"HTML(
     <style>
       html { font-size: 8px; }
@@ -1048,7 +1066,7 @@
 // value change of TextAutosizer::ClusterHasEnoughTextToAutosize() depending on
 // the length of text even when DSF is not 1 (e.g., letting DummyPageHolder
 // update the view size according to the change of DSF).
-TEST_F(TextAutosizerTest, ClusterHasEnoughTextToAutosizeForZoomDSF) {
+TEST_P(TextAutosizerTest, ClusterHasEnoughTextToAutosizeForZoomDSF) {
   const float device_scale = 3;
   set_device_scale_factor(device_scale);
   SetBodyInnerHTML(R"HTML(
@@ -1073,7 +1091,7 @@
                   target->GetLayoutObject()->StyleRef().ComputedFontSize());
 }
 
-TEST_F(TextAutosizerTest, AfterPrint) {
+TEST_P(TextAutosizerTest, AfterPrint) {
   const float device_scale = 3;
   gfx::SizeF print_size(160, 240);
   set_device_scale_factor(device_scale);
@@ -1101,7 +1119,7 @@
                   target->GetLayoutObject()->StyleRef().ComputedFontSize());
 }
 
-TEST_F(TextAutosizerTest, FingerprintWidth) {
+TEST_P(TextAutosizerTest, FingerprintWidth) {
   SetBodyInnerHTML(R"HTML(
     <style>
       html { font-size: 8px; }
@@ -1119,7 +1137,12 @@
   // The test pass if it doesn't crash nor hit DCHECK.
 }
 
-class TextAutosizerSimTest : public SimTest {
+class TextAutosizerSimTest : public SimTest,
+                             public testing::WithParamInterface<bool>,
+                             private ScopedNewTextSizeAdjustForTest {
+ public:
+  TextAutosizerSimTest() : ScopedNewTextSizeAdjustForTest(GetParam()) {}
+
  private:
   void SetUp() override {
     SimTest::SetUp();
@@ -1134,7 +1157,9 @@
   }
 };
 
-TEST_F(TextAutosizerSimTest, CrossSiteUseCounter) {
+INSTANTIATE_TEST_SUITE_P(All, TextAutosizerSimTest, testing::Bool());
+
+TEST_P(TextAutosizerSimTest, CrossSiteUseCounter) {
   WebView().MainFrameViewWidget()->Resize(gfx::Size(800, 800));
 
   SimRequest main_resource("https://example.com/", "text/html");
diff --git a/third_party/blink/renderer/core/page/page.cc b/third_party/blink/renderer/core/page/page.cc
index 8f1b175a..57f0360 100644
--- a/third_party/blink/renderer/core/page/page.cc
+++ b/third_party/blink/renderer/core/page/page.cc
@@ -906,6 +906,12 @@
       // We need to update even for remote main frames since this setting
       // could be changed via InternalSettings.
       TextAutosizer::UpdatePageInfoInAllFrames(MainFrame());
+      // The new text-size-adjust implementation requires the text autosizing
+      // setting but applies the adjustment in style rather than via the text
+      // autosizer, so we need to invalidate style.
+      if (RuntimeEnabledFeatures::NewTextSizeAdjustEnabled()) {
+        InitialStyleChanged();
+      }
       break;
     case ChangeType::kFontFamily:
       for (Frame* frame = MainFrame(); frame;
diff --git a/third_party/blink/renderer/core/page/page_animator.cc b/third_party/blink/renderer/core/page/page_animator.cc
index 25e2de97..a7dba72 100644
--- a/third_party/blink/renderer/core/page/page_animator.cc
+++ b/third_party/blink/renderer/core/page/page_animator.cc
@@ -259,7 +259,7 @@
     auto scope = SyncScrollAttemptHeuristic::GetScrollHandlerScope();
     active_controllers[i]->DispatchEvents(WTF::BindRepeating([](Event* event) {
       return event->type() == event_type_names::kScroll ||
-             event->type() == event_type_names::kSnapchanged ||
+             event->type() == event_type_names::kScrollsnapchange ||
              event->type() == event_type_names::kSnapchanging ||
              event->type() == event_type_names::kScrollend;
     }));
diff --git a/third_party/blink/renderer/core/page/scrolling/snap_coordinator.cc b/third_party/blink/renderer/core/page/scrolling/snap_coordinator.cc
index be5de04..bbade88 100644
--- a/third_party/blink/renderer/core/page/scrolling/snap_coordinator.cc
+++ b/third_party/blink/renderer/core/page/scrolling/snap_coordinator.cc
@@ -77,9 +77,9 @@
     if (old_snap_container_data) {
       snap_container.SetNeedsPaintPropertyUpdate();
       scrollable_area->SetSnapchangingTargetIds(std::nullopt);
-      scrollable_area->SetSnapchangedTargetIds(std::nullopt);
-      if (RuntimeEnabledFeatures::CSSSnapChangedEventEnabled()) {
-        scrollable_area->EnqueueSnapChangedEvent();
+      scrollable_area->SetScrollsnapchangeTargetIds(std::nullopt);
+      if (RuntimeEnabledFeatures::CSSScrollSnapChangeEventEnabled()) {
+        scrollable_area->EnqueueScrollSnapChangeEvent();
       }
       scrollable_area->SetSnapContainerData(std::nullopt);
     }
diff --git a/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc b/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc
index 03825f5..e88b810 100644
--- a/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc
+++ b/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc
@@ -3172,8 +3172,9 @@
       ->OwnerNodeId();
 }
 
-void PaintLayerScrollableArea::UpdateSnappedTargetsAndEnqueueSnapChanged() {
-  if (!RuntimeEnabledFeatures::CSSSnapChangedEventEnabled()) {
+void PaintLayerScrollableArea::
+    UpdateSnappedTargetsAndEnqueueScrollSnapChange() {
+  if (!RuntimeEnabledFeatures::CSSScrollSnapChangeEventEnabled()) {
     return;
   }
   const cc::SnapContainerData* container_data = GetSnapContainerData();
@@ -3183,14 +3184,14 @@
   cc::TargetSnapAreaElementIds new_target_ids =
       container_data->GetTargetSnapAreaElementIds();
   auto& rare_data = EnsureRareData();
-  bool snapchanged =
-      (rare_data.snapchanged_target_ids_
-           ? (new_target_ids.x != rare_data.snapchanged_target_ids_->x ||
-              new_target_ids.y != rare_data.snapchanged_target_ids_->y)
+  bool scrollsnapchange =
+      (rare_data.scrollsnapchange_target_ids_
+           ? (new_target_ids.x != rare_data.scrollsnapchange_target_ids_->x ||
+              new_target_ids.y != rare_data.scrollsnapchange_target_ids_->y)
            : true);
-  if (snapchanged) {
-    rare_data.snapchanged_target_ids_ = new_target_ids;
-    EnqueueSnapChangedEvent();
+  if (scrollsnapchange) {
+    rare_data.scrollsnapchange_target_ids_ = new_target_ids;
+    EnqueueScrollSnapChangeEvent();
   }
 }
 
@@ -3243,8 +3244,8 @@
   }
   bool horiz = GetLayoutBox()->Style()->GetWritingDirection().IsHorizontal();
   std::optional<cc::TargetSnapAreaElementIds> ids;
-  if (event_type == event_type_names::kSnapchanged) {
-    ids = RareData()->snapchanged_target_ids_;
+  if (event_type == event_type_names::kScrollsnapchange) {
+    ids = RareData()->scrollsnapchange_target_ids_;
   } else {
     ids = RareData()->snapchanging_target_ids_;
   }
@@ -3267,9 +3268,9 @@
   return node;
 }
 
-void PaintLayerScrollableArea::SetSnapchangedTargetIds(
+void PaintLayerScrollableArea::SetScrollsnapchangeTargetIds(
     std::optional<cc::TargetSnapAreaElementIds> ids) {
-  EnsureRareData().snapchanged_target_ids_ = ids;
+  EnsureRareData().scrollsnapchange_target_ids_ = ids;
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h b/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h
index d19a85b..e15826b 100644
--- a/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h
+++ b/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h
@@ -88,8 +88,8 @@
   std::optional<cc::TargetSnapAreaElementIds> snapchanging_target_ids_;
   std::unique_ptr<cc::SnapSelectionStrategy> impl_snap_strategy_;
   // The ids of the elements that were reported as the selected snap targets
-  // along each axis during the last snapchanged event that fired.
-  std::optional<cc::TargetSnapAreaElementIds> snapchanged_target_ids_;
+  // along each axis during the last scrollsnapchange event that fired.
+  std::optional<cc::TargetSnapAreaElementIds> scrollsnapchange_target_ids_;
   // If this is a snap container, this represents the cc::ElementId of the snap
   // area (snapped to by this snap container) that is targeted[1] or contains a
   // targeted[1] element.
@@ -555,10 +555,10 @@
 
   std::optional<gfx::PointF> GetSnapPositionAndSetTarget(
       const cc::SnapSelectionStrategy& strategy) override;
-  // Functions related to firing snapchanged events.
-  void SetSnapchangedTargetIds(
+  // Functions related to firing scrollsnapchange events.
+  void SetScrollsnapchangeTargetIds(
       std::optional<cc::TargetSnapAreaElementIds>) override;
-  void UpdateSnappedTargetsAndEnqueueSnapChanged() override;
+  void UpdateSnappedTargetsAndEnqueueScrollSnapChange() override;
 
   // Functions related to firing snapchanging events.
   std::optional<cc::TargetSnapAreaElementIds> GetSnapchangingTargetIds()
@@ -720,8 +720,8 @@
   bool UsedColorSchemeScrollbarsChanged(const ComputedStyle* old_style) const;
   bool IsGlobalRootNonOverlayScroller() const;
 
-  // Get the current target for a snap event of |type| (either "snapchanged" or
-  // snapchanging) along axis |axis|.
+  // Get the current target for a snap event of |type| (either
+  // "scrollsnapchange" or snapchanging) along axis |axis|.
   Node* GetSnapEventTargetAlongAxis(const AtomicString& type,
                                     cc::SnapAxis) const override;
 
diff --git a/third_party/blink/renderer/core/permissions_policy/document_policy_features.json5 b/third_party/blink/renderer/core/permissions_policy/document_policy_features.json5
index 8c1d13c..9d1be11 100644
--- a/third_party/blink/renderer/core/permissions_policy/document_policy_features.json5
+++ b/third_party/blink/renderer/core/permissions_policy/document_policy_features.json5
@@ -57,13 +57,6 @@
       depends_on: []
     },
     {
-      name: "FontDisplay",
-      document_policy_name: "font-display-late-swap",
-      value_type: "Bool",
-      default_value: "true",
-      depends_on: ["ExperimentalPolicies"],
-    },
-    {
       // The ForceLoadAtTop policy lets pages opt-out of scrolling that
       // automatically happens on page load. This includes fragment scrolls,
       // text fragment scrolls (i.e. this provides an opt-out for the Scroll To
diff --git a/third_party/blink/renderer/core/scroll/scrollable_area.cc b/third_party/blink/renderer/core/scroll/scrollable_area.cc
index 3c8c5bc..7991298 100644
--- a/third_party/blink/renderer/core/scroll/scrollable_area.cc
+++ b/third_party/blink/renderer/core/scroll/scrollable_area.cc
@@ -1231,7 +1231,7 @@
 void ScrollableArea::OnScrollFinished(bool scroll_did_end) {
   if (GetLayoutBox()) {
     if (scroll_did_end) {
-      UpdateSnappedTargetsAndEnqueueSnapChanged();
+      UpdateSnappedTargetsAndEnqueueScrollSnapChange();
       if (RuntimeEnabledFeatures::ScrollEndEventsEnabled()) {
         if (Node* node = EventTargetNode()) {
           node->GetDocument().EnqueueScrollEndEventForNode(node);
@@ -1299,7 +1299,7 @@
 void ScrollableArea::SnapAfterLayout() {
   const cc::SnapContainerData* container_data = GetSnapContainerData();
   if (!container_data || !container_data->size()) {
-    UpdateSnappedTargetsAndEnqueueSnapChanged();
+    UpdateSnappedTargetsAndEnqueueScrollSnapChange();
     return;
   }
 
@@ -1315,7 +1315,7 @@
     base::ScopedClosureRunner on_finish) {
   std::optional<gfx::PointF> snap_point = GetSnapPositionAndSetTarget(strategy);
   if (!snap_point) {
-    UpdateSnappedTargetsAndEnqueueSnapChanged();
+    UpdateSnappedTargetsAndEnqueueScrollSnapChange();
     return false;
   }
 
@@ -1334,8 +1334,9 @@
                        IgnoreArgs<ScrollableArea::ScrollCompletionMode>(
                            on_finish.Release()))) {
     // If no scroll happens, e.g. we got here because of a layout change, we
-    // need to re-compute snapped targets and fire snapchanged if necessary.
-    UpdateSnappedTargetsAndEnqueueSnapChanged();
+    // need to re-compute snapped targets and fire scrollsnapchange if
+    // necessary.
+    UpdateSnappedTargetsAndEnqueueScrollSnapChange();
   }
   return true;
 }
@@ -1410,18 +1411,18 @@
               : offset);
 }
 
-void ScrollableArea::EnqueueSnapChangedEvent() const {
-  DCHECK(RuntimeEnabledFeatures::CSSSnapChangedEventEnabled());
+void ScrollableArea::EnqueueScrollSnapChangeEvent() const {
+  DCHECK(RuntimeEnabledFeatures::CSSScrollSnapChangeEventEnabled());
   Node* target_node = EventTargetNode();
   if (!target_node) {
     return;
   }
   Member<Node> block_target = GetSnapEventTargetAlongAxis(
-      event_type_names::kSnapchanged, cc::SnapAxis::kBlock);
+      event_type_names::kScrollsnapchange, cc::SnapAxis::kBlock);
   Member<Node> inline_target = GetSnapEventTargetAlongAxis(
-      event_type_names::kSnapchanged, cc::SnapAxis::kInline);
-  target_node->GetDocument().EnqueueSnapChangedEvent(target_node, block_target,
-                                                     inline_target);
+      event_type_names::kScrollsnapchange, cc::SnapAxis::kInline);
+  target_node->GetDocument().EnqueueScrollSnapChangeEvent(
+      target_node, block_target, inline_target);
 }
 
 void ScrollableArea::EnqueueSnapChangingEvent() const {
diff --git a/third_party/blink/renderer/core/scroll/scrollable_area.h b/third_party/blink/renderer/core/scroll/scrollable_area.h
index 1b67f2e..c046d072 100644
--- a/third_party/blink/renderer/core/scroll/scrollable_area.h
+++ b/third_party/blink/renderer/core/scroll/scrollable_area.h
@@ -580,7 +580,7 @@
   void ClearPendingScrollAnchorAdjustment();
 
   scoped_refptr<base::SingleThreadTaskRunner> GetCompositorTaskRunner();
-  void EnqueueSnapChangedEvent() const;
+  void EnqueueScrollSnapChangeEvent() const;
 
   ScrollOffset ScrollOffsetFromScrollStartData(
       const ScrollStartData& block_value,
@@ -589,9 +589,9 @@
   bool ScrollStartIsDefault() const;
   virtual bool IsApplyingScrollStart() const { return false; }
 
-  virtual void SetSnapchangedTargetIds(
+  virtual void SetScrollsnapchangeTargetIds(
       std::optional<cc::TargetSnapAreaElementIds>) {}
-  virtual void UpdateSnappedTargetsAndEnqueueSnapChanged() {}
+  virtual void UpdateSnappedTargetsAndEnqueueScrollSnapChange() {}
 
   bool ScrollOffsetIsNoop(const ScrollOffset& offset) const;
 
diff --git a/third_party/blink/renderer/core/scroll/snap_event.h b/third_party/blink/renderer/core/scroll/snap_event.h
index e510747..af2a612 100644
--- a/third_party/blink/renderer/core/scroll/snap_event.h
+++ b/third_party/blink/renderer/core/scroll/snap_event.h
@@ -15,10 +15,10 @@
 namespace blink {
 
 // This class implements the SnapEvent interface for scroll-snap-related
-// JavaScript events, snapchanged and snapchanging.
+// JavaScript events, scrollsnapchange and snapchanging.
 // SnapEvents are sent to a scroller when it snaps to a different element from
 // the element to which it was previously snapped along either axis.
-// https://drafts.csswg.org/css-scroll-snap-2/#snapchanged-and-snapchanging
+// https://drafts.csswg.org/css-scroll-snap-2/#scrollsnapchange-and-snapchanging
 class SnapEvent : public Event {
   DEFINE_WRAPPERTYPEINFO();
 
diff --git a/third_party/blink/renderer/modules/mediastream/webaudio_media_stream_audio_sink_test.cc b/third_party/blink/renderer/modules/mediastream/webaudio_media_stream_audio_sink_test.cc
index 0eed0e4..a77b1d7 100644
--- a/third_party/blink/renderer/modules/mediastream/webaudio_media_stream_audio_sink_test.cc
+++ b/third_party/blink/renderer/modules/mediastream/webaudio_media_stream_audio_sink_test.cc
@@ -169,6 +169,7 @@
   // 1. Source preparation.
   std::unique_ptr<media::AudioBus> source_bus =
       media::AudioBus::Create(source_params_);
+  source_bus->Zero();
 
   // 2. Sink preparation.
 
diff --git a/third_party/blink/renderer/modules/shared_storage/shared_storage_worklet_unittest.cc b/third_party/blink/renderer/modules/shared_storage/shared_storage_worklet_unittest.cc
index c48ad1a6..9b72a6a 100644
--- a/third_party/blink/renderer/modules/shared_storage/shared_storage_worklet_unittest.cc
+++ b/third_party/blink/renderer/modules/shared_storage/shared_storage_worklet_unittest.cc
@@ -2567,7 +2567,7 @@
 
   RunResult run_result{run_future.Get<0>(), run_future.Get<1>()};
   EXPECT_FALSE(run_result.success);
-  EXPECT_EQ(run_result.error_message, "Error: Internal error 12345");
+  EXPECT_EQ(run_result.error_message, "OperationError: Internal error 12345");
 
   EXPECT_EQ(test_client_->observed_console_log_messages_.size(), 0u);
 }
@@ -2660,7 +2660,7 @@
 
   RunResult run_result{run_future.Get<0>(), run_future.Get<1>()};
   EXPECT_FALSE(run_result.success);
-  EXPECT_EQ(run_result.error_message, "Error: Internal error 12345");
+  EXPECT_EQ(run_result.error_message, "OperationError: Internal error 12345");
 
   EXPECT_EQ(test_client_->observed_console_log_messages_.size(), 1u);
 }
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index 102fc998..b60f692a 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -984,10 +984,15 @@
       status: "stable",
     },
     {
+      // https://drafts.csswg.org/css-scroll-snap-2/#scrollsnapchange
+      name: "CSSScrollSnapChangeEvent",
+      status: "experimental",
+    },
+    {
       // https://drafts.csswg.org/css-scroll-snap-2/#snap-events
       name: "CSSScrollSnapEvents",
       status: "experimental",
-      implied_by: ["CSSSnapChangedEvent", "CSSSnapChangingEvent"],
+      implied_by: ["CSSScrollSnapChangeEvent", "CSSSnapChangingEvent"],
     },
     {
       // https://drafts.csswg.org/css-scroll-snap-2#scroll-start
@@ -1020,11 +1025,6 @@
       name: "CSSSizingKeywordAnimation",
     },
     {
-      // https://drafts.csswg.org/css-scroll-snap-2/#snapchanged
-      name: "CSSSnapChangedEvent",
-      status: "experimental",
-    },
-    {
       // https://drafts.csswg.org/css-scroll-snap-2/#snapchanging
       name: "CSSSnapChangingEvent",
       status: "experimental",
@@ -2606,6 +2606,15 @@
       status: "stable",
     },
     {
+      // New implementation of text-size-adjust that applies during style rather
+      // than via the text autosizer. This also includes changes such as
+      // directly using percentage values without heuristics, and fully
+      // disabling automatic size adjustments when non-auto values are used.
+      // See: https://crbug.com/340389272.
+      name: "NewTextSizeAdjust",
+      status: "experimental",
+    },
+    {
       name: "NextSiblingPositionUseNextCandidate",
       status: "stable",
     },
diff --git a/third_party/blink/renderer/platform/widget/input/main_thread_event_queue.cc b/third_party/blink/renderer/platform/widget/input/main_thread_event_queue.cc
index 5c13e333..5ba4953 100644
--- a/third_party/blink/renderer/platform/widget/input/main_thread_event_queue.cc
+++ b/third_party/blink/renderer/platform/widget/input/main_thread_event_queue.cc
@@ -322,26 +322,26 @@
   std::unique_ptr<cc::EventMetrics> metrics_;
 };
 
-MainThreadEventQueue::SharedState::SharedState()
-    : sent_main_frame_request_(false), sent_post_task_(false) {}
-
-MainThreadEventQueue::SharedState::~SharedState() {}
-
 MainThreadEventQueue::MainThreadEventQueue(
     MainThreadEventQueueClient* client,
-    const scoped_refptr<base::SingleThreadTaskRunner>& main_task_runner,
+    const scoped_refptr<base::SingleThreadTaskRunner>& compositor_task_runner,
+    scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
     scoped_refptr<scheduler::WidgetScheduler> widget_scheduler,
     bool allow_raf_aligned_input)
     : client_(client),
       allow_raf_aligned_input_(allow_raf_aligned_input),
-      main_task_runner_(main_task_runner),
+      main_task_runner_(std::move(main_task_runner)),
       widget_scheduler_(std::move(widget_scheduler)) {
   DCHECK(widget_scheduler_);
   raf_fallback_timer_ = std::make_unique<base::OneShotTimer>();
-  raf_fallback_timer_->SetTaskRunner(main_task_runner);
+  raf_fallback_timer_->SetTaskRunner(main_task_runner_);
 
   event_predictor_ = std::make_unique<InputEventPrediction>(
       base::FeatureList::IsEnabled(blink::features::kResamplingInputEvents));
+
+#if DCHECK_IS_ON()
+  compositor_task_runner_ = compositor_task_runner;
+#endif
 }
 
 MainThreadEventQueue::~MainThreadEventQueue() {}
@@ -414,27 +414,32 @@
       touch_event->dispatch_type =
           WebInputEvent::DispatchType::kListenersNonBlockingPassive;
     }
-    if (touch_event->GetType() == WebInputEvent::Type::kTouchStart)
-      last_touch_start_forced_nonblocking_due_to_fling_ = false;
 
+    bool& last_touch_start_forced_nonblocking_due_to_fling =
+        GetCompositorThreadOnly()
+            .last_touch_start_forced_nonblocking_due_to_fling;
+    if (touch_event->GetType() == WebInputEvent::Type::kTouchStart) {
+      last_touch_start_forced_nonblocking_due_to_fling = false;
+    }
     if (touch_event->touch_start_or_first_touch_move &&
         touch_event->dispatch_type == WebInputEvent::DispatchType::kBlocking) {
       // If the touch start is forced to be passive due to fling, its following
       // touch move should also be passive.
       if (ack_result ==
               mojom::blink::InputEventResultState::kSetNonBlockingDueToFling ||
-          last_touch_start_forced_nonblocking_due_to_fling_) {
+          last_touch_start_forced_nonblocking_due_to_fling) {
         touch_event->dispatch_type =
             WebInputEvent::DispatchType::kListenersForcedNonBlockingDueToFling;
         is_blocking = false;
-        last_touch_start_forced_nonblocking_due_to_fling_ = true;
+        last_touch_start_forced_nonblocking_due_to_fling = true;
       }
     }
 
     // If the event is non-cancelable ACK it right away.
     if (is_blocking &&
-        touch_event->dispatch_type != WebInputEvent::DispatchType::kBlocking)
+        touch_event->dispatch_type != WebInputEvent::DispatchType::kBlocking) {
       is_blocking = false;
+    }
   }
 
   if (is_wheel) {
@@ -838,7 +843,8 @@
   needs_unbuffered_input_for_debugger_ = unbuffered;
 }
 
-void MainThreadEventQueue::HasPointerRawUpdateEventHandlers(bool has_handlers) {
+void MainThreadEventQueue::SetHasPointerRawUpdateEventHandlers(
+    bool has_handlers) {
   has_pointerrawupdate_handlers_ = has_handlers;
 }
 
@@ -927,4 +933,12 @@
   return main_thread_only_;
 }
 
+MainThreadEventQueue::CompositorThreadOnly&
+MainThreadEventQueue::GetCompositorThreadOnly() {
+#if DCHECK_IS_ON()
+  DCHECK(compositor_task_runner_->BelongsToCurrentThread());
+#endif
+  return compositor_thread_only_;
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/widget/input/main_thread_event_queue.h b/third_party/blink/renderer/platform/widget/input/main_thread_event_queue.h
index c47f092..4c5b8e6a 100644
--- a/third_party/blink/renderer/platform/widget/input/main_thread_event_queue.h
+++ b/third_party/blink/renderer/platform/widget/input/main_thread_event_queue.h
@@ -100,7 +100,8 @@
  public:
   MainThreadEventQueue(
       MainThreadEventQueueClient* client,
-      const scoped_refptr<base::SingleThreadTaskRunner>& main_task_runner,
+      const scoped_refptr<base::SingleThreadTaskRunner>& compositor_task_runner,
+      scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
       scoped_refptr<scheduler::WidgetScheduler> widget_scheduler,
       bool allow_raf_aligned_input);
   MainThreadEventQueue(const MainThreadEventQueue&) = delete;
@@ -125,7 +126,7 @@
   void SetNeedsLowLatency(bool low_latency);
   void SetNeedsUnbufferedInputForDebugger(bool unbuffered);
 
-  void HasPointerRawUpdateEventHandlers(bool has_handlers);
+  void SetHasPointerRawUpdateEventHandlers(bool has_handlers);
 
   // Request unbuffered input events until next pointerup.
   void RequestUnbufferedInputEvents();
@@ -174,6 +175,12 @@
       const WebInputEvent& dispatched_event,
       mojom::blink::InputEventResultState ack_result);
 
+  friend class QueuedWebInputEvent;
+  friend class MainThreadEventQueueTest;
+  friend class MainThreadEventQueueInitializationTest;
+  raw_ptr<MainThreadEventQueueClient> client_;
+  const bool allow_raf_aligned_input_;
+
   // Contains data that are read and written on the main thread only.
   struct MainThreadOnly {
     bool blocking_touch_start_not_consumed = false;
@@ -181,13 +188,11 @@
   } main_thread_only_;
   MainThreadOnly& GetMainThreadOnly();
 
-  friend class QueuedWebInputEvent;
-  friend class MainThreadEventQueueTest;
-  friend class MainThreadEventQueueInitializationTest;
-  raw_ptr<MainThreadEventQueueClient> client_;
-  const bool allow_raf_aligned_input_;
-  bool last_touch_start_forced_nonblocking_due_to_fling_ = false;
-  bool has_pointerrawupdate_handlers_ = false;
+  // Contains data that are read and written on the compositor thread only.
+  struct CompositorThreadOnly {
+    bool last_touch_start_forced_nonblocking_due_to_fling = false;
+  } compositor_thread_only_;
+  CompositorThreadOnly& GetCompositorThreadOnly();
 
   // These variables are read on the compositor thread but are
   // written on the main thread, so we use atomics to keep them
@@ -195,26 +200,24 @@
   // is best effort. It is fine that the compositor executes a slightly
   // different path for events in flight while these variables are
   // mutated via the main thread.
+  std::atomic<bool> has_pointerrawupdate_handlers_ = false;
   std::atomic<bool> needs_low_latency_ = false;
   std::atomic<bool> needs_unbuffered_input_for_debugger_ = false;
   std::atomic<bool> needs_low_latency_until_pointer_up_ = false;
 
   // Contains data to be shared between main thread and compositor thread.
   struct SharedState {
-    SharedState();
-    ~SharedState();
-
     MainThreadEventQueueTaskList events_;
     // A BeginMainFrame has been requested but not received yet.
-    bool sent_main_frame_request_;
+    bool sent_main_frame_request_ = false;
     // A PostTask to the main thread has been sent but not executed yet.
-    bool sent_post_task_;
+    bool sent_post_task_ = false;
     base::TimeTicks last_async_touch_move_timestamp_;
   };
 
   // Lock used to serialize |shared_state_|.
   base::Lock shared_state_lock_;
-  SharedState shared_state_;
+  SharedState shared_state_ GUARDED_BY(shared_state_lock_);
 
   scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_;
   scoped_refptr<scheduler::WidgetScheduler> widget_scheduler_;
@@ -234,6 +237,10 @@
   // scroll events to go to main.  See CursorControlHandler (impl-side filter)
   // and WebFrameWidgetImpl::WillHandleGestureEvent (main thread consumer).
   bool cursor_control_in_progress_ = false;
+
+#if DCHECK_IS_ON()
+  scoped_refptr<base::SingleThreadTaskRunner> compositor_task_runner_;
+#endif
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/widget/input/main_thread_event_queue_unittest.cc b/third_party/blink/renderer/platform/widget/input/main_thread_event_queue_unittest.cc
index 7157da11c..32dc6918 100644
--- a/third_party/blink/renderer/platform/widget/input/main_thread_event_queue_unittest.cc
+++ b/third_party/blink/renderer/platform/widget/input/main_thread_event_queue_unittest.cc
@@ -247,8 +247,8 @@
   }
 
   void SetUp() override {
-    queue_ = new MainThreadEventQueue(this, main_task_runner_,
-                                      widget_scheduler_, true);
+    queue_ = new MainThreadEventQueue(
+        this, main_task_runner_, main_task_runner_, widget_scheduler_, true);
     queue_->ClearRafFallbackTimerForTesting();
   }
 
@@ -283,7 +283,8 @@
   }
 
   bool last_touch_start_forced_nonblocking_due_to_fling() {
-    return queue_->last_touch_start_forced_nonblocking_due_to_fling_;
+    return queue_->compositor_thread_only_
+        .last_touch_start_forced_nonblocking_due_to_fling;
   }
 
   void RunPendingTasksWithSimulatedRaf() {
@@ -1808,7 +1809,7 @@
 }
 
 TEST_P(MainThreadEventQueueTest, PointerEventsCoalescing) {
-  queue_->HasPointerRawUpdateEventHandlers(true);
+  queue_->SetHasPointerRawUpdateEventHandlers(true);
   WebMouseEvent mouse_move = SyntheticWebMouseEventBuilder::Build(
       WebInputEvent::Type::kMouseMove, 10, 10, 0);
   SyntheticWebTouchEvent touch_move;
@@ -1853,14 +1854,14 @@
   EXPECT_EQ(0u, event_queue().size());
   EXPECT_FALSE(needs_main_frame_);
 
-  queue_->HasPointerRawUpdateEventHandlers(true);
+  queue_->SetHasPointerRawUpdateEventHandlers(true);
   HandleEvent(mouse_move, blink::mojom::InputEventResultState::kSetNonBlocking);
   EXPECT_EQ(2u, event_queue().size());
   RunPendingTasksWithSimulatedRaf();
   EXPECT_EQ(0u, event_queue().size());
   EXPECT_FALSE(needs_main_frame_);
 
-  queue_->HasPointerRawUpdateEventHandlers(false);
+  queue_->SetHasPointerRawUpdateEventHandlers(false);
   SyntheticWebTouchEvent touch_move;
   touch_move.PressPoint(10, 10);
   touch_move.MovePoint(0, 50, 50);
@@ -1870,7 +1871,7 @@
   EXPECT_EQ(0u, event_queue().size());
   EXPECT_FALSE(needs_main_frame_);
 
-  queue_->HasPointerRawUpdateEventHandlers(true);
+  queue_->SetHasPointerRawUpdateEventHandlers(true);
   HandleEvent(touch_move, blink::mojom::InputEventResultState::kSetNonBlocking);
   EXPECT_EQ(2u, event_queue().size());
   RunPendingTasksWithSimulatedRaf();
@@ -1931,7 +1932,7 @@
               DidHandleInputEventOnMainThread(testing::_, testing::_))
       .Times(0);
 
-  queue_->HasPointerRawUpdateEventHandlers(true);
+  queue_->SetHasPointerRawUpdateEventHandlers(true);
 
   // Inject two mouse move events. For each event injected, there will be two
   // events in the queue. One for kPointerRawUpdate and another kMouseMove
diff --git a/third_party/blink/renderer/platform/widget/input/widget_input_handler_manager.cc b/third_party/blink/renderer/platform/widget/input/widget_input_handler_manager.cc
index bd3ca493..b2849a8 100644
--- a/third_party/blink/renderer/platform/widget/input/widget_input_handler_manager.cc
+++ b/third_party/blink/renderer/platform/widget/input/widget_input_handler_manager.cc
@@ -235,11 +235,6 @@
       frame_widget_input_handler_(std::move(frame_widget_input_handler)),
       widget_scheduler_(std::move(widget_scheduler)),
       widget_is_embedded_(widget_ && widget_->is_embedded()),
-      input_event_queue_(base::MakeRefCounted<MainThreadEventQueue>(
-          this,
-          widget_scheduler_->InputTaskRunner(),
-          widget_scheduler_,
-          /*allow_raf_aligned_input=*/!never_composited)),
       main_thread_task_runner_(widget_scheduler_->InputTaskRunner()),
       compositor_thread_default_task_runner_(
           compositor_thread_scheduler
@@ -249,6 +244,12 @@
           compositor_thread_scheduler
               ? compositor_thread_scheduler->InputTaskRunner()
               : nullptr),
+      input_event_queue_(base::MakeRefCounted<MainThreadEventQueue>(
+          this,
+          InputThreadTaskRunner(),
+          widget_scheduler_->InputTaskRunner(),
+          widget_scheduler_,
+          /*allow_raf_aligned_input=*/!never_composited)),
       allow_scroll_resampling_(allow_scroll_resampling) {
 #if BUILDFLAG(IS_ANDROID)
   if (compositor_thread_default_task_runner_) {
diff --git a/third_party/blink/renderer/platform/widget/input/widget_input_handler_manager.h b/third_party/blink/renderer/platform/widget/input/widget_input_handler_manager.h
index 5eb9c020..1fab2a6 100644
--- a/third_party/blink/renderer/platform/widget/input/widget_input_handler_manager.h
+++ b/third_party/blink/renderer/platform/widget/input/widget_input_handler_manager.h
@@ -318,12 +318,12 @@
   mojo::SharedRemote<mojom::blink::WidgetInputHandlerHost> host_;
 
   // Any thread can access these variables.
-  scoped_refptr<MainThreadEventQueue> input_event_queue_;
   scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner_;
   scoped_refptr<base::SingleThreadTaskRunner>
       compositor_thread_default_task_runner_;
   scoped_refptr<base::SingleThreadTaskRunner>
       compositor_thread_input_blocking_task_runner_;
+  scoped_refptr<MainThreadEventQueue> input_event_queue_;
 
   // The touch action that InputHandlerProxy has asked us to allow. This should
   // only be accessed on the compositor thread!
diff --git a/third_party/blink/tools/commit_stats/affiliations.json5 b/third_party/blink/tools/commit_stats/affiliations.json5
index 4e9a151..7efb285 100644
--- a/third_party/blink/tools/commit_stats/affiliations.json5
+++ b/third_party/blink/tools/commit_stats/affiliations.json5
@@ -30,6 +30,12 @@
       { start: "2019-05-01", domain: "microsoft.com" },
     ]
   },
+  "joone@chromium.org": {
+    affiliations: [
+      { start: "2024-04-29", domain: "microsoft.com" },
+      { start: "2022-06-01", domain: "joone.net" },
+    ]
+  },
   "kzar@chromium.org": {
     affiliations: [
       { start: "2021-11-29", domain: "kzar.co.uk" },
diff --git a/third_party/blink/web_tests/NeverFixTests b/third_party/blink/web_tests/NeverFixTests
index 9e154f0f..f38624f 100644
--- a/third_party/blink/web_tests/NeverFixTests
+++ b/third_party/blink/web_tests/NeverFixTests
@@ -1604,6 +1604,9 @@
 # Tests that require support for <meta name="viewport">
 crbug.com/1343368 external/wpt/css/mediaqueries/viewport-script-dynamic.html [ Skip ]
 
+# Tests that require the text autosizing setting.
+crbug.com/340389272 wpt_internal/css/css-size-adjust/* [ Skip ]
+
 # Predefined counter styles that we support a larger range than the spec.
 external/wpt/css/css-counter-styles/armenian/css3-counter-styles-008.html [ Skip ]
 external/wpt/css/css-counter-styles/hebrew/css3-counter-styles-016a.html [ Skip ]
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 76dba9c..7fa4a7d 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -5109,6 +5109,25 @@
 # Sheriff 2021-12-22
 crbug.com/1283295 [ Mac ] fast/text-autosizing/hackernews-comments.html [ Failure Pass ]
 
+# These virtual suites enable text autosizing and should pass.
+crbug.com/340389272 virtual/new-text-size-adjust/wpt_internal/css/css-size-adjust/* [ Pass ]
+crbug.com/340389272 virtual/new-text-size-adjust-with-os-font-scale/wpt_internal/css/css-size-adjust/* [ Pass ]
+crbug.com/340389272 virtual/old-text-size-adjust/wpt_internal/css/css-size-adjust/* [ Pass ]
+crbug.com/340389272 virtual/old-text-size-adjust-with-os-font-scale/wpt_internal/css/css-size-adjust/* [ Pass ]
+
+# These tests fail without the new text-size-adjust implementation.
+crbug.com/340389272 virtual/old-text-size-adjust/wpt_internal/css/css-size-adjust/text-size-adjust-affects-font-size-with-narrow-page.html [ Failure ]
+crbug.com/340389272 virtual/old-text-size-adjust-with-os-font-scale/wpt_internal/css/css-size-adjust/text-size-adjust-affects-font-size-with-narrow-page.html [ Failure ]
+crbug.com/340389272 virtual/old-text-size-adjust-with-os-font-scale/wpt_internal/css/css-size-adjust/text-size-adjust-none.html [ Failure ]
+crbug.com/340389272 virtual/old-text-size-adjust/wpt_internal/css/css-size-adjust/text-size-adjust-affects-font-size-large.html [ Failure ]
+crbug.com/340389272 virtual/old-text-size-adjust-with-os-font-scale/wpt_internal/css/css-size-adjust/text-size-adjust-affects-font-size-large.html [ Failure ]
+
+# These tests fail because the old text size adjust wasn't applied if the page didn't run the autosizer.
+crbug.com/340389272 virtual/old-text-size-adjust/wpt_internal/css/css-size-adjust/text-size-adjust-affects-font-size-nested-change.html [ Failure ]
+crbug.com/340389272 virtual/old-text-size-adjust/wpt_internal/css/css-size-adjust/text-size-adjust-affects-font-size-nested.html [ Failure ]
+crbug.com/340389272 virtual/old-text-size-adjust/wpt_internal/css/css-size-adjust/text-size-adjust-affects-font-size.html [ Failure ]
+crbug.com/340389272 virtual/old-text-size-adjust/wpt_internal/css/css-size-adjust/webkit-text-size-adjust-affects-font-size.html [ Failure ]
+
 # Sheriff 2022-01-04
 crbug.com/1283865 external/wpt/webmessaging/without-ports/020.html [ Failure Pass ]
 
@@ -5291,6 +5310,7 @@
 crbug.com/1511354 virtual/stylable-select-disabled/external/wpt/html/semantics/forms/the-select-element/stylable-select/select-icon-color.tentative.html [ Failure ]
 crbug.com/1511354 virtual/stylable-select-disabled/external/wpt/html/semantics/forms/the-select-element/stylable-select/select-appearance-dark-mode.tentative.html [ Failure ]
 crbug.com/1511354 virtual/stylable-select-disabled/external/wpt/html/semantics/forms/the-select-element/stylable-select/select-accessibility-minimum-target-size.tentative.html [ Failure ]
+crbug.com/1511354 virtual/stylable-select-disabled/external/wpt/html/semantics/forms/the-select-element/stylable-select/select-open-invalidation.tentative.html [ Failure ]
 
 # Sheriff 2022-04-21
 crbug.com/1318318 external/wpt/fetch/private-network-access/service-worker-background-fetch.tentative.https.window.html [ Failure Pass Timeout ]
diff --git a/third_party/blink/web_tests/VirtualTestSuites b/third_party/blink/web_tests/VirtualTestSuites
index dd78ef6e..96a0a1f 100644
--- a/third_party/blink/web_tests/VirtualTestSuites
+++ b/third_party/blink/web_tests/VirtualTestSuites
@@ -2223,6 +2223,54 @@
     "expires": "Dec 1, 2023"
   },
   {
+    "prefix": "new-text-size-adjust",
+    "owners": ["pdr@chromium.org"],
+    "platforms": ["Linux", "Mac"],
+    "bases": ["wpt_internal/css/css-size-adjust",
+              "external/wpt/css/css-size-adjust"],
+    "args": ["--enable-threaded-compositing",
+             "--enable-blink-features=NewTextSizeAdjust",
+             "--enable-viewport",
+             "--blink-settings=textAutosizingEnabled=true,viewportMetaEnabled=true"],
+    "expires": "Jan 1, 2026"
+  },
+  {
+    "prefix": "new-text-size-adjust-with-os-font-scale",
+    "owners": ["pdr@chromium.org"],
+    "platforms": ["Linux", "Mac"],
+    "bases": ["wpt_internal/css/css-size-adjust",
+              "external/wpt/css/css-size-adjust"],
+    "args": ["--enable-threaded-compositing",
+             "--enable-blink-features=NewTextSizeAdjust",
+             "--enable-viewport",
+             "--blink-settings=textAutosizingEnabled=true,accessibilityFontScaleFactor=2,deviceScaleAdjustment=3,viewportMetaEnabled=true"],
+    "expires": "Jan 1, 2026"
+  },
+  {
+    "prefix": "old-text-size-adjust",
+    "owners": ["pdr@chromium.org"],
+    "platforms": ["Linux", "Mac"],
+    "bases": ["wpt_internal/css/css-size-adjust",
+              "external/wpt/css/css-size-adjust"],
+    "args": ["--enable-threaded-compositing",
+             "--disable-blink-features=NewTextSizeAdjust",
+             "--enable-viewport",
+             "--blink-settings=textAutosizingEnabled=true,viewportMetaEnabled=true"],
+    "expires": "Jan 1, 2026"
+  },
+  {
+    "prefix": "old-text-size-adjust-with-os-font-scale",
+    "owners": ["pdr@chromium.org"],
+    "platforms": ["Linux", "Mac"],
+    "bases": ["wpt_internal/css/css-size-adjust",
+              "external/wpt/css/css-size-adjust"],
+    "args": ["--enable-threaded-compositing",
+             "--disable-blink-features=NewTextSizeAdjust",
+             "--enable-viewport",
+             "--blink-settings=textAutosizingEnabled=true,accessibilityFontScaleFactor=2,deviceScaleAdjustment=3,viewportMetaEnabled=true"],
+    "expires": "Jan 1, 2026"
+  },
+  {
     "owners": [
       "haoliuk@chromium.org"
     ],
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
index 679c714f..41c9d09 100644
--- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
+++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
@@ -464116,36 +464116,36 @@
        }
       ]
      ],
-     "snapchanged": {
-      "snapchanged-after-layout-change.tentative.html": [
+     "scrollsnapchange": {
+      "scrollsnapchange-after-layout-change.tentative.html": [
        "a3ba05fdf5e9ffa42ab5689349e33b3c9a0aee15",
        [
         null,
         {}
        ]
       ],
-      "snapchanged-on-interrupted-scroll.tentative.html": [
+      "scrollsnapchange-on-interrupted-scroll.tentative.html": [
        "a1d5259451e970c8a0c9418aa3f8629b7c95f4df",
        [
         null,
         {}
        ]
       ],
-      "snapchanged-on-programmatic-root-scroll.tentative.html": [
+      "scrollsnapchange-on-programmatic-root-scroll.tentative.html": [
        "836036942206172262a5f3ea91c1dae26c82e26a",
        [
         null,
         {}
        ]
       ],
-      "snapchanged-on-programmatic-scroll.tentative.html": [
+      "scrollsnapchange-on-programmatic-scroll.tentative.html": [
        "2b2e6a77c581ffe889fa91ab9f6a452e364ab236",
        [
         null,
         {}
        ]
       ],
-      "snapchanged-on-user-root-scroll.tentative.html": [
+      "scrollsnapchange-on-user-root-scroll.tentative.html": [
        "a59d9c58590bf1096b48542a72bdd4c03f243192",
        [
         null,
@@ -464154,7 +464154,7 @@
         }
        ]
       ],
-      "snapchanged-on-user-scroll.tentative.html": [
+      "scrollsnapchange-on-user-scroll.tentative.html": [
        "d2c2789c88a3daae45ae111d949ab4e76e9347e8",
        [
         null,
@@ -464163,14 +464163,14 @@
         }
        ]
       ],
-      "snapchanged-same-targets-after-layout-changed.html": [
+      "scrollsnapchange-same-targets-after-layout-changed.html": [
        "4d16bd80a3a270a6b008df2c45640b7d31494987",
        [
         null,
         {}
        ]
       ],
-      "snapchanged-scrolling-non-snapping-axis.tentative.html": [
+      "scrollsnapchange-scrolling-non-snapping-axis.tentative.html": [
        "e39fc0c44ee34c5a3ea56949a224e5186d9ebb2a",
        [
         null,
@@ -464179,7 +464179,7 @@
         }
        ]
       ],
-      "snapchanged-with-proximity-strictness.tentative.html": [
+      "scrollsnapchange-with-proximity-strictness.tentative.html": [
        "96cab337398654fd778eb42b6a84167b72c9363d",
        [
         null,
diff --git a/third_party/blink/web_tests/external/wpt/css/css-scroll-snap-2/resources/common.js b/third_party/blink/web_tests/external/wpt/css/css-scroll-snap-2/resources/common.js
index d95b605..b2547af 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-scroll-snap-2/resources/common.js
+++ b/third_party/blink/web_tests/external/wpt/css/css-scroll-snap-2/resources/common.js
@@ -1,6 +1,6 @@
 function checkSnapEventSupport(event_type) {
-  if (event_type == "snapchanged") {
-    assert_true(window.onsnapchanged !== undefined, "snapchanged not supported");
+  if (event_type == "scrollsnapchange") {
+    assert_true(window.onscrollsnapchange !== undefined, "scrollsnapchange not supported");
   } else if (event_type == "snapchanging") {
     assert_true(window.onsnapchanging !== undefined, "snapchanging not supported");
   } else {
@@ -48,8 +48,8 @@
     "horizontal scroll offset mismatch.");
 }
 
-async function test_snapchanged(test, test_data, use_onsnap_member = false) {
-  await test_snap_event(test, test_data, "snapchanged", use_onsnap_member);
+async function test_scrollsnapchange(test, test_data, use_onsnap_member = false) {
+  await test_snap_event(test, test_data, "scrollsnapchange", use_onsnap_member);
 }
 
 function waitForEventUntil(event_target, event_type, wait_until,
@@ -63,7 +63,7 @@
       if (event_type === "snapchanging") {
         event_target.onsnapchanging = listener;
       } else {
-        event_target.onsnapchanged = listener;
+        event_target.onscrollsnapchange = listener;
       }
     } else {
       event_target.addEventListener(event_type, listener);
@@ -73,7 +73,7 @@
         if (event_type === "snapchanging") {
           event_target.onsnapchanging = null;
         } else {
-          event_target.onsnapchanged = null;
+          event_target.onscrollsnapchange = null;
         }
       } else {
         event_target.removeEventListener(event_type, listener);
@@ -112,8 +112,8 @@
                                    use_onsnap_member);
 }
 
-function waitForSnapChangedEvent(event_target, scroll_happens = true) {
-  return waitForSnapEvent(event_target, "snapchanged", scroll_happens);
+function waitForScrollSnapChangeEvent(event_target, scroll_happens = true) {
+  return waitForSnapEvent(event_target, "scrollsnapchange", scroll_happens);
 }
 
 function getScrollbarToScrollerRatio(scroller) {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-scroll-snap-2/resources/user-scroll-common.js b/third_party/blink/web_tests/external/wpt/css/css-scroll-snap-2/resources/user-scroll-common.js
index 07c1428..1728ff568b 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-scroll-snap-2/resources/user-scroll-common.js
+++ b/third_party/blink/web_tests/external/wpt/css/css-scroll-snap-2/resources/user-scroll-common.js
@@ -1,8 +1,8 @@
-// Helper functions for snapchanged-on-user-* tests.
+// Helper functions for scrollsnapchange-on-user-* tests.
 
 // This performs a touch scroll on |scroller| using the coordinates provided
 // in |start_pos| and |end_pos|.
-// It is meant for use in snapchanged & snapchanging tests for triggering snap
+// It is meant for use in scrollsnapchange & snapchanging tests for triggering snap
 // events when touch scrolling from |start_pos| to |end_pos|.
 function snap_event_touch_scroll_helper(start_pos, end_pos) {
   return new test_driver.Actions()
@@ -62,8 +62,8 @@
   assert_equals(scroller.scrollLeft, 0, "scroller snaps back to the left");
 }
 
-async function test_no_snapchanged(t, scroller, delta) {
-  await test_no_snap_event(t, scroller, delta, "snapchanged");
+async function test_no_scrollsnapchange(t, scroller, delta) {
+  await test_no_snap_event(t, scroller, delta, "scrollsnapchange");
 }
 
 async function test_no_snapchanging(t, scroller, delta) {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-scroll-snap-2/snap-events-with-pseudo-target.tentative.html b/third_party/blink/web_tests/external/wpt/css/css-scroll-snap-2/snap-events-with-pseudo-target.tentative.html
index baa3efc..df4913d 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-scroll-snap-2/snap-events-with-pseudo-target.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-scroll-snap-2/snap-events-with-pseudo-target.tentative.html
@@ -73,8 +73,8 @@
       };
 
       promise_test(async (t) => {
-        await test_snapchanged(t, test_data);
-      }, "snapTarget for snapchanged is the owning element when a snap area " +
+        await test_scrollsnapchange(t, test_data);
+      }, "snapTarget for scrollsnapchange is the owning element when a snap area " +
          "belongs to a pseudo-element");
 
       promise_test(async (t) => {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-scroll-snap-2/snapchanged/snapchanged-after-layout-change.tentative.html b/third_party/blink/web_tests/external/wpt/css/css-scroll-snap-2/snapchanged/snapchanged-after-layout-change.tentative.html
index a3ba05fd..7f4ec2f 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-scroll-snap-2/snapchanged/snapchanged-after-layout-change.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-scroll-snap-2/snapchanged/snapchanged-after-layout-change.tentative.html
@@ -56,7 +56,7 @@
     }
 
     async function setup(t) {
-      checkSnapEventSupport("snapchanged");
+      checkSnapEventSupport("scrollsnapchange");
       await reset(t);
       await waitForCompositorCommit();
       assert_equals(scroller.scrollTop, 0, "test precondition: scroller " +
@@ -77,17 +77,17 @@
       // We are just below the inner snap area. Increase its height so that it
       // is larger than the snapport and straddled by the snapport. Verify
       // that we snap to its bottom.
-      let snapchanged_promise = waitForSnapChangedEvent(scroller);
+      let scrollsnapchange_promise = waitForScrollSnapChangeEvent(scroller);
       inner_snap_area.style.height =
         `${scroller.clientHeight + inner_snap_area.clientHeight - 10}px`;
-      const evt = await snapchanged_promise;
+      const evt = await scrollsnapchange_promise;
       assertSnapEvent(evt, { block: inner_snap_area, inline: null });
       target_snap_position = inner_snap_area.offsetTop +
           inner_snap_area.offsetHeight - scroller.clientHeight;
       assert_equals(scroller.scrollTop, target_snap_position,
           "scroller snaps to the bottom of the smaller snap area (which is " +
           "now covering).");
-    }, "snapchanged fires after snap area is snapped to upon layout change.");
+    }, "scrollsnapchange fires after snap area is snapped to upon layout change.");
 
     promise_test(async (t) => {
       await setup(t);
@@ -103,43 +103,43 @@
       // We are just below the inner snap area. Increase its height so that it
       // is larger than the snapport making the current scroll position
       // a valid covering position within the inner snap area.
-      let snapchanged_promise = waitForSnapChangedEvent(scroller, false);
+      let scrollsnapchange_promise = waitForScrollSnapChangeEvent(scroller, false);
       inner_snap_area.style.height =
           `${scroller.clientHeight + inner_snap_area.clientHeight + 10}px`;
-      const evt = await snapchanged_promise;
+      const evt = await scrollsnapchange_promise;
       assertSnapEvent(evt, { block: inner_snap_area, inline: null });
       assert_equals(scroller.scrollTop, target_snap_position,
           "scroller maintains offset which is now covering within inner area");
-    }, "snapchanged fires after snap area is snapped to upon layout change " +
+    }, "scrollsnapchange fires after snap area is snapped to upon layout change " +
        "without scroll.");
 
     promise_test(async(t) => {
       await setup(t);
       await waitForCompositorCommit();
-      let snapchanged_promise = waitForSnapChangedEvent(scroller, false);
+      let scrollsnapchange_promise = waitForScrollSnapChangeEvent(scroller, false);
       scroller.style.scrollSnapType = "none";
-      let evt = await snapchanged_promise;
+      let evt = await scrollsnapchange_promise;
       assertSnapEvent(evt, { block: null, inline: null });
-      snapchanged_promise = waitForSnapChangedEvent(scroller, false);
+      scrollsnapchange_promise = waitForScrollSnapChangeEvent(scroller, false);
       scroller.style.scrollSnapType = "y mandatory";
-      evt = await snapchanged_promise;
+      evt = await scrollsnapchange_promise;
       assertSnapEvent(evt, { block: outer_snap_area, inline: null });
-    }, "snapchanged fires when container stops snapping");
+    }, "scrollsnapchange fires when container stops snapping");
 
     promise_test(async(t) => {
       await setup(t);
       await waitForCompositorCommit();
-      let snapchanged_promise = waitForSnapChangedEvent(scroller, false);
+      let scrollsnapchange_promise = waitForScrollSnapChangeEvent(scroller, false);
       inner_snap_area.style.scrollSnapAlign = "none";
       outer_snap_area.style.scrollSnapAlign = "none";
-      let evt = await snapchanged_promise;
+      let evt = await scrollsnapchange_promise;
       assertSnapEvent(evt, { block: null, inline: null });
-      snapchanged_promise = waitForSnapChangedEvent(scroller, false);
+      scrollsnapchange_promise = waitForScrollSnapChangeEvent(scroller, false);
       inner_snap_area.style.scrollSnapAlign = "start";
       outer_snap_area.style.scrollSnapAlign = "start";
-      evt = await snapchanged_promise;
+      evt = await scrollsnapchange_promise;
       assertSnapEvent(evt, { block: outer_snap_area,  inline: null });
-    }, "snapchanged fires when snap container no longer has snap areas");
+    }, "scrollsnapchange fires when snap container no longer has snap areas");
   </script>
 </body>
 </html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-scroll-snap-2/snapchanged/snapchanged-on-interrupted-scroll.tentative.html b/third_party/blink/web_tests/external/wpt/css/css-scroll-snap-2/snapchanged/snapchanged-on-interrupted-scroll.tentative.html
index a1d5259..9d0ddd0 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-scroll-snap-2/snapchanged/snapchanged-on-interrupted-scroll.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-scroll-snap-2/snapchanged/snapchanged-on-interrupted-scroll.tentative.html
@@ -2,7 +2,7 @@
 <html>
   <head>
     <meta charset="utf-8">
-    <title> CSS Scroll Snap 2 Test: snapchanged events</title>
+    <title> CSS Scroll Snap 2 Test: scrollsnapchange events</title>
     <link rel="help" href="https://drafts.csswg.org/css-scroll-snap-2/#snap-events">
     <script src="/resources/testharness.js"></script>
     <script src="/resources/testharnessreport.js"></script>
@@ -46,8 +46,8 @@
       promise_test(async (t) => {
         await waitForCompositorCommit();
 
-        container.addEventListener("snapchanged",
-            t.unreached_func("snapchanged should not fire"));
+        container.addEventListener("scrollsnapchange",
+            t.unreached_func("scrollsnapchange should not fire"));
         let reset = () => {
           container.scrollTo({ top: 0, behavior: "smooth"});
           container.removeEventListener("scroll", reset);
@@ -59,10 +59,10 @@
         await scrollend_promise;
         assert_equals(container.scrollTop, 0, "scroll position is reset");
 
-        // snapchanged should not fire since the scroll ended on the same snap
+        // scrollsnapchange should not fire since the scroll ended on the same snap
         // target as the one it started on.
         await waitForCompositorCommit();
-      }, "snapchanged doesn't fire if interrupting scroll cancels snap");
+      }, "scrollsnapchange doesn't fire if interrupting scroll cancels snap");
     </script>
   </body>
 </html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-scroll-snap-2/snapchanged/snapchanged-on-programmatic-root-scroll.tentative.html b/third_party/blink/web_tests/external/wpt/css/css-scroll-snap-2/snapchanged/snapchanged-on-programmatic-root-scroll.tentative.html
index 83603694..940dc2c 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-scroll-snap-2/snapchanged/snapchanged-on-programmatic-root-scroll.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-scroll-snap-2/snapchanged/snapchanged-on-programmatic-root-scroll.tentative.html
@@ -3,7 +3,7 @@
 
 <head>
   <meta charset="utf-8">
-  <title> CSS Scroll Snap 2 Test: snapchanged event on the root/document</title>
+  <title> CSS Scroll Snap 2 Test: scrollsnapchange event on the root/document</title>
   <link rel="help" href="https://drafts.csswg.org/css-scroll-snap-2/#snap-events">
   <script src="/resources/testharness.js"></script>
   <script src="/resources/testharnessreport.js"></script>
@@ -77,8 +77,8 @@
           y: snap_point_2.offsetTop,
         }
       };
-      await test_snapchanged(t, test_data);
-    }, "snapchanged event fires after snap target changes via scrollTo");
+      await test_scrollsnapchange(t, test_data);
+    }, "scrollsnapchange event fires after snap target changes via scrollTo");
 
     promise_test(async (t) => {
       await waitForCompositorCommit();
@@ -93,12 +93,12 @@
           y: snap_point_2.offsetTop,
         }
       };
-      await test_snapchanged(t, test_data, /*use_onsnap_member*/true);
-    }, "Document.onsnapchanged event fires after snap target changes via" +
+      await test_scrollsnapchange(t, test_data, /*use_onsnap_member*/true);
+    }, "Document.onscrollsnapchange event fires after snap target changes via" +
        "scrollTo");
 
     promise_test(async (t) => {
-      checkSnapEventSupport("snapchanged");
+      checkSnapEventSupport("scrollsnapchange");
       await waitForScrollReset(t, scroller);
       await waitForCompositorCommit();
       assert_equals(scroller.scrollTop, 0,
@@ -106,35 +106,35 @@
       assert_equals(scroller.scrollLeft, 0,
         "scroller is initially not scrolled horizontally");
 
-      let snapchanged_promise = waitForSnapChangedEvent(document, false);
+      let scrollsnapchange_promise = waitForScrollSnapChangeEvent(document, false);
       // Set the scroll destination to just a little off (0, 0) so we snap
       // back to the top box.
       let scroll_top_target = 10;
       let scroll_left_target = 10;
 
       scroller.scrollTo(scroll_left_target, scroll_top_target);
-      let evt = await snapchanged_promise;
+      let evt = await scrollsnapchange_promise;
       assert_equals(evt, null, "no snapchanges since scroller is back to top");
-      // scroller should snap back to (0,0) with no snapchanged event.
+      // scroller should snap back to (0,0) with no scrollsnapchange event.
       assert_equals(scroller.scrollTop, 0,
       "scroller snaps back to the top");
       assert_equals(scroller.scrollLeft, 0,
       "scroller snaps back to the left");
 
-      snapchanged_promise = waitForSnapChangedEvent(document);
+      scrollsnapchange_promise = waitForScrollSnapChangeEvent(document);
       scroll_top_target = snap_point_2.offsetTop + 10;
       scroll_left_target = snap_point_2.offsetLeft + 10;
-      // This scroll should snap to snap_point_2, so snapchanged should be
+      // This scroll should snap to snap_point_2, so scrollsnapchange should be
       // fired.
       scroller.scrollTo(scroll_left_target, scroll_top_target);
 
-      evt = await snapchanged_promise;
+      evt = await scrollsnapchange_promise;
       assertSnapEvent(evt, { block: snap_point_2,  inline: snap_point_2 });
       assert_approx_equals(scroller.scrollTop, snap_point_2.offsetTop, 1,
         "scroller snaps to the top of snap_point_2");
       assert_approx_equals(scroller.scrollLeft, snap_point_2.offsetLeft, 1,
         "scroller snaps to the left of snap_point_2");
-    }, "snapchanged is not fired if snap target doesn't change on " +
+    }, "scrollsnapchange is not fired if snap target doesn't change on " +
        "programmatic scroll");
   </script>
 </body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-scroll-snap-2/snapchanged/snapchanged-on-programmatic-scroll.tentative.html b/third_party/blink/web_tests/external/wpt/css/css-scroll-snap-2/snapchanged/snapchanged-on-programmatic-scroll.tentative.html
index 2b2e6a7..03d14c6 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-scroll-snap-2/snapchanged/snapchanged-on-programmatic-scroll.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-scroll-snap-2/snapchanged/snapchanged-on-programmatic-scroll.tentative.html
@@ -3,7 +3,7 @@
 
 <head>
   <meta charset="utf-8">
-  <title> CSS Scroll Snap 2 Test: snapchanged events</title>
+  <title> CSS Scroll Snap 2 Test: scrollsnapchange events</title>
   <link rel="help" href="https://drafts.csswg.org/css-scroll-snap-2/#snap-events">
   <script src="/resources/testharness.js"></script>
   <script src="/resources/testharnessreport.js"></script>
@@ -84,8 +84,8 @@
           y: snap_point_2.offsetTop,
         }
       };
-      await test_snapchanged(t, test_data);
-    }, "snapchanged event fires after snap target changes via scrollTo");
+      await test_scrollsnapchange(t, test_data);
+    }, "scrollsnapchange event fires after snap target changes via scrollTo");
 
     promise_test(async (t) => {
       await waitForCompositorCommit();
@@ -100,12 +100,12 @@
           y: snap_point_2.offsetTop,
         }
       };
-      await test_snapchanged(t, test_data, /*use_onsnap_member*/true);
-    }, "Element.onsnapchanged event fires after snap target changes via" +
+      await test_scrollsnapchange(t, test_data, /*use_onsnap_member*/true);
+    }, "Element.onscrollsnapchange event fires after snap target changes via" +
        "scrollTo");
 
     promise_test(async (t) => {
-      checkSnapEventSupport("snapchanged");
+      checkSnapEventSupport("scrollsnapchange");
       await waitForScrollReset(t, scroller);
       await waitForCompositorCommit();
       assert_equals(scroller.scrollTop, 0,
@@ -113,33 +113,33 @@
       assert_equals(scroller.scrollLeft, 0,
         "scroller is initially not scrolled horizontally");
 
-      let snapchanged_promise = waitForSnapChangedEvent(scroller, false);
+      let scrollsnapchange_promise = waitForScrollSnapChangeEvent(scroller, false);
       // Set the scroll destination to just a little off (0, 0) so we snap
       // back to the top box.
       let scroll_top_target = 10;
       let scroll_left_target = 10;
 
       scroller.scrollTo(scroll_left_target, scroll_top_target);
-      let evt = await snapchanged_promise;
+      let evt = await scrollsnapchange_promise;
       assert_equals(evt, null, "no snapchanges since scroller is back to top");
-      // scroller should snap back to (0,0) with no snapchanged event.
+      // scroller should snap back to (0,0) with no scrollsnapchange event.
       assert_equals(scroller.scrollTop, 0, "scroller snaps back to the top");
       assert_equals(scroller.scrollLeft, 0, "scroller snaps back to the left");
 
-      snapchanged_promise = waitForSnapChangedEvent(scroller);
+      scrollsnapchange_promise = waitForScrollSnapChangeEvent(scroller);
       scroll_top_target = snap_point_2.offsetTop + 10;
       scroll_left_target = snap_point_2.offsetLeft + 10;
-      // This scroll should snap to snap_point_2, so snapchanged should be
+      // This scroll should snap to snap_point_2, so scrollsnapchange should be
       // fired.
       scroller.scrollTo(scroll_left_target, scroll_top_target);
 
-      evt = await snapchanged_promise;
+      evt = await scrollsnapchange_promise;
       assertSnapEvent(evt, { block: snap_point_2, inline: snap_point_2 })
       assert_approx_equals(scroller.scrollTop, snap_point_2.offsetTop, 1,
           "scroller snaps to the top of snap_point_2");
       assert_approx_equals(scroller.scrollLeft, snap_point_2.offsetLeft, 1,
           "scroller snaps to the left of snap_point_2");
-    }, "snapchanged is not fired if snap target doesn't change on " +
+    }, "scrollsnapchange is not fired if snap target doesn't change on " +
        "programmatic scroll");
   }
   </script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-scroll-snap-2/snapchanged/snapchanged-on-user-root-scroll.tentative.html b/third_party/blink/web_tests/external/wpt/css/css-scroll-snap-2/snapchanged/snapchanged-on-user-root-scroll.tentative.html
index a59d9c58..8aff17b 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-scroll-snap-2/snapchanged/snapchanged-on-user-root-scroll.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-scroll-snap-2/snapchanged/snapchanged-on-user-root-scroll.tentative.html
@@ -3,7 +3,7 @@
 
 <head>
   <meta charset="utf-8">
-  <title> CSS Scroll Snap 2 Test: snapchanged events</title>
+  <title> CSS Scroll Snap 2 Test: scrollsnapchange events</title>
   <link rel="help" href="https://drafts.csswg.org/css-scroll-snap-2/#snap-events">
   <script src="/resources/testharness.js"></script>
   <script src="/resources/testharnessreport.js"></script>
@@ -90,8 +90,8 @@
           y: offset_to_snap_point_2.y,
         }
       };
-      await test_snapchanged(t, test_data);
-    }, "snapchanged event fires after snap target changes on touch scroll");
+      await test_scrollsnapchange(t, test_data);
+    }, "scrollsnapchange event fires after snap target changes on touch scroll");
 
     // Wheel scroll test.
     promise_test(async (t) => {
@@ -110,8 +110,8 @@
           y: offset_to_snap_point_2.y,
         }
       };
-      await test_snapchanged(t, test_data);
-    }, "snapchanged event fires after snap target changes on wheel scroll");
+      await test_scrollsnapchange(t, test_data);
+    }, "scrollsnapchange event fires after snap target changes on wheel scroll");
 
     // Scrollbar drag test.
     promise_test(async (t) => {
@@ -140,8 +140,8 @@
           y: offset_to_snap_point_2.y,
         }
       };
-      await test_snapchanged(t, test_data);
-    }, "snapchanged event fires after snap target changes on scrollbar drag");
+      await test_scrollsnapchange(t, test_data);
+    }, "scrollsnapchange event fires after snap target changes on scrollbar drag");
 
     // Keyboard test.
     promise_test(async (t) => {
@@ -158,10 +158,10 @@
           y: offset_to_snap_point_2.y,
         }
       };
-      await test_snapchanged(t, test_data);
-    }, "snapchanged event fires after snap target changes on keydown press");
+      await test_scrollsnapchange(t, test_data);
+    }, "scrollsnapchange event fires after snap target changes on keydown press");
 
-    // Touch scroll test (onsnapchanged variant).
+    // Touch scroll test (onscrollsnapchange variant).
     promise_test(async (t) => {
       await waitForCompositorCommit();
       const start_pos = {
@@ -180,12 +180,12 @@
           y: offset_to_snap_point_2.y,
         }
       };
-      await test_snapchanged(t, test_data, /*use_onsnap_memeber*/true);
-    }, "Document.snapchanged event fires after snap target changes on touch " +
+      await test_scrollsnapchange(t, test_data, /*use_onsnap_memeber*/true);
+    }, "Document.scrollsnapchange event fires after snap target changes on touch " +
        "scroll");
 
     promise_test(async (t) => {
-      await test_no_snapchanged(t, scroller, /*delta*/10);
-    }, "snapchanged is not fired if snap target doesn't change on user scroll");
+      await test_no_scrollsnapchange(t, scroller, /*delta*/10);
+    }, "scrollsnapchange is not fired if snap target doesn't change on user scroll");
   </script>
 </body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-scroll-snap-2/snapchanged/snapchanged-on-user-scroll.tentative.html b/third_party/blink/web_tests/external/wpt/css/css-scroll-snap-2/snapchanged/snapchanged-on-user-scroll.tentative.html
index d2c2789..c3f1b82 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-scroll-snap-2/snapchanged/snapchanged-on-user-scroll.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-scroll-snap-2/snapchanged/snapchanged-on-user-scroll.tentative.html
@@ -3,7 +3,7 @@
 
 <head>
   <meta charset="utf-8">
-  <title> CSS Scroll Snap 2 Test: snapchanged events</title>
+  <title> CSS Scroll Snap 2 Test: scrollsnapchange events</title>
   <link rel="help" href="https://drafts.csswg.org/css-scroll-snap-2/#snap-events">
   <script src="/resources/testharness.js"></script>
   <script src="/resources/testharnessreport.js"></script>
@@ -97,8 +97,8 @@
           y: offset_to_snap_point_2.y,
         }
       };
-      await test_snapchanged(t, test_data);
-    }, "snapchanged event fires after snap target changes on touch scroll");
+      await test_scrollsnapchange(t, test_data);
+    }, "scrollsnapchange event fires after snap target changes on touch scroll");
 
     // Wheel scroll test.
     promise_test(async (t) => {
@@ -117,8 +117,8 @@
           y: offset_to_snap_point_2.y,
         }
       };
-      await test_snapchanged(t, test_data);
-    }, "snapchanged event fires after snap target changes on wheel scroll");
+      await test_scrollsnapchange(t, test_data);
+    }, "scrollsnapchange event fires after snap target changes on wheel scroll");
 
     // Scrollbar drag test.
     promise_test(async (t) => {
@@ -144,8 +144,8 @@
           y: offset_to_snap_point_2.y,
         }
       };
-      await test_snapchanged(t, test_data);
-    }, "snapchanged event fires after snap target changes on scrollbar drag");
+      await test_scrollsnapchange(t, test_data);
+    }, "scrollsnapchange event fires after snap target changes on scrollbar drag");
 
     // Keyboard test.
     promise_test(async (t) => {
@@ -162,10 +162,10 @@
           y: offset_to_snap_point_2.y,
         }
       };
-      await test_snapchanged(t, test_data);
-    }, "snapchanged event fires after snap target changes on keydown press");
+      await test_scrollsnapchange(t, test_data);
+    }, "scrollsnapchange event fires after snap target changes on keydown press");
 
-    // Touch scroll test (onsnapchanged variant).
+    // Touch scroll test (onscrollsnapchange variant).
     promise_test(async (t) => {
       await waitForCompositorCommit();
       const start_pos = {
@@ -184,12 +184,12 @@
           y: offset_to_snap_point_2.y,
         }
       };
-      await test_snapchanged(t, test_data, /*use_onsnap_memeber*/true);
-    }, "Element.onsnapchanged event fires after snap target changes on touch " +
+      await test_scrollsnapchange(t, test_data, /*use_onsnap_memeber*/true);
+    }, "Element.onscrollsnapchange event fires after snap target changes on touch " +
        "scroll");
 
     promise_test(async (t) => {
-      await test_no_snapchanged(t, scroller, /*delta*/10);
-    }, "snapchanged is not fired if snap target doesn't change on user scroll");
+      await test_no_scrollsnapchange(t, scroller, /*delta*/10);
+    }, "scrollsnapchange is not fired if snap target doesn't change on user scroll");
   </script>
 </body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-scroll-snap-2/snapchanged/snapchanged-same-targets-after-layout-changed.html b/third_party/blink/web_tests/external/wpt/css/css-scroll-snap-2/snapchanged/snapchanged-same-targets-after-layout-changed.html
index 4d16bd80..dddad7e 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-scroll-snap-2/snapchanged/snapchanged-same-targets-after-layout-changed.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-scroll-snap-2/snapchanged/snapchanged-same-targets-after-layout-changed.html
@@ -61,13 +61,13 @@
   <script>
     let unreached_func = null;
     promise_test(async (t) => {
-      checkSnapEventSupport("snapchanged");
+      checkSnapEventSupport("scrollsnapchange");
       await waitForCompositorCommit();
-      unreached_func = t.unreached_func("snapchanged shouldn't fire " +
+      unreached_func = t.unreached_func("scrollsnapchange shouldn't fire " +
           "since the scroller is snapped to the same elements.");
-      scroller.addEventListener("snapchanged", unreached_func);
+      scroller.addEventListener("scrollsnapchange", unreached_func);
       t.add_cleanup(() => {
-        scroller.removeEventListener("snapchanged", unreached_func);
+        scroller.removeEventListener("scrollsnapchange", unreached_func);
       });
       assert_greater_than(right.offsetLeft, left.offsetLeft,
           "boxes have switched positions.");
@@ -78,27 +78,27 @@
       assert_less_than(right.offsetLeft, left.offsetLeft,
           "boxes have switched positions.");
       await waitForCompositorCommit();
-    }, "snapchanged doesn't fire after layout change if snapped to the same " +
+    }, "scrollsnapchange doesn't fire after layout change if snapped to the same " +
     "elements");
 
     promise_test(async (t) => {
-      checkSnapEventSupport("snapchanged");
+      checkSnapEventSupport("scrollsnapchange");
       await waitForCompositorCommit();
-      unreached_func = t.unreached_func("snapchanged shouldn't fire " +
+      unreached_func = t.unreached_func("scrollsnapchange shouldn't fire " +
           "since the scroller is snapped to the same elements.");
-      scroller.addEventListener("snapchanged", unreached_func);
+      scroller.addEventListener("scrollsnapchange", unreached_func);
       t.add_cleanup(() => {
-        scroller.removeEventListener("snapchanged", unreached_func);
+        scroller.removeEventListener("scrollsnapchange", unreached_func);
       });
       const scrollend_promise = waitForScrollendEventNoTimeout(scroller);
       // Move the boxes to the same vertical level. Both boxes should still be
-      // considered snapped to so snapchanged should not be triggerred.
+      // considered snapped to so scrollsnapchange should not be triggerred.
       right.style.top = `0px`;
       left.style.top = `0px`;
       await scrollend_promise;
       assert_equals(scroller.scrollTop, 0);
       await waitForCompositorCommit();
-    }, "snapchanged doesn't fire after snap to the same targets after scroll. " +
+    }, "scrollsnapchange doesn't fire after snap to the same targets after scroll. " +
     "elements");
 
   </script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-scroll-snap-2/snapchanged/snapchanged-scrolling-non-snapping-axis.tentative.html b/third_party/blink/web_tests/external/wpt/css/css-scroll-snap-2/snapchanged/snapchanged-scrolling-non-snapping-axis.tentative.html
index e39fc0c..32393fe7 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-scroll-snap-2/snapchanged/snapchanged-scrolling-non-snapping-axis.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-scroll-snap-2/snapchanged/snapchanged-scrolling-non-snapping-axis.tentative.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <html>
   <head>
-    <title> CSS Scroll Snap 2 Test: snapchanged events</title>
+    <title> CSS Scroll Snap 2 Test: scrollsnapchange events</title>
     <link rel="help" href="https://drafts.csswg.org/css-scroll-snap-2/#snap-events">
     <script src="/resources/testharness.js"></script>
     <script src="/resources/testharnessreport.js"></script>
@@ -49,29 +49,29 @@
 
         scroller.focus();
 
-        const snapchanged_promise = waitForSnapEvent(scroller, "snapchanged");
+        const scrollsnapchange_promise = waitForSnapEvent(scroller, "scrollsnapchange");
         await test_driver.send_keys(scroller, KEY_CODE_MAP["ArrowRight"]);
-        const snap_event = await snapchanged_promise;
+        const snap_event = await scrollsnapchange_promise;
 
-        assert_equals(snap_event, null, "no snapchanged event fired as " +
+        assert_equals(snap_event, null, "no scrollsnapchange event fired as " +
           "scroller doesn't snap in the x axis");
-      }, "keyboard scroll on non-snapping axis doesn't trigger snapchanged");
+      }, "keyboard scroll on non-snapping axis doesn't trigger scrollsnapchange");
 
       promise_test(async (t) => {
         await waitForScrollReset(t, scroller);
         await waitForCompositorCommit();
         scroller.focus();
 
-        const snapchanged_promise = waitForSnapEvent(scroller, "snapchanged");
+        const scrollsnapchange_promise = waitForSnapEvent(scroller, "scrollsnapchange");
         const wheel_scroll_amount = 25;
         new test_driver.Actions().scroll(0, 0,
           wheel_scroll_amount,
           0,
           { origin: scroller }).send();
-        const snap_event = await snapchanged_promise;
-        assert_equals(snap_event, null, "no snapchanged event fired as " +
+        const snap_event = await scrollsnapchange_promise;
+        assert_equals(snap_event, null, "no scrollsnapchange event fired as " +
           "scroller doesn't snap in the x axis");
-      }, "wheel scroll on non-snapping axis doesn't trigger snapchanged");
+      }, "wheel scroll on non-snapping axis doesn't trigger scrollsnapchange");
     </script>
   </body>
 </html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-scroll-snap-2/snapchanged/snapchanged-with-proximity-strictness.tentative.html b/third_party/blink/web_tests/external/wpt/css/css-scroll-snap-2/snapchanged/snapchanged-with-proximity-strictness.tentative.html
index 96cab33..9c2e3df 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-scroll-snap-2/snapchanged/snapchanged-with-proximity-strictness.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-scroll-snap-2/snapchanged/snapchanged-with-proximity-strictness.tentative.html
@@ -2,7 +2,7 @@
 <html>
 <head>
 <title>
-  <title> CSS Scroll Snap 2 Test: snapchanged events on proximity strictness container</title>
+  <title> CSS Scroll Snap 2 Test: scrollsnapchange events on proximity strictness container</title>
   <link rel="help" href="https://drafts.csswg.org/css-scroll-snap-2/#snap-events"/>
 </title>
 <script src="/resources/testharness.js"></script>
@@ -46,35 +46,35 @@
   let resolve_func = null;
 
   promise_test(async (test) => {
-    checkSnapEventSupport("snapchanged");
+    checkSnapEventSupport("scrollsnapchange");
     await waitForCompositorCommit();
     // The initial snap position is at (0, 0).
     assert_equals(scroller.scrollTop, 0);
     assert_equals(scroller.scrollLeft, 0);
 
-    let snapchanged_promise = waitForSnapChangedEvent(scroller);
+    let scrollsnapchange_promise = waitForScrollSnapChangeEvent(scroller);
     // Scroll to a position where it's outside of the scroll-snap proximity
     // threshold, so that it won't trigger snapping.
     scroller.scrollTo(0, 250);
 
-    // snapchanged should fire as we've moved from within the proximity range
+    // scrollsnapchange should fire as we've moved from within the proximity range
     // to outside the proximity range and are no longer snapped.
-    let evt = await snapchanged_promise;
+    let evt = await scrollsnapchange_promise;
     assert_equals(scroller.scrollTop, 250);
     assertSnapEvent(evt, { block: null, inline: null });
     evt = null;
 
-    snapchanged_promise = waitForSnapChangedEvent(scroller);
+    scrollsnapchange_promise = waitForScrollSnapChangeEvent(scroller);
     // Scroll to a position within the scroll-snap proximity
     // threshold, so that it triggers snapping.
     scroller.scrollTo(0, 190);
 
-    evt = await snapchanged_promise;
+    evt = await scrollsnapchange_promise;
     assert_equals(scroller.scrollTop, 0);
-    // snapchanged should fire as we've moved from outside the proximity range
+    // scrollsnapchange should fire as we've moved from outside the proximity range
     // to inside the proximity range and are once again snapped.
     assertSnapEvent(evt, { block: target, inline: null });
-  }, "Snapchanged fires when scrolling outside proximity range.");
+  }, "Scrollsnapchange fires when scrolling outside proximity range.");
   </script>
 </body>
 </html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-scroll-snap-2/snapevents-at-document-bubble-to-window.html b/third_party/blink/web_tests/external/wpt/css/css-scroll-snap-2/snapevents-at-document-bubble-to-window.html
index e50be0b..c1f5536 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-scroll-snap-2/snapevents-at-document-bubble-to-window.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-scroll-snap-2/snapevents-at-document-bubble-to-window.html
@@ -3,7 +3,7 @@
 
 <head>
   <meta charset="utf-8">
-  <title> CSS Scroll Snap 2 Test: snapchanged event on the document bubbles</title>
+  <title> CSS Scroll Snap 2 Test: scrollsnapchange event on the document bubbles</title>
   <link rel="help" href="https://drafts.csswg.org/css-scroll-snap-2/#snap-events">
   <script src="/resources/testharness.js"></script>
   <script src="/resources/testharnessreport.js"></script>
@@ -64,14 +64,14 @@
       await waitForCompositorCommit();
 
       let snapchanging_promise = waitForSnapEvent(window, "snapchanging");
-      let snapchanged_promise = waitForSnapEvent(window, "snapchanged");
+      let scrollsnapchange_promise = waitForSnapEvent(window, "scrollsnapchange");
       document.scrollingElement.scrollTo(0, snap_point_2.offsetTop);
       let snapchanging_evt = await snapchanging_promise;
-      let snapchanged_evt = await snapchanged_promise;
+      let scrollsnapchange_evt = await scrollsnapchange_promise;
 
       assertSnapEvent(snapchanging_evt, { inline: null, block: snap_point_2 });
-      assertSnapEvent(snapchanged_evt, { inline: null, block: snap_point_2 });
-    }, "snapchanged bubbles when fired at the document (addEventListener).");
+      assertSnapEvent(scrollsnapchange_evt, { inline: null, block: snap_point_2 });
+    }, "scrollsnapchange bubbles when fired at the document (addEventListener).");
 
     promise_test(async(t) => {
       await waitForScrollReset(t, document.scrollingElement);
@@ -80,16 +80,16 @@
       let snapchanging_promise = waitForSnapEvent(window, "snapchanging",
                                     /*scroll_happens=*/true,
                                     /*use_onsnap_member=*/true);
-      let snapchanged_promise = waitForSnapEvent(window, "snapchanged",
+      let scrollsnapchange_promise = waitForSnapEvent(window, "scrollsnapchange",
                                     /*scroll_happens=*/true,
                                     /*use_onsnap_member=*/true);
       document.scrollingElement.scrollTo(0, snap_point_2.offsetTop);
       let snapchanging_evt = await snapchanging_promise;
-      let snapchanged_evt = await snapchanged_promise;
+      let scrollsnapchange_evt = await scrollsnapchange_promise;
 
       assertSnapEvent(snapchanging_evt, { inline: null, block: snap_point_2 });
-      assertSnapEvent(snapchanged_evt, { inline: null, block: snap_point_2 });
-    }, "snapchanged bubbles when fired at the document (onsnapchanged).");
+      assertSnapEvent(scrollsnapchange_evt, { inline: null, block: snap_point_2 });
+    }, "scrollsnapchange bubbles when fired at the document (onscrollsnapchange).");
   </script>
 </body>
 
diff --git a/third_party/blink/web_tests/external/wpt/css/css-values/calc-size/calc-size-typed-om.html b/third_party/blink/web_tests/external/wpt/css/css-values/calc-size/calc-size-typed-om.html
new file mode 100644
index 0000000..3af431b
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-values/calc-size/calc-size-typed-om.html
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<title>calc-size() expressions</title>
+<link rel="help" href="https://drafts.csswg.org/css-values-5/#calc-size">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../../support/parsing-testcommon.js"></script>
+
+<div id="test"></div>
+
+<script>
+
+let e = document.getElementById("test");
+
+function test_typed_om_string(property, value, expected) {
+  test((t) => {
+    if (!expected) {
+      expected = value;
+    }
+    e.style.setProperty(property, value);
+    let actual = e.computedStyleMap().get(property).toString();
+    assert_equals(actual, expected);
+    e.style.setProperty(property, "");
+  }, `computedStyleMap serialization of ${property}: ${value}`);
+}
+
+test_typed_om_string("width", "calc-size(auto, size)");
+test_typed_om_string("width", "calc-size(fit-content, size)");
+test_typed_om_string("height", "calc-size(min-content, size * 2)");
+test_typed_om_string("max-width", "calc-size(max-content, size / 2)", "calc-size(max-content, size * 0.5)");
+test_typed_om_string("max-height", "calc-size(fit-content, 30px + size / 2)", "calc-size(fit-content, 30px + size * 0.5)");
+test_typed_om_string("width", "calc-size(fit-content, 50% + size / 2)", "calc-size(fit-content, 50% + size * 0.5)");
+test_typed_om_string("width", "calc-size(any, 25em)", "calc-size(any, 400px)");
+test_typed_om_string("width", "calc-size(any, 40%)");
+test_typed_om_string("width", "calc-size(any, 50px + 30%)", "calc-size(any, 30% + 50px)");
+test_typed_om_string("width", "calc-size(10px, sign(size) * size)");
+test_typed_om_string("width", "calc-size(30px, 25em)", "calc-size(30px, 400px)");
+test_typed_om_string("width", "calc-size(calc-size(any, 30px), 25em)", "calc-size(calc-size(any, 30px), 400px)");
+test_typed_om_string("width", "calc-size(calc-size(2in, 30px), 25em)", "calc-size(calc-size(192px, 30px), 400px)");
+test_typed_om_string("width", "calc-size(calc-size(min-content, 30px), 25em)", "calc-size(calc-size(min-content, 30px), 400px)");
+test_typed_om_string("width", "calc-size(calc-size(min-content, size), size)");
+
+// Based on the discussion in https://github.com/w3c/csswg-drafts/issues/10259
+// this presumes parse-time conversion of the one-argument form to the
+// two-argument form, but this isn't yet specified.
+test_typed_om_string("width", "calc-size(30px)", "calc-size(any, 30px)");
+test_typed_om_string("width", "calc-size(min(30px, 2em))", "calc-size(any, 30px)");
+test_typed_om_string("width", "calc-size(calc-size(any, 30px))", "calc-size(calc-size(any, 30px), size)");
+test_typed_om_string("width", "calc-size(fit-content)", "calc-size(fit-content, size)");
+test_typed_om_string("width", "calc-size(calc-size(fit-content, size * 2))", "calc-size(calc-size(fit-content, size * 2), size)");
+test_typed_om_string("width", "calc-size(calc-size(30px))", "calc-size(calc-size(any, 30px), size)");
+
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/document-policy/font-display/override-to-optional.tentative-ref.html b/third_party/blink/web_tests/external/wpt/document-policy/font-display/override-to-optional.tentative-ref.html
deleted file mode 100644
index 78bca05..0000000
--- a/third_party/blink/web_tests/external/wpt/document-policy/font-display/override-to-optional.tentative-ref.html
+++ /dev/null
@@ -1,30 +0,0 @@
-<!DOCTYPE html>
-<title>Test for no-font-display-late-swap document policy behavior</title>
-<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
-<style>
-.ahem {
-  font-family: 'Ahem';
-}
-.arial {
-  font-family: 'Arial';
-}
-</style>
-<p>Tests if font-display is set to optional for each option except for when it is set to fallback</p>
-<table id="container">
- <tr>
-  <th>not-set</th>
-  <th>auto</th>
-  <th>block</th>
-  <th>swap</th>
-  <th>fallback</th>
-  <th>optional</th>
- </tr>
- <tr>
-  <td class="arial">a</td>
-  <td class="arial">a</td>
-  <td class="arial">a</td>
-  <td class="arial">a</td>
-  <td class="ahem">a</td>
-  <td class="arial">a</td>
- </tr>
-</table>
diff --git a/third_party/blink/web_tests/external/wpt/document-policy/font-display/override-to-optional.tentative.html b/third_party/blink/web_tests/external/wpt/document-policy/font-display/override-to-optional.tentative.html
deleted file mode 100644
index 26927bb..0000000
--- a/third_party/blink/web_tests/external/wpt/document-policy/font-display/override-to-optional.tentative.html
+++ /dev/null
@@ -1,46 +0,0 @@
-<!DOCTYPE html>
-<html class="reftest-wait">
-<title>Test for no-font-display-late-swap document policy behavior</title>
-<link rel="help" href="https://github.com/w3c/webappsec-feature-policy/blob/master/policies/font-display-late-swap.md">
-<link rel="match" href="override-to-optional.tentative-ref.html">
-<style>
-</style>
-<p>Tests if font-display is set to optional for each option except for when it is set to fallback</p>
-<table id="container">
- <tr>
-  <th>not-set</th>
-  <th>auto</th>
-  <th>block</th>
-  <th>swap</th>
-  <th>fallback</th>
-  <th>optional</th>
- </tr>
-</table>
-<script>
-const fontDisplayValues = ['', 'auto', 'block', 'swap', 'fallback', 'optional'];
-const table = document.getElementById('container');
-
-function makeFontFaceDeclaration(family, display) {
-    url = '/fonts/Ahem.ttf?pipe=trickle(d1)'; // Before the swap period is over
-    return '@font-face { font-family: ' + family + '; src: url("' + url + '"); font-display: ' + display + '; }';
-}
-
-window.onload = () => {
-    let tr = document.createElement('tr');
-    for (let display of fontDisplayValues) {
-        const family = display + '-face';
-        const rule = makeFontFaceDeclaration(family, display);
-        document.styleSheets[0].insertRule(rule, 0);
-        let td = document.createElement('td');
-        td.textContent = 'a';
-        td.style.fontFamily = family + ', Arial';
-        tr.appendChild(td);
-    }
-    table.appendChild(tr);
-    const timeoutMilliSec = 1500; // After the font is loaded
-    setTimeout(() => {
-        document.documentElement.classList.remove("reftest-wait");
-    }, timeoutMilliSec);
-}
-</script>
-</html>
diff --git a/third_party/blink/web_tests/external/wpt/document-policy/font-display/override-to-optional.tentative.html.headers b/third_party/blink/web_tests/external/wpt/document-policy/font-display/override-to-optional.tentative.html.headers
deleted file mode 100644
index 01b8bbc..0000000
--- a/third_party/blink/web_tests/external/wpt/document-policy/font-display/override-to-optional.tentative.html.headers
+++ /dev/null
@@ -1 +0,0 @@
-Document-Policy: font-display-late-swap=?0
diff --git a/third_party/blink/web_tests/external/wpt/document-policy/font-display/report-only-auto.tentative.html b/third_party/blink/web_tests/external/wpt/document-policy/font-display/report-only-auto.tentative.html
deleted file mode 100644
index 9eea350..0000000
--- a/third_party/blink/web_tests/external/wpt/document-policy/font-display/report-only-auto.tentative.html
+++ /dev/null
@@ -1,9 +0,0 @@
-<!DOCTYPE html>
-<title>Test for font-display-late-swap document policy with font display value 'auto'</title>
-<link rel="help" href="https://github.com/w3c/webappsec-permissions-policy/blob/master/policies/font-display-late-swap.md">
-<script src='/resources/testharness.js'></script>
-<script src='/resources/testharnessreport.js'></script>
-<script src='resources/font-display-reporting-helper.js'></script>
-<script>
-  testFontDisplayPolicyReportOnlyGenerated('auto');
-</script>
diff --git a/third_party/blink/web_tests/external/wpt/document-policy/font-display/report-only-auto.tentative.html.headers b/third_party/blink/web_tests/external/wpt/document-policy/font-display/report-only-auto.tentative.html.headers
deleted file mode 100644
index 1509127..0000000
--- a/third_party/blink/web_tests/external/wpt/document-policy/font-display/report-only-auto.tentative.html.headers
+++ /dev/null
@@ -1 +0,0 @@
-Document-Policy-Report-Only: font-display-late-swap=?0
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/document-policy/font-display/report-only-blank.tentative.html b/third_party/blink/web_tests/external/wpt/document-policy/font-display/report-only-blank.tentative.html
deleted file mode 100644
index 628dbcc..0000000
--- a/third_party/blink/web_tests/external/wpt/document-policy/font-display/report-only-blank.tentative.html
+++ /dev/null
@@ -1,9 +0,0 @@
-<!DOCTYPE html>
-<title>Test for font-display-late-swap document policy with font display value empty</title>
-<link rel="help" href="https://github.com/w3c/webappsec-permissions-policy/blob/master/policies/font-display-late-swap.md">
-<script src='/resources/testharness.js'></script>
-<script src='/resources/testharnessreport.js'></script>
-<script src='resources/font-display-reporting-helper.js'></script>
-<script>
-  testFontDisplayPolicyReportOnlyGenerated('');
-</script>
diff --git a/third_party/blink/web_tests/external/wpt/document-policy/font-display/report-only-blank.tentative.html.headers b/third_party/blink/web_tests/external/wpt/document-policy/font-display/report-only-blank.tentative.html.headers
deleted file mode 100644
index 1509127..0000000
--- a/third_party/blink/web_tests/external/wpt/document-policy/font-display/report-only-blank.tentative.html.headers
+++ /dev/null
@@ -1 +0,0 @@
-Document-Policy-Report-Only: font-display-late-swap=?0
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/document-policy/font-display/report-only-block.tentative.html b/third_party/blink/web_tests/external/wpt/document-policy/font-display/report-only-block.tentative.html
deleted file mode 100644
index a383695..0000000
--- a/third_party/blink/web_tests/external/wpt/document-policy/font-display/report-only-block.tentative.html
+++ /dev/null
@@ -1,9 +0,0 @@
-<!DOCTYPE html>
-<title>Test for font-display-late-swap document policy with font display value 'block'</title>
-<link rel="help" href="https://github.com/w3c/webappsec-permissions-policy/blob/master/policies/font-display-late-swap.md">
-<script src='/resources/testharness.js'></script>
-<script src='/resources/testharnessreport.js'></script>
-<script src='resources/font-display-reporting-helper.js'></script>
-<script>
-  testFontDisplayPolicyReportOnlyGenerated('block');
-</script>
diff --git a/third_party/blink/web_tests/external/wpt/document-policy/font-display/report-only-block.tentative.html.headers b/third_party/blink/web_tests/external/wpt/document-policy/font-display/report-only-block.tentative.html.headers
deleted file mode 100644
index 1509127..0000000
--- a/third_party/blink/web_tests/external/wpt/document-policy/font-display/report-only-block.tentative.html.headers
+++ /dev/null
@@ -1 +0,0 @@
-Document-Policy-Report-Only: font-display-late-swap=?0
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/document-policy/font-display/report-only-fallback.tentative.html b/third_party/blink/web_tests/external/wpt/document-policy/font-display/report-only-fallback.tentative.html
deleted file mode 100644
index a03015e..0000000
--- a/third_party/blink/web_tests/external/wpt/document-policy/font-display/report-only-fallback.tentative.html
+++ /dev/null
@@ -1,9 +0,0 @@
-<!DOCTYPE html>
-<title>Test for font-display-late-swap document policy with font display value 'fallback'</title>
-<link rel="help" href="https://github.com/w3c/webappsec-permissions-policy/blob/master/policies/font-display-late-swap.md">
-<script src='/resources/testharness.js'></script>
-<script src='/resources/testharnessreport.js'></script>
-<script src='resources/font-display-reporting-helper.js'></script>
-<script>
-  testCompliantWithFontDisplayPolicy('fallback');
-</script>
diff --git a/third_party/blink/web_tests/external/wpt/document-policy/font-display/report-only-fallback.tentative.html.headers b/third_party/blink/web_tests/external/wpt/document-policy/font-display/report-only-fallback.tentative.html.headers
deleted file mode 100644
index 1509127..0000000
--- a/third_party/blink/web_tests/external/wpt/document-policy/font-display/report-only-fallback.tentative.html.headers
+++ /dev/null
@@ -1 +0,0 @@
-Document-Policy-Report-Only: font-display-late-swap=?0
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/document-policy/font-display/report-only-optional.tentative.html b/third_party/blink/web_tests/external/wpt/document-policy/font-display/report-only-optional.tentative.html
deleted file mode 100644
index 2a4673f..0000000
--- a/third_party/blink/web_tests/external/wpt/document-policy/font-display/report-only-optional.tentative.html
+++ /dev/null
@@ -1,9 +0,0 @@
-<!DOCTYPE html>
-<title>Test for font-display-late-swap document policy with font display value 'optional'</title>
-<link rel="help" href="https://github.com/w3c/webappsec-permissions-policy/blob/master/policies/font-display-late-swap.md">
-<script src='/resources/testharness.js'></script>
-<script src='/resources/testharnessreport.js'></script>
-<script src='resources/font-display-reporting-helper.js'></script>
-<script>
-  testCompliantWithFontDisplayPolicy('optional');
-</script>
diff --git a/third_party/blink/web_tests/external/wpt/document-policy/font-display/report-only-optional.tentative.html.headers b/third_party/blink/web_tests/external/wpt/document-policy/font-display/report-only-optional.tentative.html.headers
deleted file mode 100644
index 1509127..0000000
--- a/third_party/blink/web_tests/external/wpt/document-policy/font-display/report-only-optional.tentative.html.headers
+++ /dev/null
@@ -1 +0,0 @@
-Document-Policy-Report-Only: font-display-late-swap=?0
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/document-policy/font-display/report-only-swap.tentative.html b/third_party/blink/web_tests/external/wpt/document-policy/font-display/report-only-swap.tentative.html
deleted file mode 100644
index ad1f437..0000000
--- a/third_party/blink/web_tests/external/wpt/document-policy/font-display/report-only-swap.tentative.html
+++ /dev/null
@@ -1,9 +0,0 @@
-<!DOCTYPE html>
-<title>Test for font-display-late-swap document policy with font display value 'swap'</title>
-<link rel="help" href="https://github.com/w3c/webappsec-permissions-policy/blob/master/policies/font-display-late-swap.md">
-<script src='/resources/testharness.js'></script>
-<script src='/resources/testharnessreport.js'></script>
-<script src='resources/font-display-reporting-helper.js'></script>
-<script>
-  testFontDisplayPolicyReportOnlyGenerated('swap');
-</script>
diff --git a/third_party/blink/web_tests/external/wpt/document-policy/font-display/report-only-swap.tentative.html.headers b/third_party/blink/web_tests/external/wpt/document-policy/font-display/report-only-swap.tentative.html.headers
deleted file mode 100644
index 1509127..0000000
--- a/third_party/blink/web_tests/external/wpt/document-policy/font-display/report-only-swap.tentative.html.headers
+++ /dev/null
@@ -1 +0,0 @@
-Document-Policy-Report-Only: font-display-late-swap=?0
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/document-policy/font-display/reporting-auto.tentative.html b/third_party/blink/web_tests/external/wpt/document-policy/font-display/reporting-auto.tentative.html
deleted file mode 100644
index 01857dd..0000000
--- a/third_party/blink/web_tests/external/wpt/document-policy/font-display/reporting-auto.tentative.html
+++ /dev/null
@@ -1,9 +0,0 @@
-<!DOCTYPE html>
-<title>Test for font-display-late-swap document policy with font display value 'auto'</title>
-<link rel="help" href="https://github.com/w3c/webappsec-permissions-policy/blob/master/policies/font-display-late-swap.md">
-<script src='/resources/testharness.js'></script>
-<script src='/resources/testharnessreport.js'></script>
-<script src='resources/font-display-reporting-helper.js'></script>
-<script>
-  testFontDisplayPolicyViolationGenerated('auto');
-</script>
diff --git a/third_party/blink/web_tests/external/wpt/document-policy/font-display/reporting-auto.tentative.html.headers b/third_party/blink/web_tests/external/wpt/document-policy/font-display/reporting-auto.tentative.html.headers
deleted file mode 100644
index 273ca356..0000000
--- a/third_party/blink/web_tests/external/wpt/document-policy/font-display/reporting-auto.tentative.html.headers
+++ /dev/null
@@ -1 +0,0 @@
-Document-Policy: font-display-late-swap=?0
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/document-policy/font-display/reporting-blank.tentative.html b/third_party/blink/web_tests/external/wpt/document-policy/font-display/reporting-blank.tentative.html
deleted file mode 100644
index 6521ae1..0000000
--- a/third_party/blink/web_tests/external/wpt/document-policy/font-display/reporting-blank.tentative.html
+++ /dev/null
@@ -1,9 +0,0 @@
-<!DOCTYPE html>
-<title>Test for font-display-late-swap document policy with font display value empty</title>
-<link rel="help" href="https://github.com/w3c/webappsec-permissions-policy/blob/master/policies/font-display-late-swap.md">
-<script src='/resources/testharness.js'></script>
-<script src='/resources/testharnessreport.js'></script>
-<script src='resources/font-display-reporting-helper.js'></script>
-<script>
-  testFontDisplayPolicyViolationGenerated('');
-</script>
diff --git a/third_party/blink/web_tests/external/wpt/document-policy/font-display/reporting-blank.tentative.html.headers b/third_party/blink/web_tests/external/wpt/document-policy/font-display/reporting-blank.tentative.html.headers
deleted file mode 100644
index 273ca356..0000000
--- a/third_party/blink/web_tests/external/wpt/document-policy/font-display/reporting-blank.tentative.html.headers
+++ /dev/null
@@ -1 +0,0 @@
-Document-Policy: font-display-late-swap=?0
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/document-policy/font-display/reporting-block.tentative.html b/third_party/blink/web_tests/external/wpt/document-policy/font-display/reporting-block.tentative.html
deleted file mode 100644
index 60403c6..0000000
--- a/third_party/blink/web_tests/external/wpt/document-policy/font-display/reporting-block.tentative.html
+++ /dev/null
@@ -1,9 +0,0 @@
-<!DOCTYPE html>
-<title>Test for font-display-late-swap document policy with font display value 'block'</title>
-<link rel="help" href="https://github.com/w3c/webappsec-permissions-policy/blob/master/policies/font-display-late-swap.md">
-<script src='/resources/testharness.js'></script>
-<script src='/resources/testharnessreport.js'></script>
-<script src='resources/font-display-reporting-helper.js'></script>
-<script>
-  testFontDisplayPolicyViolationGenerated('block');
-</script>
diff --git a/third_party/blink/web_tests/external/wpt/document-policy/font-display/reporting-block.tentative.html.headers b/third_party/blink/web_tests/external/wpt/document-policy/font-display/reporting-block.tentative.html.headers
deleted file mode 100644
index 273ca356..0000000
--- a/third_party/blink/web_tests/external/wpt/document-policy/font-display/reporting-block.tentative.html.headers
+++ /dev/null
@@ -1 +0,0 @@
-Document-Policy: font-display-late-swap=?0
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/document-policy/font-display/reporting-fallback.tentative.html b/third_party/blink/web_tests/external/wpt/document-policy/font-display/reporting-fallback.tentative.html
deleted file mode 100644
index a03015e..0000000
--- a/third_party/blink/web_tests/external/wpt/document-policy/font-display/reporting-fallback.tentative.html
+++ /dev/null
@@ -1,9 +0,0 @@
-<!DOCTYPE html>
-<title>Test for font-display-late-swap document policy with font display value 'fallback'</title>
-<link rel="help" href="https://github.com/w3c/webappsec-permissions-policy/blob/master/policies/font-display-late-swap.md">
-<script src='/resources/testharness.js'></script>
-<script src='/resources/testharnessreport.js'></script>
-<script src='resources/font-display-reporting-helper.js'></script>
-<script>
-  testCompliantWithFontDisplayPolicy('fallback');
-</script>
diff --git a/third_party/blink/web_tests/external/wpt/document-policy/font-display/reporting-fallback.tentative.html.headers b/third_party/blink/web_tests/external/wpt/document-policy/font-display/reporting-fallback.tentative.html.headers
deleted file mode 100644
index 273ca356..0000000
--- a/third_party/blink/web_tests/external/wpt/document-policy/font-display/reporting-fallback.tentative.html.headers
+++ /dev/null
@@ -1 +0,0 @@
-Document-Policy: font-display-late-swap=?0
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/document-policy/font-display/reporting-optional.tentative.html b/third_party/blink/web_tests/external/wpt/document-policy/font-display/reporting-optional.tentative.html
deleted file mode 100644
index 2a4673f..0000000
--- a/third_party/blink/web_tests/external/wpt/document-policy/font-display/reporting-optional.tentative.html
+++ /dev/null
@@ -1,9 +0,0 @@
-<!DOCTYPE html>
-<title>Test for font-display-late-swap document policy with font display value 'optional'</title>
-<link rel="help" href="https://github.com/w3c/webappsec-permissions-policy/blob/master/policies/font-display-late-swap.md">
-<script src='/resources/testharness.js'></script>
-<script src='/resources/testharnessreport.js'></script>
-<script src='resources/font-display-reporting-helper.js'></script>
-<script>
-  testCompliantWithFontDisplayPolicy('optional');
-</script>
diff --git a/third_party/blink/web_tests/external/wpt/document-policy/font-display/reporting-optional.tentative.html.headers b/third_party/blink/web_tests/external/wpt/document-policy/font-display/reporting-optional.tentative.html.headers
deleted file mode 100644
index 273ca356..0000000
--- a/third_party/blink/web_tests/external/wpt/document-policy/font-display/reporting-optional.tentative.html.headers
+++ /dev/null
@@ -1 +0,0 @@
-Document-Policy: font-display-late-swap=?0
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/document-policy/font-display/reporting-swap.tentative.html b/third_party/blink/web_tests/external/wpt/document-policy/font-display/reporting-swap.tentative.html
deleted file mode 100644
index 53d823f..0000000
--- a/third_party/blink/web_tests/external/wpt/document-policy/font-display/reporting-swap.tentative.html
+++ /dev/null
@@ -1,9 +0,0 @@
-<!DOCTYPE html>
-<title>Test for font-display-late-swap document policy with font display value 'swap'</title>
-<link rel="help" href="https://github.com/w3c/webappsec-permissions-policy/blob/master/policies/font-display-late-swap.md">
-<script src='/resources/testharness.js'></script>
-<script src='/resources/testharnessreport.js'></script>
-<script src='resources/font-display-reporting-helper.js'></script>
-<script>
-  testFontDisplayPolicyViolationGenerated('swap');
-</script>
diff --git a/third_party/blink/web_tests/external/wpt/document-policy/font-display/reporting-swap.tentative.html.headers b/third_party/blink/web_tests/external/wpt/document-policy/font-display/reporting-swap.tentative.html.headers
deleted file mode 100644
index 273ca356..0000000
--- a/third_party/blink/web_tests/external/wpt/document-policy/font-display/reporting-swap.tentative.html.headers
+++ /dev/null
@@ -1 +0,0 @@
-Document-Policy: font-display-late-swap=?0
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/document-policy/font-display/resources/font-display-reporting-helper.js b/third_party/blink/web_tests/external/wpt/document-policy/font-display/resources/font-display-reporting-helper.js
deleted file mode 100644
index f3c8a22..0000000
--- a/third_party/blink/web_tests/external/wpt/document-policy/font-display/resources/font-display-reporting-helper.js
+++ /dev/null
@@ -1,81 +0,0 @@
-function check_report_format(report, expected_url, expected_disposition) {
-  assert_equals(report.type, 'document-policy-violation');
-  assert_equals(report.url, expected_url);
-  assert_equals(report.body.featureId, 'font-display-late-swap');
-  assert_equals(report.body.disposition, expected_disposition);
-  assert_true('sourceFile' in report.body);
-  assert_true('lineNumber' in report.body);
-  assert_true('columnNumber' in report.body);
-}
-
-function check_violation_report_format(report, expected_url) {
-  check_report_format(report, expected_url, 'enforce');
-}
-
-function check_report_only_report_format(report, expected_url) {
-  check_report_format(report, expected_url, 'report');
-}
-
-function makeFontFaceDeclaration(family, display) {
-  url = '/fonts/Ahem.ttf?pipe=trickle(d1)'; // Before the swap period is over
-  return `@font-face { font-family: ${family}; src: url("${url}"); font-display: ${display}; }`;
-}
-
-/**
- * Run font-display test with given parameters.
- *
- * A violation report is expected with fontDisplayValue set to
- * ['', 'auto', 'block', 'swap']
- *
- * No violation report is expected with fontDisplayValue set to
- * ['fallback', 'optional']
-
- * @param {String} fontDisplayValue
- * @param {(Report, String) => () | undefined} format_check pass a callback to
- * check report format if a violation report is expected. If no report is
- * expected to be generated, leave this argument undefined.
- */
-function runTest(fontDisplayValue, format_check) {
-  window.onload = () => {
-    const family = fontDisplayValue + '-face';
-    const rule = makeFontFaceDeclaration(family, fontDisplayValue);
-
-    const style = document.createElement('style');
-    style.innerHTML = rule;
-    document.body.appendChild(style);
-
-    const div = document.createElement('div');
-    div.textContent = 'a';
-    div.style.fontFamily = family + ', Arial';
-    document.body.appendChild(div);
-  };
-
-  const t = async_test('font-display-late-swap Report Format');
-
-  new ReportingObserver(
-    t.step_func_done((reports, _) => {
-      assert_equals(reports.length, 1);
-      assert_true(!!format_check);
-      format_check(reports[0], document.location.href);
-    }), {
-      types: ['document-policy-violation'],
-      buffered: true
-    }
-  ).observe();
-
-  t.step_timeout(t.step_func_done(() => {
-    assert_false(!!format_check, 'Expected violation report but did not get one.');
-  }), 400); // 400ms should be sufficient to observe the violation report.
-}
-
-function testFontDisplayPolicyViolationGenerated(fontDisplayValue) {
-  runTest(fontDisplayValue, check_violation_report_format);
-}
-
-function testFontDisplayPolicyReportOnlyGenerated(fontDisplayValue) {
-  runTest(fontDisplayValue, check_report_only_report_format);
-}
-
-function testCompliantWithFontDisplayPolicy(fontDisplayValue) {
-  runTest(fontDisplayValue);
-}
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/html/infrastructure/safe-passing-of-structured-data/structured-cloning-error-stack-optional.sub.window-expected.txt b/third_party/blink/web_tests/external/wpt/html/infrastructure/safe-passing-of-structured-data/structured-cloning-error-stack-optional.sub.window-expected.txt
index 3cc0dba1f..9a732a9 100644
--- a/third_party/blink/web_tests/external/wpt/html/infrastructure/safe-passing-of-structured-data/structured-cloning-error-stack-optional.sub.window-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/html/infrastructure/safe-passing-of-structured-data/structured-cloning-error-stack-optional.sub.window-expected.txt
@@ -1,11 +1,11 @@
 This is a testharness.js-based test.
 [FAIL] web API-created DOMException (structuredClone())
-  assert_equals: expected (string) "Error: Failed to execute 'createElement' on 'Document': The tag name provided ('') is not a valid name.\\n    at http://web-platform.test:8001/html/infrastructure/safe-passing-of-structured-data/structured-cloning-error-stack-optional.sub.window.js:33:14\\n    at Test.<anonymous> (http://web-platform.test:8001/html/infrastructure/safe-passing-of-structured-data/structured-cloning-error-stack-optional.sub.window.js:41:19)\\n    at Test.step (http://web-platform.test:8001/resources/testharness.js:2622:25)\\n    at test (http://web-platform.test:8001/resources/testharness.js:633:30)\\n    at stackTests (http://web-platform.test:8001/html/infrastructure/safe-passing-of-structured-data/structured-cloning-error-stack-optional.sub.window.js:40:3)\\n    at http://web-platform.test:8001/html/infrastructure/safe-passing-of-structured-data/structured-cloning-error-stack-optional.sub.window.js:31:1" but got (undefined) undefined
+  assert_equals: expected (string) "InvalidCharacterError: Failed to execute 'createElement' on 'Document': The tag name provided ('') is not a valid name.\\n    at http://web-platform.test:8001/html/infrastructure/safe-passing-of-structured-data/structured-cloning-error-stack-optional.sub.window.js:33:14\\n    at Test.<anonymous> (http://web-platform.test:8001/html/infrastructure/safe-passing-of-structured-data/structured-cloning-error-stack-optional.sub.window.js:41:19)\\n    at Test.step (http://web-platform.test:8001/resources/testharness.js:2622:25)\\n    at test (http://web-platform.test:8001/resources/testharness.js:633:30)\\n    at stackTests (http://web-platform.test:8001/html/infrastructure/safe-passing-of-structured-data/structured-cloning-error-stack-optional.sub.window.js:40:3)\\n    at http://web-platform.test:8001/html/infrastructure/safe-passing-of-structured-data/structured-cloning-error-stack-optional.sub.window.js:31:1" but got (undefined) undefined
 [FAIL] web API-created DOMException (worker)
-  assert_equals: expected (string) "Error: Failed to execute 'createElement' on 'Document': The tag name provided ('') is not a valid name.\\n    at http://web-platform.test:8001/html/infrastructure/safe-passing-of-structured-data/structured-cloning-error-stack-optional.sub.window.js:33:14\\n    at Test.<anonymous> (http://web-platform.test:8001/html/infrastructure/safe-passing-of-structured-data/structured-cloning-error-stack-optional.sub.window.js:53:19)\\n    at Test.step (http://web-platform.test:8001/resources/testharness.js:2622:25)\\n    at async_test (http://web-platform.test:8001/resources/testharness.js:681:34)\\n    at stackTests (http://web-platform.test:8001/html/infrastructure/safe-passing-of-structured-data/structured-cloning-error-stack-optional.sub.window.js:52:3)\\n    at http://web-platform.test:8001/html/infrastructure/safe-passing-of-structured-data/structured-cloning-error-stack-optional.sub.window.js:31:1" but got (undefined) undefined
+  assert_equals: expected (string) "InvalidCharacterError: Failed to execute 'createElement' on 'Document': The tag name provided ('') is not a valid name.\\n    at http://web-platform.test:8001/html/infrastructure/safe-passing-of-structured-data/structured-cloning-error-stack-optional.sub.window.js:33:14\\n    at Test.<anonymous> (http://web-platform.test:8001/html/infrastructure/safe-passing-of-structured-data/structured-cloning-error-stack-optional.sub.window.js:53:19)\\n    at Test.step (http://web-platform.test:8001/resources/testharness.js:2622:25)\\n    at async_test (http://web-platform.test:8001/resources/testharness.js:681:34)\\n    at stackTests (http://web-platform.test:8001/html/infrastructure/safe-passing-of-structured-data/structured-cloning-error-stack-optional.sub.window.js:52:3)\\n    at http://web-platform.test:8001/html/infrastructure/safe-passing-of-structured-data/structured-cloning-error-stack-optional.sub.window.js:31:1" but got (undefined) undefined
 [FAIL] web API-created DOMException (cross-site iframe)
-  assert_equals: expected (string) "Error: Failed to execute 'createElement' on 'Document': The tag name provided ('') is not a valid name.\\n    at http://web-platform.test:8001/html/infrastructure/safe-passing-of-structured-data/structured-cloning-error-stack-optional.sub.window.js:33:14\\n    at iframeTest (http://web-platform.test:8001/html/infrastructure/safe-passing-of-structured-data/structured-cloning-error-stack-optional.sub.window.js:72:19)\\n    at Test.<anonymous> (http://web-platform.test:8001/html/infrastructure/safe-passing-of-structured-data/structured-cloning-error-stack-optional.sub.window.js:99:5)\\n    at Test.step (http://web-platform.test:8001/resources/testharness.js:2622:25)\\n    at async_test (http://web-platform.test:8001/resources/testharness.js:681:34)\\n    at stackTests (http://web-platform.test:8001/html/infrastructure/safe-passing-of-structured-data/structured-cloning-error-stack-optional.sub.window.js:96:3)\\n    at http://web-platform.test:8001/html/infrastructure/safe-passing-of-structured-data/structured-cloning-error-stack-optional.sub.window.js:31:1" but got (undefined) undefined
+  assert_equals: expected (string) "InvalidCharacterError: Failed to execute 'createElement' on 'Document': The tag name provided ('') is not a valid name.\\n    at http://web-platform.test:8001/html/infrastructure/safe-passing-of-structured-data/structured-cloning-error-stack-optional.sub.window.js:33:14\\n    at iframeTest (http://web-platform.test:8001/html/infrastructure/safe-passing-of-structured-data/structured-cloning-error-stack-optional.sub.window.js:72:19)\\n    at Test.<anonymous> (http://web-platform.test:8001/html/infrastructure/safe-passing-of-structured-data/structured-cloning-error-stack-optional.sub.window.js:99:5)\\n    at Test.step (http://web-platform.test:8001/resources/testharness.js:2622:25)\\n    at async_test (http://web-platform.test:8001/resources/testharness.js:681:34)\\n    at stackTests (http://web-platform.test:8001/html/infrastructure/safe-passing-of-structured-data/structured-cloning-error-stack-optional.sub.window.js:96:3)\\n    at http://web-platform.test:8001/html/infrastructure/safe-passing-of-structured-data/structured-cloning-error-stack-optional.sub.window.js:31:1" but got (undefined) undefined
 [FAIL] web API-created DOMException (same-origin iframe)
-  assert_equals: expected (string) "Error: Failed to execute 'createElement' on 'Document': The tag name provided ('') is not a valid name.\\n    at http://web-platform.test:8001/html/infrastructure/safe-passing-of-structured-data/structured-cloning-error-stack-optional.sub.window.js:33:14\\n    at iframeTest (http://web-platform.test:8001/html/infrastructure/safe-passing-of-structured-data/structured-cloning-error-stack-optional.sub.window.js:72:19)\\n    at Test.<anonymous> (http://web-platform.test:8001/html/infrastructure/safe-passing-of-structured-data/structured-cloning-error-stack-optional.sub.window.js:104:5)\\n    at Test.step (http://web-platform.test:8001/resources/testharness.js:2622:25)\\n    at async_test (http://web-platform.test:8001/resources/testharness.js:681:34)\\n    at stackTests (http://web-platform.test:8001/html/infrastructure/safe-passing-of-structured-data/structured-cloning-error-stack-optional.sub.window.js:102:3)\\n    at http://web-platform.test:8001/html/infrastructure/safe-passing-of-structured-data/structured-cloning-error-stack-optional.sub.window.js:31:1" but got (undefined) undefined
+  assert_equals: expected (string) "InvalidCharacterError: Failed to execute 'createElement' on 'Document': The tag name provided ('') is not a valid name.\\n    at http://web-platform.test:8001/html/infrastructure/safe-passing-of-structured-data/structured-cloning-error-stack-optional.sub.window.js:33:14\\n    at iframeTest (http://web-platform.test:8001/html/infrastructure/safe-passing-of-structured-data/structured-cloning-error-stack-optional.sub.window.js:72:19)\\n    at Test.<anonymous> (http://web-platform.test:8001/html/infrastructure/safe-passing-of-structured-data/structured-cloning-error-stack-optional.sub.window.js:104:5)\\n    at Test.step (http://web-platform.test:8001/resources/testharness.js:2622:25)\\n    at async_test (http://web-platform.test:8001/resources/testharness.js:681:34)\\n    at stackTests (http://web-platform.test:8001/html/infrastructure/safe-passing-of-structured-data/structured-cloning-error-stack-optional.sub.window.js:102:3)\\n    at http://web-platform.test:8001/html/infrastructure/safe-passing-of-structured-data/structured-cloning-error-stack-optional.sub.window.js:31:1" but got (undefined) undefined
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-select-element/stylable-select/select-open-invalidation-ref.html b/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-select-element/stylable-select/select-open-invalidation-ref.html
new file mode 100644
index 0000000..f5b70c49
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-select-element/stylable-select/select-open-invalidation-ref.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<link rel=stylesheet href="resources/stylable-select-styles.css">
+
+<style>
+button {
+  color: green;
+}
+</style>
+
+<div id=container class=stylable-select-container>
+  <button>button</button>
+  <div id=popover popover=auto anchor=container class=stylable-select-datalist>
+    <div tabindex=0 class=stylable-select-option>one</div>
+    <div class=stylable-select-option>two</div>
+  </div>
+</div>
+
+<script>
+document.getElementById('popover').showPopover();
+document.querySelector('.stylable-select-option').focus();
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-select-element/stylable-select/select-open-invalidation.tentative.html b/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-select-element/stylable-select/select-open-invalidation.tentative.html
new file mode 100644
index 0000000..809ae69
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-select-element/stylable-select/select-open-invalidation.tentative.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html class=reftest-wait>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://github.com/whatwg/html/issues/9799">
+<link rel=match href="select-open-invalidation-ref.html">
+
+<style>
+select > button {
+  color: red;
+}
+select:open > button {
+  color: green;
+}
+</style>
+
+<select style="appearance:base-select">
+  <button type=popover>button</button>
+  <datalist>
+    <option>one</option>
+    <option>two</option>
+  </datalist>
+</select>
+
+<script>
+(async () => {
+  await new Promise(requestAnimationFrame);
+  try {
+    document.querySelector('datalist').showPopover();
+  } catch (error) {}
+  await new Promise(requestAnimationFrame);
+  document.documentElement.classList.remove('reftest-wait');
+})();
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-dialog-element/dialog-close-via-attribute.html b/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-dialog-element/dialog-close-via-attribute.tentative.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-dialog-element/dialog-close-via-attribute.html
rename to third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-dialog-element/dialog-close-via-attribute.tentative.html
diff --git a/third_party/blink/web_tests/fast/dom/Window/property-access-on-cached-window-after-frame-navigated-expected.txt b/third_party/blink/web_tests/fast/dom/Window/property-access-on-cached-window-after-frame-navigated-expected.txt
index 3d991ae4..a310a9b46 100644
--- a/third_party/blink/web_tests/fast/dom/Window/property-access-on-cached-window-after-frame-navigated-expected.txt
+++ b/third_party/blink/web_tests/fast/dom/Window/property-access-on-cached-window-after-frame-navigated-expected.txt
@@ -212,6 +212,7 @@
 PASS oldChildWindow.onresize is newChildWindow.onresize
 PASS oldChildWindow.onscroll is newChildWindow.onscroll
 PASS oldChildWindow.onscrollend is newChildWindow.onscrollend
+PASS oldChildWindow.onscrollsnapchange is newChildWindow.onscrollsnapchange
 PASS oldChildWindow.onsearch is newChildWindow.onsearch
 PASS oldChildWindow.onsecuritypolicyviolation is newChildWindow.onsecuritypolicyviolation
 PASS oldChildWindow.onseeked is newChildWindow.onseeked
@@ -220,7 +221,6 @@
 PASS oldChildWindow.onselectionchange is newChildWindow.onselectionchange
 PASS oldChildWindow.onselectstart is newChildWindow.onselectstart
 PASS oldChildWindow.onslotchange is newChildWindow.onslotchange
-PASS oldChildWindow.onsnapchanged is newChildWindow.onsnapchanged
 PASS oldChildWindow.onsnapchanging is newChildWindow.onsnapchanging
 PASS oldChildWindow.onstalled is newChildWindow.onstalled
 PASS oldChildWindow.onstorage is newChildWindow.onstorage
diff --git a/third_party/blink/web_tests/fast/dom/Window/property-access-on-cached-window-after-frame-removed-and-gced-expected.txt b/third_party/blink/web_tests/fast/dom/Window/property-access-on-cached-window-after-frame-removed-and-gced-expected.txt
index 5fff1561..78833932 100644
--- a/third_party/blink/web_tests/fast/dom/Window/property-access-on-cached-window-after-frame-removed-and-gced-expected.txt
+++ b/third_party/blink/web_tests/fast/dom/Window/property-access-on-cached-window-after-frame-removed-and-gced-expected.txt
@@ -151,6 +151,7 @@
 PASS childWindow.onresize is null
 PASS childWindow.onscroll is null
 PASS childWindow.onscrollend is null
+PASS childWindow.onscrollsnapchange is null
 PASS childWindow.onsearch is null
 PASS childWindow.onsecuritypolicyviolation is null
 PASS childWindow.onseeked is null
@@ -159,7 +160,6 @@
 PASS childWindow.onselectionchange is null
 PASS childWindow.onselectstart is null
 PASS childWindow.onslotchange is null
-PASS childWindow.onsnapchanged is null
 PASS childWindow.onsnapchanging is null
 PASS childWindow.onstalled is null
 PASS childWindow.onstorage is null
diff --git a/third_party/blink/web_tests/fast/dom/Window/property-access-on-cached-window-after-frame-removed-expected.txt b/third_party/blink/web_tests/fast/dom/Window/property-access-on-cached-window-after-frame-removed-expected.txt
index cbe8b24..f720680a 100644
--- a/third_party/blink/web_tests/fast/dom/Window/property-access-on-cached-window-after-frame-removed-expected.txt
+++ b/third_party/blink/web_tests/fast/dom/Window/property-access-on-cached-window-after-frame-removed-expected.txt
@@ -151,6 +151,7 @@
 PASS childWindow.onresize is null
 PASS childWindow.onscroll is null
 PASS childWindow.onscrollend is null
+PASS childWindow.onscrollsnapchange is null
 PASS childWindow.onsearch is null
 PASS childWindow.onsecuritypolicyviolation is null
 PASS childWindow.onseeked is null
@@ -159,7 +160,6 @@
 PASS childWindow.onselectionchange is null
 PASS childWindow.onselectstart is null
 PASS childWindow.onslotchange is null
-PASS childWindow.onsnapchanged is null
 PASS childWindow.onsnapchanging is null
 PASS childWindow.onstalled is null
 PASS childWindow.onstorage is null
diff --git a/third_party/blink/web_tests/http/tests/devtools/console/console-log-linkify-stack-in-errors-expected.txt b/third_party/blink/web_tests/http/tests/devtools/console/console-log-linkify-stack-in-errors-expected.txt
index 2d3d8869..26f708b 100644
--- a/third_party/blink/web_tests/http/tests/devtools/console/console-log-linkify-stack-in-errors-expected.txt
+++ b/third_party/blink/web_tests/http/tests/devtools/console/console-log-linkify-stack-in-errors-expected.txt
@@ -7,7 +7,7 @@
 break
     at forStack (console-log-linkify-…-in-errors.js:17:23)
     at console-log-linkify-…k-in-errors.js:20:7
-console-log-linkify-…ack-in-errors.js:36 Error: Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of this node.
+console-log-linkify-…ack-in-errors.js:36 NotFoundError: Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of this node.
     at domError (console-log-linkify-…-in-errors.js:34:29)
     at console-log-linkify-…k-in-errors.js:40:7
 console-log-linkify-…ack-in-errors.js:47 Error: some error
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/attribution-reporting/multiple-source-headers-ignored-expected.txt b/third_party/blink/web_tests/http/tests/inspector-protocol/attribution-reporting/multiple-source-headers-ignored-expected.txt
deleted file mode 100644
index a2d52012..0000000
--- a/third_party/blink/web_tests/http/tests/inspector-protocol/attribution-reporting/multiple-source-headers-ignored-expected.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-Test that clicking an attributionsrc anchor which returns multiple Attribution-Reporting-Register-Source headers triggers an issue.
-Issue reported: {
-    code : AttributionReportingIssue
-    details : {
-        attributionReportingIssueDetails : {
-            request : <object>
-            violationType : SourceIgnored
-        }
-    }
-}
-
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/attribution-reporting/multiple-source-headers-ignored.js b/third_party/blink/web_tests/http/tests/inspector-protocol/attribution-reporting/multiple-source-headers-ignored.js
deleted file mode 100644
index 143239a..0000000
--- a/third_party/blink/web_tests/http/tests/inspector-protocol/attribution-reporting/multiple-source-headers-ignored.js
+++ /dev/null
@@ -1,25 +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.
-
-(async function(/** @type {import('test_runner').TestRunner} */ testRunner) {
-  const { dp } = await testRunner.startHTML(
-    '<a href="https://devtools.test:8443/inspector-protocol/attribution-reporting/resources/register-multiple-sources.php" attributionsrc target="_blank">Link</a>',
-    "Test that clicking an attributionsrc anchor which returns multiple Attribution-Reporting-Register-Source headers triggers an issue."
-  );
-
-  await dp.Audits.enable();
-
-  const issue = dp.Audits.onceIssueAdded();
-
-  await dp.Runtime.evaluate({
-    expression: `document.querySelector('a').click()`,
-    userGesture: true,
-  });
-
-  testRunner.log((await issue).params.issue, "Issue reported: ", [
-    "request",
-  ]);
-
-  testRunner.completeTest();
-});
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/attribution-reporting/multiple-trigger-headers-ignored-expected.txt b/third_party/blink/web_tests/http/tests/inspector-protocol/attribution-reporting/multiple-trigger-headers-ignored-expected.txt
deleted file mode 100644
index 7c98e32..0000000
--- a/third_party/blink/web_tests/http/tests/inspector-protocol/attribution-reporting/multiple-trigger-headers-ignored-expected.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-Test that clicking an attributionsrc anchor which returns multiple Attribution-Reporting-Register-Trigger headers triggers an issue.
-Issue reported: {
-    code : AttributionReportingIssue
-    details : {
-        attributionReportingIssueDetails : {
-            request : <object>
-            violationType : TriggerIgnored
-        }
-    }
-}
-
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/attribution-reporting/multiple-trigger-headers-ignored.js b/third_party/blink/web_tests/http/tests/inspector-protocol/attribution-reporting/multiple-trigger-headers-ignored.js
deleted file mode 100644
index bd63c9d..0000000
--- a/third_party/blink/web_tests/http/tests/inspector-protocol/attribution-reporting/multiple-trigger-headers-ignored.js
+++ /dev/null
@@ -1,31 +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.
-
-// TODO(@anthonygarant): Update to be a background ping instead of a navigation
-// the keepalive in-browser migration is enabled in the main
-// attribution-reporting WPT suite. With a navigation, the trigger registration
-// would be ignored regardless of the fact that there are multiple headers since
-// the eligibility is source only.
-
-(async function(/** @type {import('test_runner').TestRunner} */ testRunner) {
-  const { dp } = await testRunner.startHTML(
-    '<a href="https://devtools.test:8443/inspector-protocol/attribution-reporting/resources/register-multiple-triggers.php" attributionsrc target="_blank">Link</a>',
-    "Test that clicking an attributionsrc anchor which returns multiple Attribution-Reporting-Register-Trigger headers triggers an issue."
-  );
-
-  await dp.Audits.enable();
-
-  const issue = dp.Audits.onceIssueAdded();
-
-  await dp.Runtime.evaluate({
-    expression: `document.querySelector('a').click()`,
-    userGesture: true,
-  });
-
-  testRunner.log((await issue).params.issue, "Issue reported: ", [
-    "request",
-  ]);
-
-  testRunner.completeTest();
-});
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/attribution-reporting/resources/register-multiple-sources.php b/third_party/blink/web_tests/http/tests/inspector-protocol/attribution-reporting/resources/register-multiple-sources.php
deleted file mode 100644
index 5766df4..0000000
--- a/third_party/blink/web_tests/http/tests/inspector-protocol/attribution-reporting/resources/register-multiple-sources.php
+++ /dev/null
@@ -1,4 +0,0 @@
-<?php
-header('Attribution-Reporting-Register-Source: {"source_event_id":"0","destination":"https://a.example"}');
-header('Attribution-Reporting-Register-Source: {"source_event_id":"1","destination":"https://b.example"}', /*replace=*/false);
-?>
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/attribution-reporting/resources/register-multiple-triggers.php b/third_party/blink/web_tests/http/tests/inspector-protocol/attribution-reporting/resources/register-multiple-triggers.php
deleted file mode 100644
index a4152bd..0000000
--- a/third_party/blink/web_tests/http/tests/inspector-protocol/attribution-reporting/resources/register-multiple-triggers.php
+++ /dev/null
@@ -1,4 +0,0 @@
-<?php
-header('Attribution-Reporting-Register-Trigger: {}');
-header('Attribution-Reporting-Register-Trigger: {}', /*replace=*/false);
-?>
diff --git a/third_party/blink/web_tests/virtual/new-text-size-adjust-with-os-font-scale/README.md b/third_party/blink/web_tests/virtual/new-text-size-adjust-with-os-font-scale/README.md
new file mode 100644
index 0000000..defbbd1
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/new-text-size-adjust-with-os-font-scale/README.md
@@ -0,0 +1,3 @@
+Tests for a new implementation of text-size-adjust with "mobile"
+settings (viewport, text autosizing enabled) and an os font scale
+factor.
diff --git a/third_party/blink/web_tests/virtual/new-text-size-adjust/README.md b/third_party/blink/web_tests/virtual/new-text-size-adjust/README.md
new file mode 100644
index 0000000..f0f11a1
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/new-text-size-adjust/README.md
@@ -0,0 +1,2 @@
+Tests for a new implementation of text-size-adjust with "mobile"
+settings (viewport, text autosizing enabled).
diff --git a/third_party/blink/web_tests/virtual/old-text-size-adjust-with-os-font-scale/README.md b/third_party/blink/web_tests/virtual/old-text-size-adjust-with-os-font-scale/README.md
new file mode 100644
index 0000000..6109026c
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/old-text-size-adjust-with-os-font-scale/README.md
@@ -0,0 +1,3 @@
+Tests for the old implementation of text-size-adjust with "mobile"
+settings (viewport, text autosizing enabled) and an os font scale
+factor.
diff --git a/third_party/blink/web_tests/virtual/old-text-size-adjust/README.md b/third_party/blink/web_tests/virtual/old-text-size-adjust/README.md
new file mode 100644
index 0000000..18127864
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/old-text-size-adjust/README.md
@@ -0,0 +1,2 @@
+Tests for the old implementation of text-size-adjust with "mobile"
+settings (viewport, text autosizing enabled).
diff --git a/third_party/blink/web_tests/webexposed/element-instance-property-listing-expected.txt b/third_party/blink/web_tests/webexposed/element-instance-property-listing-expected.txt
index 098d904..c2fc0d4c 100644
--- a/third_party/blink/web_tests/webexposed/element-instance-property-listing-expected.txt
+++ b/third_party/blink/web_tests/webexposed/element-instance-property-listing-expected.txt
@@ -260,6 +260,7 @@
     property onresize
     property onscroll
     property onscrollend
+    property onscrollsnapchange
     property onsearch
     property onsecuritypolicyviolation
     property onseeked
@@ -268,7 +269,6 @@
     property onselectionchange
     property onselectstart
     property onslotchange
-    property onsnapchanged
     property onsnapchanging
     property onstalled
     property onsubmit
@@ -1534,6 +1534,7 @@
     property onresize
     property onscroll
     property onscrollend
+    property onscrollsnapchange
     property onsearch
     property onsecuritypolicyviolation
     property onseeked
@@ -1542,7 +1543,6 @@
     property onselectionchange
     property onselectstart
     property onslotchange
-    property onsnapchanged
     property onsnapchanging
     property onstalled
     property onsubmit
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
index d2e83d6..eeec5cb 100644
--- a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
+++ b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
@@ -2095,6 +2095,7 @@
     getter onresume
     getter onscroll
     getter onscrollend
+    getter onscrollsnapchange
     getter onsearch
     getter onsecuritypolicyviolation
     getter onseeked
@@ -2103,7 +2104,6 @@
     getter onselectionchange
     getter onselectstart
     getter onslotchange
-    getter onsnapchanged
     getter onsnapchanging
     getter onstalled
     getter onsubmit
@@ -2319,6 +2319,7 @@
     setter onresume
     setter onscroll
     setter onscrollend
+    setter onscrollsnapchange
     setter onsearch
     setter onsecuritypolicyviolation
     setter onseeked
@@ -2327,7 +2328,6 @@
     setter onselectionchange
     setter onselectstart
     setter onslotchange
-    setter onsnapchanged
     setter onsnapchanging
     setter onstalled
     setter onsubmit
@@ -3915,6 +3915,7 @@
     getter onresize
     getter onscroll
     getter onscrollend
+    getter onscrollsnapchange
     getter onsecuritypolicyviolation
     getter onseeked
     getter onseeking
@@ -3922,7 +3923,6 @@
     getter onselectionchange
     getter onselectstart
     getter onslotchange
-    getter onsnapchanged
     getter onsnapchanging
     getter onstalled
     getter onsubmit
@@ -4052,6 +4052,7 @@
     setter onresize
     setter onscroll
     setter onscrollend
+    setter onscrollsnapchange
     setter onsecuritypolicyviolation
     setter onseeked
     setter onseeking
@@ -4059,7 +4060,6 @@
     setter onselectionchange
     setter onselectstart
     setter onslotchange
-    setter onsnapchanged
     setter onsnapchanging
     setter onstalled
     setter onsubmit
@@ -6039,6 +6039,7 @@
     getter onresize
     getter onscroll
     getter onscrollend
+    getter onscrollsnapchange
     getter onsecuritypolicyviolation
     getter onseeked
     getter onseeking
@@ -6046,7 +6047,6 @@
     getter onselectionchange
     getter onselectstart
     getter onslotchange
-    getter onsnapchanged
     getter onsnapchanging
     getter onstalled
     getter onsubmit
@@ -6152,6 +6152,7 @@
     setter onresize
     setter onscroll
     setter onscrollend
+    setter onscrollsnapchange
     setter onsecuritypolicyviolation
     setter onseeked
     setter onseeking
@@ -6159,7 +6160,6 @@
     setter onselectionchange
     setter onselectstart
     setter onslotchange
-    setter onsnapchanged
     setter onsnapchanging
     setter onstalled
     setter onsubmit
@@ -8443,6 +8443,7 @@
     getter onresize
     getter onscroll
     getter onscrollend
+    getter onscrollsnapchange
     getter onsecuritypolicyviolation
     getter onseeked
     getter onseeking
@@ -8450,7 +8451,6 @@
     getter onselectionchange
     getter onselectstart
     getter onslotchange
-    getter onsnapchanged
     getter onsnapchanging
     getter onstalled
     getter onsubmit
@@ -8558,6 +8558,7 @@
     setter onresize
     setter onscroll
     setter onscrollend
+    setter onscrollsnapchange
     setter onsecuritypolicyviolation
     setter onseeked
     setter onseeking
@@ -8565,7 +8566,6 @@
     setter onselectionchange
     setter onselectstart
     setter onslotchange
-    setter onsnapchanged
     setter onsnapchanging
     setter onstalled
     setter onsubmit
@@ -12989,6 +12989,7 @@
     getter onresize
     getter onscroll
     getter onscrollend
+    getter onscrollsnapchange
     getter onsearch
     getter onsecuritypolicyviolation
     getter onseeked
@@ -12997,7 +12998,6 @@
     getter onselectionchange
     getter onselectstart
     getter onslotchange
-    getter onsnapchanged
     getter onsnapchanging
     getter onstalled
     getter onstorage
@@ -13223,6 +13223,7 @@
     setter onresize
     setter onscroll
     setter onscrollend
+    setter onscrollsnapchange
     setter onsearch
     setter onsecuritypolicyviolation
     setter onseeked
@@ -13231,7 +13232,6 @@
     setter onselectionchange
     setter onselectstart
     setter onslotchange
-    setter onsnapchanged
     setter onsnapchanging
     setter onstalled
     setter onstorage
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-size-adjust/text-size-adjust-affects-font-size-large.html b/third_party/blink/web_tests/wpt_internal/css/css-size-adjust/text-size-adjust-affects-font-size-large.html
new file mode 100644
index 0000000..f331ed5
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/css/css-size-adjust/text-size-adjust-affects-font-size-large.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<meta content="width=device-width,initial-scale=1.0,minimum-scale=1.0" name="viewport">
+<meta name="assert" content="text-size-adjust should affect font-size, even for large values.">
+<link rel="help" href="https://drafts.csswg.org/css-size-adjust/#propdef-text-size-adjust">
+<script src='/resources/testharness.js'></script>
+<script src='/resources/testharnessreport.js'></script>
+<script>
+  function runTest() {
+    // This should not be needed but is required to work around a bug in
+    // Chrome's old autosizer due to computed style being updated during layout.
+    // TODO(pdr): Remove this when b/340389272 launches.
+    const forceLayout = document.body.offsetHeight;
+
+    test(() => {
+      const fontSize = getComputedStyle(textAdjust2FontSize12)['font-size'];
+      assert_equals(fontSize, '24px');
+    }, 'Font-size on element with text-size-adjust: 200% and font-size 12px');
+
+    test(() => {
+      const fontSize = getComputedStyle(textAdjust2FontSize16)['font-size'];
+      assert_equals(fontSize, '32px');
+    }, 'Font-size on element with text-size-adjust: 200% and font-size 16px');
+
+    test(() => {
+      const fontSize = getComputedStyle(textAdjust2FontSize20)['font-size'];
+      assert_equals(fontSize, '40px');
+    }, 'Font-size on element with text-size-adjust: 200% and font-size 20px');
+  }
+</script>
+<body onload="runTest()" style="text-size-adjust: 200%;">
+  <div id="textAdjust2FontSize12" style="font-size: 12px;">Hello world</div>
+  <div id="textAdjust2FontSize16" style="font-size: 16px;">Hello world</div>
+  <div id="textAdjust2FontSize20" style="font-size: 20px;">Hello world</div>
+</body>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-size-adjust/text-size-adjust-affects-font-size-nested-change.html b/third_party/blink/web_tests/wpt_internal/css/css-size-adjust/text-size-adjust-affects-font-size-nested-change.html
new file mode 100644
index 0000000..64ed24f2
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/css/css-size-adjust/text-size-adjust-affects-font-size-nested-change.html
@@ -0,0 +1,68 @@
+<!DOCTYPE html>
+<meta content="width=device-width,initial-scale=1.0,minimum-scale=1.0" name="viewport">
+<meta name="assert" content="nested text-size-adjust changes should update font-size.">
+<link rel="help" href="https://drafts.csswg.org/css-size-adjust/#propdef-text-size-adjust">
+<script src='/resources/testharness.js'></script>
+<script src='/resources/testharnessreport.js'></script>
+<script>
+  function runTest() {
+    // This should not be needed but is required to work around a bug in
+    // Chrome's old autosizer due to computed style being updated during layout.
+    // TODO(pdr): Remove this when b/340389272 launches.
+    let forceLayout = document.body.offsetHeight;
+
+    test(() => {
+      const fontSize = getComputedStyle(rootWithTextSizeAdjust)['font-size'];
+      assert_equals(fontSize, '20px');
+    }, 'Font-size on element with text-size-adjust: 125%');
+
+    rootWithTextSizeAdjust.style.textSizeAdjust = '112.5%';
+
+    // This should not be needed but is required to work around a bug in
+    // Chrome's old autosizer due to computed style being updated during layout.
+    // TODO(pdr): Remove this when b/340389272 launches.
+    forceLayout = document.body.offsetHeight;
+
+    test(() => {
+      const fontSize = getComputedStyle(rootWithTextSizeAdjust)['font-size'];
+      assert_equals(fontSize, '18px');
+    }, 'Font-size on element with text-size-adjust: 112.5%');
+
+    test(() => {
+      const fontSize = getComputedStyle(child)['font-size'];
+      assert_equals(fontSize, '18px');
+    }, 'Font-size on child without text-size-adjust');
+
+    test(() => {
+      const fontSize = getComputedStyle(childWithTextSizeAdjust100)['font-size'];
+      assert_equals(fontSize, '16px');
+    }, 'Font-size on child with text-size-adjust: 100%');
+
+    test(() => {
+      const fontSize = getComputedStyle(childWithTextSizeAdjustNone)['font-size'];
+      assert_equals(fontSize, '16px');
+    }, 'Font-size on child with text-size-adjust: none');
+
+    test(() => {
+      const fontSize = getComputedStyle(childWithTextSizeAdjust200)['font-size'];
+      assert_equals(fontSize, '32px');
+    }, 'Font-size on child with text-size-adjust: 200%');
+  }
+</script>
+<body onload="runTest()">
+  <div id="rootWithTextSizeAdjust" style="text-size-adjust: 125%;">
+    Hello world
+    <div id="child">
+      Hello world
+    </div>
+    <div id="childWithTextSizeAdjust100" style="text-size-adjust: 100%;">
+      Hello world
+    </div>
+    <div id="childWithTextSizeAdjustNone" style="text-size-adjust: none;">
+      Hello world
+    </div>
+    <div id="childWithTextSizeAdjust200" style="text-size-adjust: 200%;">
+      Hello world
+    </div>
+  </div>
+</body>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-size-adjust/text-size-adjust-affects-font-size-nested.html b/third_party/blink/web_tests/wpt_internal/css/css-size-adjust/text-size-adjust-affects-font-size-nested.html
new file mode 100644
index 0000000..cbb40ae
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/css/css-size-adjust/text-size-adjust-affects-font-size-nested.html
@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<meta content="width=device-width,initial-scale=1.0,minimum-scale=1.0" name="viewport">
+<meta name="assert" content="text-size-adjust should affect font-size when nested.">
+<link rel="help" href="https://drafts.csswg.org/css-size-adjust/#propdef-text-size-adjust">
+<script src='/resources/testharness.js'></script>
+<script src='/resources/testharnessreport.js'></script>
+<script>
+  function runTest() {
+    // This should not be needed but is required to work around a bug in
+    // Chrome's old autosizer due to computed style being updated during layout.
+    // TODO(pdr): Remove this when b/340389272 launches.
+    const forceLayout = document.body.offsetHeight;
+
+    test(() => {
+      const fontSize = getComputedStyle(rootWithTextSizeAdjust125)['font-size'];
+      assert_equals(fontSize, '20px');
+    }, 'Font-size on element with text-size-adjust: 125%');
+
+    test(() => {
+      const fontSize = getComputedStyle(child)['font-size'];
+      assert_equals(fontSize, '20px');
+    }, 'Font-size on child without text-size-adjust');
+
+    test(() => {
+      const fontSize = getComputedStyle(childWithTextSizeAdjust100)['font-size'];
+      assert_equals(fontSize, '16px');
+    }, 'Font-size on child with text-size-adjust: 100%');
+
+    test(() => {
+      const fontSize = getComputedStyle(childWithTextSizeAdjustNone)['font-size'];
+      assert_equals(fontSize, '16px');
+    }, 'Font-size on child with text-size-adjust: none');
+
+    test(() => {
+      const fontSize = getComputedStyle(childWithTextSizeAdjust200)['font-size'];
+      assert_equals(fontSize, '32px');
+    }, 'Font-size on child with text-size-adjust: 200%');
+  }
+</script>
+<body onload="runTest()">
+  <div id="rootWithTextSizeAdjust125" style="text-size-adjust: 125%;">
+    Hello world
+    <div id="child">
+      Hello world
+    </div>
+    <div id="childWithTextSizeAdjust100" style="text-size-adjust: 100%;">
+      Hello world
+    </div>
+    <div id="childWithTextSizeAdjustNone" style="text-size-adjust: none;">
+      Hello world
+    </div>
+    <div id="childWithTextSizeAdjust200" style="text-size-adjust: 200%;">
+      Hello world
+    </div>
+  </div>
+</body>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-size-adjust/text-size-adjust-affects-font-size-with-narrow-page.html b/third_party/blink/web_tests/wpt_internal/css/css-size-adjust/text-size-adjust-affects-font-size-with-narrow-page.html
new file mode 100644
index 0000000..1e91581
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/css/css-size-adjust/text-size-adjust-affects-font-size-with-narrow-page.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<meta content="width=100" name="viewport">
+<meta name="assert" content="text-size-adjust should affect font-size, even if a narrow viewport is used.">
+<link rel="help" href="https://drafts.csswg.org/css-size-adjust/#propdef-text-size-adjust">
+<script src='/resources/testharness.js'></script>
+<script src='/resources/testharnessreport.js'></script>
+<script>
+  function runTest() {
+    // This should not be needed but is required to work around a bug in
+    // Chrome's old autosizer due to computed style being updated during layout.
+    // TODO(pdr): Remove this when b/340389272 launches.
+    const forceLayout = document.body.offsetHeight;
+
+    test(() => {
+      const fontSize = getComputedStyle(textSizeAdjust75)['font-size'];
+      assert_equals(fontSize, '12px');
+    }, 'Font-size on element with text-size-adjust: 75%');
+
+    test(() => {
+      const fontSize = getComputedStyle(textSizeAdjust100)['font-size'];
+      assert_equals(fontSize, '16px');
+    }, 'Font-size on element with text-size-adjust: 100%');
+
+    test(() => {
+      const fontSize = getComputedStyle(textSizeAdjust125)['font-size'];
+      assert_equals(fontSize, '20px');
+    }, 'Font-size on element with text-size-adjust: 125%');
+  }
+</script>
+<body onload="runTest()">
+  <div id="textSizeAdjust75" style="text-size-adjust: 75%;">Hello world</div>
+  <div id="textSizeAdjust100" style="text-size-adjust: 100%;">Hello world</div>
+  <div id="textSizeAdjust125" style="text-size-adjust: 125%;">Hello world</div>
+</body>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-size-adjust/text-size-adjust-affects-font-size.html b/third_party/blink/web_tests/wpt_internal/css/css-size-adjust/text-size-adjust-affects-font-size.html
new file mode 100644
index 0000000..3a66ff65
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/css/css-size-adjust/text-size-adjust-affects-font-size.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<meta content="width=device-width,initial-scale=1.0,minimum-scale=1.0" name="viewport">
+<meta name="assert" content="text-size-adjust should affect font-size.">
+<link rel="help" href="https://drafts.csswg.org/css-size-adjust/#propdef-text-size-adjust">
+<script src='/resources/testharness.js'></script>
+<script src='/resources/testharnessreport.js'></script>
+<script>
+  function runTest() {
+    // This should not be needed but is required to work around a bug in
+    // Chrome's old autosizer due to computed style being updated during layout.
+    // TODO(pdr): Remove this when b/340389272 launches.
+    const forceLayout = document.body.offsetHeight;
+
+    test(() => {
+      const fontSize = getComputedStyle(textSizeAdjust75)['font-size'];
+      assert_equals(fontSize, '12px');
+    }, 'Font-size on element with text-size-adjust: 75%');
+
+    test(() => {
+      const fontSize = getComputedStyle(textSizeAdjust100)['font-size'];
+      assert_equals(fontSize, '16px');
+    }, 'Font-size on element with text-size-adjust: 100%');
+
+    test(() => {
+      const fontSize = getComputedStyle(textSizeAdjust125)['font-size'];
+      assert_equals(fontSize, '20px');
+    }, 'Font-size on element with text-size-adjust: 125%');
+  }
+</script>
+<body onload="runTest()">
+  <div id="textSizeAdjust75" style="text-size-adjust: 75%;">Hello world</div>
+  <div id="textSizeAdjust100" style="text-size-adjust: 100%;">Hello world</div>
+  <div id="textSizeAdjust125" style="text-size-adjust: 125%;">Hello world</div>
+</body>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-size-adjust/text-size-adjust-none.html b/third_party/blink/web_tests/wpt_internal/css/css-size-adjust/text-size-adjust-none.html
new file mode 100644
index 0000000..1db1c01
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/css/css-size-adjust/text-size-adjust-none.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<meta name="assert" content="text-size-adjust: none should disable text size adjustment.">
+<link rel="help" href="https://drafts.csswg.org/css-size-adjust/#propdef-text-size-adjust">
+<script src='/resources/testharness.js'></script>
+<script src='/resources/testharnessreport.js'></script>
+<style>
+  body {
+    text-size-adjust: none;
+  }
+</style>
+
+<body onload="runTest()">
+  <div id="div">
+    Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello
+    Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello
+    Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello
+    Hello Hello Hello Hello
+  </div>
+</body>
+
+<script>
+  function runTest() {
+    // This should not be needed but is required to work around a bug in
+    // Chrome's old autosizer due to computed style being updated during layout.
+    // TODO(pdr): Remove this when b/340389272 launches.
+    const forceLayout = document.body.offsetHeight;
+
+    test(() => {
+      assert_equals(getComputedStyle(div)['font-size'], '16px');
+    }, 'Text-size-adjust:none should disable adjustment');
+  }
+</script>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-size-adjust/webkit-text-size-adjust-affects-font-size.html b/third_party/blink/web_tests/wpt_internal/css/css-size-adjust/webkit-text-size-adjust-affects-font-size.html
new file mode 100644
index 0000000..4f961fe
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/css/css-size-adjust/webkit-text-size-adjust-affects-font-size.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<meta name="assert" content="-webkit-text-size-adjust should affect font-size.">
+<link rel="help" href="https://drafts.csswg.org/css-size-adjust/#propdef-text-size-adjust">
+<script src='/resources/testharness.js'></script>
+<script src='/resources/testharnessreport.js'></script>
+<script>
+  function runTest() {
+    // This should not be needed but is required to work around a bug in
+    // Chrome's old autosizer due to computed style being updated during layout.
+    // TODO(pdr): Remove this when b/340389272 launches.
+    const forceLayout = document.body.offsetHeight;
+
+    test(() => {
+      const fontSize = getComputedStyle(textSizeAdjust75)['font-size'];
+      assert_equals(fontSize, '12px');
+    }, 'Font-size on element with -webkit-text-size-adjust: 75%');
+
+    test(() => {
+      const fontSize = getComputedStyle(textSizeAdjust100)['font-size'];
+      assert_equals(fontSize, '16px');
+    }, 'Font-size on element with -webkit-text-size-adjust: 100%');
+
+    test(() => {
+      const fontSize = getComputedStyle(textSizeAdjust125)['font-size'];
+      assert_equals(fontSize, '20px');
+    }, 'Font-size on element with -webkit-text-size-adjust: 125%');
+  }
+</script>
+<body onload="runTest()">
+  <div id="textSizeAdjust75" style="-webkit-text-size-adjust: 75%;">Hello world</div>
+  <div id="textSizeAdjust100" style="-webkit-text-size-adjust: 100%;">Hello world</div>
+  <div id="textSizeAdjust125" style="-webkit-text-size-adjust: 125%;">Hello world</div>
+</body>
diff --git a/third_party/chromite b/third_party/chromite
index 8199e2b..cca6a35 160000
--- a/third_party/chromite
+++ b/third_party/chromite
@@ -1 +1 @@
-Subproject commit 8199e2bbaef867e258148454b1ee7a472fe3a638
+Subproject commit cca6a353a89b50997d6122904e1465c5eb3e57ea
diff --git a/third_party/dawn b/third_party/dawn
index d25761d..6167f1b 160000
--- a/third_party/dawn
+++ b/third_party/dawn
@@ -1 +1 @@
-Subproject commit d25761da20b4fd38f3b1bf1cc53e3a40bc8932dd
+Subproject commit 6167f1b4634a9240b237942266d1f1617f1d16f1
diff --git a/third_party/depot_tools b/third_party/depot_tools
index b4fbce4..22d7982 160000
--- a/third_party/depot_tools
+++ b/third_party/depot_tools
@@ -1 +1 @@
-Subproject commit b4fbce4794de1b135797bcdcfcc7be5900c3d109
+Subproject commit 22d7982fb2f1aec831063ac82d29a19311601760
diff --git a/third_party/jni_zero/codegen/proxy_impl_java.py b/third_party/jni_zero/codegen/proxy_impl_java.py
index 8f74432..5b491af 100644
--- a/third_party/jni_zero/codegen/proxy_impl_java.py
+++ b/third_party/jni_zero/codegen/proxy_impl_java.py
@@ -6,125 +6,147 @@
 import common
 
 
+class _Context:
+
+  def __init__(self, jni_obj, gen_jni_class, script_name, per_file_natives):
+    self.jni_obj = jni_obj
+    self.gen_jni_class = gen_jni_class
+    self.script_name = script_name
+    self.per_file_natives = per_file_natives
+
+    self.interface_name = jni_obj.proxy_interface.name_with_dots
+    self.proxy_class = java_types.JavaClass(
+        f'{self.jni_obj.java_class.full_name_with_slashes}Jni')
+    self.type_resolver = java_types.TypeResolver(self.proxy_class)
+    self.type_resolver.imports = jni_obj.GetClassesToBeImported()
+
+
 def _implicit_array_class_param(native, type_resolver):
   return_type = native.return_type
   class_name = return_type.to_array_element_type().to_java(type_resolver)
-  ret = class_name + '.class'
-  if native.params:
-    ret = ', ' + ret
-  return ret
+  return class_name + '.class'
+
+
+def _proxy_method(sb, ctx, native, method_fqn):
+  return_type_str = native.return_type.to_java(ctx.type_resolver)
+  sig_params = native.params.to_java_declaration(ctx.type_resolver)
+
+  sb(f"""
+@Override
+public {return_type_str} {native.name}({sig_params})""")
+  with sb.block():
+    if native.first_param_cpp_type:
+      sb(f'assert {native.params[0].name} != 0;\n')
+    with sb.statement():
+      if not native.return_type.is_void():
+        sb(f'return ({return_type_str}) ')
+      sb(method_fqn)
+      with sb.param_list() as plist:
+        plist.extend(p.name for p in native.params)
+        if native.needs_implicit_array_element_class_param:
+          plist.append(_implicit_array_class_param(native, ctx.type_resolver))
+
+
+def _native_method(sb, native, method_fqn):
+  params = native.proxy_params.to_java_declaration()
+  return_type = native.proxy_return_type.to_java()
+  sb(f'private static native {return_type} {method_fqn}({params});\n')
+
+
+def _get_method(sb, ctx):
+  sb(f'\npublic static {ctx.interface_name} get()')
+  with sb.block():
+    if not ctx.per_file_natives:
+      sb(f"""\
+if ({ctx.gen_jni_class.name}.TESTING_ENABLED) {{
+  if (testInstance != null) {{
+    return testInstance;
+  }}
+  if ({ctx.gen_jni_class.name}.REQUIRE_MOCK) {{
+    throw new UnsupportedOperationException(
+        "No mock found for the native implementation of {ctx.interface_name}. "
+        + "The current configuration requires implementations be mocked.");
+  }}
+}}
+""")
+    sb(f"""\
+NativeLibraryLoadedStatus.checkLoaded();
+return new {ctx.proxy_class.name}();
+""")
+
+
+def _class_body(sb, ctx):
+  sb(f"""\
+private static {ctx.interface_name} testInstance;
+
+public static final JniStaticTestMocker<{ctx.interface_name}> TEST_HOOKS =
+    new JniStaticTestMocker<{ctx.interface_name}>()""")
+  with sb.block(end='};\n'):
+    sb(f"""\
+@Override
+public void setInstanceForTesting({ctx.interface_name} instance)""")
+    with sb.block():
+      if not ctx.per_file_natives:
+        sb(f"""\
+if (!{ctx.gen_jni_class.name}.TESTING_ENABLED) {{
+  throw new RuntimeException(
+      "Tried to set a JNI mock when mocks aren't enabled!");
+}}
+""")
+      sb('testInstance = instance;\n')
+
+  for native in ctx.jni_obj.proxy_natives:
+    if ctx.per_file_natives:
+      method_fqn = f'native{common.capitalize(native.name)}'
+    else:
+      method_fqn = f'{ctx.gen_jni_class.name}.{native.proxy_name}'
+
+    _proxy_method(sb, ctx, native, method_fqn)
+    if ctx.per_file_natives:
+      _native_method(sb, native, method_fqn)
+
+  _get_method(sb, ctx)
+
+
+def _imports(sb, ctx):
+  classes = {
+      'org.jni_zero.CheckDiscard',
+      'org.jni_zero.JniStaticTestMocker',
+      'org.jni_zero.NativeLibraryLoadedStatus',
+  }
+  if not ctx.per_file_natives:
+    classes.add(ctx.gen_jni_class.full_name_with_dots)
+
+  for c in ctx.type_resolver.imports:
+    if c.is_nested:
+      # We will refer to all nested classes by OuterClass.InnerClass. We do this
+      # to reduce risk of naming collisions.
+      c = c.get_outer_class()
+    classes.add(c.full_name_with_dots)
+
+  for c in sorted(classes):
+    sb(f'import {c};\n')
 
 
 def Generate(jni_obj, *, gen_jni_class, script_name, per_file_natives=False):
-  proxy_class = java_types.JavaClass(
-      f'{jni_obj.java_class.full_name_with_slashes}Jni')
-  visibility = 'public ' if jni_obj.proxy_visibility == 'public' else ''
-  interface_name = jni_obj.proxy_interface.name_with_dots
-  gen_jni = gen_jni_class.name
-  type_resolver = java_types.TypeResolver(proxy_class)
-  type_resolver.imports = jni_obj.GetClassesToBeImported()
+  ctx = _Context(jni_obj, gen_jni_class, script_name, per_file_natives)
 
-  sb = []
-  sb.append(f"""\
+  sb = common.StringBuilder()
+  sb(f"""\
 //
 // This file was generated by {script_name}
 //
 package {jni_obj.java_class.class_without_prefix.package_with_dots};
 
 """)
+  _imports(sb, ctx)
+  sb('\n')
 
-  import_classes = {
-      'org.jni_zero.CheckDiscard', 'org.jni_zero.JniStaticTestMocker',
-      'org.jni_zero.NativeLibraryLoadedStatus'
-  }
+  visibility = 'public ' if jni_obj.proxy_visibility == 'public' else ''
+  class_name = ctx.proxy_class.name
   if not per_file_natives:
-    import_classes.add(gen_jni_class.full_name_with_dots)
-
-  for c in type_resolver.imports:
-    if c.is_nested:
-      # We will refer to all nested classes by OuterClass.InnerClass. We do this
-      # to reduce risk of naming collisions.
-      c = c.get_outer_class()
-    import_classes.add(c.full_name_with_dots)
-  for c in sorted(import_classes):
-    sb.append(f'import {c};\n')
-
-  if not per_file_natives:
-    sb.append('\n@CheckDiscard("crbug.com/993421")')
-  sb.append(f"""
-{visibility}class {proxy_class.name} implements {interface_name} {{
-  private static {interface_name} testInstance;
-
-  public static final JniStaticTestMocker<{interface_name}> TEST_HOOKS =
-      new JniStaticTestMocker<{interface_name}>() {{
-    @Override
-    public void setInstanceForTesting({interface_name} instance) {{
-""")
-  if not per_file_natives:
-    sb.append(f"""      if (!{gen_jni}.TESTING_ENABLED) {{
-        throw new RuntimeException(
-            "Tried to set a JNI mock when mocks aren't enabled!");
-      }}
-""")
-
-  sb.append("""      testInstance = instance;
-    }
-  };
-""")
-
-  for native in jni_obj.proxy_natives:
-    call_params = native.params.to_call_str()
-    if native.needs_implicit_array_element_class_param:
-      call_params += _implicit_array_class_param(native, type_resolver)
-    sig_params = native.params.to_java_declaration(type_resolver)
-    return_type_str = native.return_type.to_java(type_resolver)
-    return_prefix = ''
-    if not native.return_type.is_void():
-      return_prefix = f'return ({return_type_str}) '
-
-    if per_file_natives:
-      method_fqn = f'native{common.capitalize(native.name)}'
-    else:
-      method_fqn = f'{gen_jni}.{native.proxy_name}'
-
-    sb.append(f"""
-  @Override
-  public {return_type_str} {native.name}({sig_params}) {{
-""")
-    if native.first_param_cpp_type:
-      sb.append(f"""\
-    assert {native.params[0].name} != 0;
-""")
-    sb.append(f"""\
-    {return_prefix}{method_fqn}({call_params});
-  }}
-""")
-
-    if per_file_natives:
-      proxy_sig_params = native.proxy_params.to_java_declaration()
-      proxy_return_type = native.proxy_return_type.to_java()
-      sb.append(
-          f'  private static native {proxy_return_type} {method_fqn}({proxy_sig_params});\n'
-      )
-
-  sb.append(f"""
-  public static {interface_name} get() {{""")
-  if not per_file_natives:
-    sb.append(f"""
-    if ({gen_jni}.TESTING_ENABLED) {{
-      if (testInstance != null) {{
-        return testInstance;
-      }}
-      if ({gen_jni}.REQUIRE_MOCK) {{
-        throw new UnsupportedOperationException(
-            "No mock found for the native implementation of {interface_name}. "
-            + "The current configuration requires implementations be mocked.");
-      }}
-    }}""")
-  sb.append(f"""
-    NativeLibraryLoadedStatus.checkLoaded();
-    return new {proxy_class.name}();
-  }}
-}}
-""")
-  return ''.join(sb)
+    sb('@CheckDiscard("crbug.com/993421")\n')
+  sb(f'{visibility}class {class_name} implements {ctx.interface_name}')
+  with sb.block():
+    _class_body(sb, ctx)
+  return sb.to_string()
diff --git a/third_party/jni_zero/common.py b/third_party/jni_zero/common.py
index 404236a..95e9b50 100644
--- a/third_party/jni_zero/common.py
+++ b/third_party/jni_zero/common.py
@@ -51,14 +51,15 @@
       return self._param_list_generator()
 
     self('(')
-    punctuation_size = 2 * len(values) # punctuation: ", ()"
-    single_line_size = sum(len(v) for v in values) + punctuation_size
-    if self._cur_line_length() + single_line_size < _TARGET_LINE_LENGTH:
-      self(', '.join(values))
-    else:
-      self('\n')
-      with self.indent(4):
-        self(',\n'.join(values))
+    if values:
+      punctuation_size = 2 * len(values) # punctuation: ", ()"
+      single_line_size = sum(len(v) for v in values) + punctuation_size
+      if self._cur_line_length() + single_line_size < _TARGET_LINE_LENGTH:
+        self(', '.join(values))
+      else:
+        self('\n')
+        with self.indent(4):
+          self(',\n'.join(values))
     self(')')
 
   @contextlib.contextmanager
@@ -67,11 +68,11 @@
     self(';\n')
 
   @contextlib.contextmanager
-  def block(self):
-    self(' {\n')
+  def block(self, start=' {\n', end='}\n'):
+    self(start)
     with self.indent(2):
       yield
-    self('}\n')
+    self(end)
 
   @contextlib.contextmanager
   def indent(self, amount):
diff --git a/third_party/jni_zero/test/golden/testBidirectionalClass-SampleForTestsJni.java.golden b/third_party/jni_zero/test/golden/testBidirectionalClass-SampleForTestsJni.java.golden
index b2ce04f..667d6fcf 100644
--- a/third_party/jni_zero/test/golden/testBidirectionalClass-SampleForTestsJni.java.golden
+++ b/third_party/jni_zero/test/golden/testBidirectionalClass-SampleForTestsJni.java.golden
@@ -36,7 +36,10 @@
   @Override
   public void destroy(long nativeCPPClass, SampleForTests caller, byte[] bytes) {
     assert nativeCPPClass != 0;
-    GEN_JNI.org_jni_1zero_SampleForTests_destroy(nativeCPPClass, caller, bytes);
+    GEN_JNI.org_jni_1zero_SampleForTests_destroy(
+        nativeCPPClass,
+        caller,
+        bytes);
   }
 
   @Override
@@ -46,7 +49,8 @@
 
   @Override
   public double getDoubleFunction(SampleForTests ret) {
-    return (double) GEN_JNI.org_jni_1zero_SampleForTests_getDoubleFunction(ret);
+    return (double) GEN_JNI.org_jni_1zero_SampleForTests_getDoubleFunction(
+        ret);
   }
 
   @Override
@@ -61,7 +65,8 @@
 
   @Override
   public Object getNonPODDatatype(SampleForTests jcaller) {
-    return (Object) GEN_JNI.org_jni_1zero_SampleForTests_getNonPODDatatype(jcaller);
+    return (Object) GEN_JNI.org_jni_1zero_SampleForTests_getNonPODDatatype(
+        jcaller);
   }
 
   @Override
@@ -71,31 +76,45 @@
 
   @Override
   public long init(SampleForTests caller, String param, byte[] bytes, SampleForTests convertedType, SampleForTests[] nonConvertedArray) {
-    return (long) GEN_JNI.org_jni_1zero_SampleForTests_init(caller, param, bytes, convertedType, nonConvertedArray);
+    return (long) GEN_JNI.org_jni_1zero_SampleForTests_init(
+        caller,
+        param,
+        bytes,
+        convertedType,
+        nonConvertedArray);
   }
 
   @Override
   public void iterateAndDoSomethingWithStructB(long nativeCPPClass, SampleForTests caller) {
     assert nativeCPPClass != 0;
-    GEN_JNI.org_jni_1zero_SampleForTests_iterateAndDoSomethingWithStructB(nativeCPPClass, caller);
+    GEN_JNI.org_jni_1zero_SampleForTests_iterateAndDoSomethingWithStructB(
+        nativeCPPClass,
+        caller);
   }
 
   @Override
   public int method(long nativeCPPClass, SampleForTests caller, String[] strings) {
     assert nativeCPPClass != 0;
-    return (int) GEN_JNI.org_jni_1zero_SampleForTests_method(nativeCPPClass, caller, strings);
+    return (int) GEN_JNI.org_jni_1zero_SampleForTests_method(
+        nativeCPPClass,
+        caller,
+        strings);
   }
 
   @Override
   public double methodOtherP0(long nativePtr, SampleForTests caller) {
     assert nativePtr != 0;
-    return (double) GEN_JNI.org_jni_1zero_SampleForTests_methodOtherP0(nativePtr, caller);
+    return (double) GEN_JNI.org_jni_1zero_SampleForTests_methodOtherP0(
+        nativePtr,
+        caller);
   }
 
   @Override
   public String returnAString(long nativeCPPClass, SampleForTests caller) {
     assert nativeCPPClass != 0;
-    return (String) GEN_JNI.org_jni_1zero_SampleForTests_returnAString(nativeCPPClass, caller);
+    return (String) GEN_JNI.org_jni_1zero_SampleForTests_returnAString(
+        nativeCPPClass,
+        caller);
   }
 
   @Override
diff --git a/third_party/jni_zero/test/golden/testEndToEndProxyHashed-SampleForAnnotationProcessorJni.java.golden b/third_party/jni_zero/test/golden/testEndToEndProxyHashed-SampleForAnnotationProcessorJni.java.golden
index 00fedea..d7752cbf 100644
--- a/third_party/jni_zero/test/golden/testEndToEndProxyHashed-SampleForAnnotationProcessorJni.java.golden
+++ b/third_party/jni_zero/test/golden/testEndToEndProxyHashed-SampleForAnnotationProcessorJni.java.golden
@@ -30,7 +30,8 @@
 
   @Override
   public SampleForAnnotationProcessor bar(SampleForAnnotationProcessor sample) {
-    return (SampleForAnnotationProcessor) GEN_JNI.org_jni_1zero_SampleForAnnotationProcessor_bar(sample);
+    return (SampleForAnnotationProcessor) GEN_JNI.org_jni_1zero_SampleForAnnotationProcessor_bar(
+        sample);
   }
 
   @Override
@@ -55,7 +56,8 @@
 
   @Override
   public SampleForAnnotationProcessor[] returnConvertedAppObjects() {
-    return (SampleForAnnotationProcessor[]) GEN_JNI.org_jni_1zero_SampleForAnnotationProcessor_returnConvertedAppObjects(SampleForAnnotationProcessor.class);
+    return (SampleForAnnotationProcessor[]) GEN_JNI.org_jni_1zero_SampleForAnnotationProcessor_returnConvertedAppObjects(
+        SampleForAnnotationProcessor.class);
   }
 
   @Override
@@ -115,27 +117,64 @@
 
   @Override
   public String revString(String stringToReverse) {
-    return (String) GEN_JNI.org_jni_1zero_SampleForAnnotationProcessor_revString(stringToReverse);
+    return (String) GEN_JNI.org_jni_1zero_SampleForAnnotationProcessor_revString(
+        stringToReverse);
   }
 
   @Override
   public SampleForAnnotationProcessor[] sendSamplesToNative(SampleForAnnotationProcessor[] strs) {
-    return (SampleForAnnotationProcessor[]) GEN_JNI.org_jni_1zero_SampleForAnnotationProcessor_sendSamplesToNative(strs);
+    return (SampleForAnnotationProcessor[]) GEN_JNI.org_jni_1zero_SampleForAnnotationProcessor_sendSamplesToNative(
+        strs);
   }
 
   @Override
   public String[] sendToNative(String[] strs) {
-    return (String[]) GEN_JNI.org_jni_1zero_SampleForAnnotationProcessor_sendToNative(strs);
+    return (String[]) GEN_JNI.org_jni_1zero_SampleForAnnotationProcessor_sendToNative(
+        strs);
   }
 
   @Override
   public int[] testAllPrimitives(int zint, int[] ints, long zlong, long[] longs, short zshort, short[] shorts, char zchar, char[] chars, byte zbyte, byte[] bytes, double zdouble, double[] doubles, float zfloat, float[] floats, boolean zbool, boolean[] bools) {
-    return (int[]) GEN_JNI.org_jni_1zero_SampleForAnnotationProcessor_testAllPrimitives(zint, ints, zlong, longs, zshort, shorts, zchar, chars, zbyte, bytes, zdouble, doubles, zfloat, floats, zbool, bools);
+    return (int[]) GEN_JNI.org_jni_1zero_SampleForAnnotationProcessor_testAllPrimitives(
+        zint,
+        ints,
+        zlong,
+        longs,
+        zshort,
+        shorts,
+        zchar,
+        chars,
+        zbyte,
+        bytes,
+        zdouble,
+        doubles,
+        zfloat,
+        floats,
+        zbool,
+        bools);
   }
 
   @Override
   public void testSpecialTypes(Class clazz, Class[] classes, Throwable throwable, Throwable[] throwables, String string, String[] strings, String convertedString, String[] convertedStrings, String optionalString, SampleForAnnotationProcessor.TestStruct tStruct, SampleForAnnotationProcessor.TestStruct[] structs, Object obj, Object convertedObj, Object[] objects, MyClass.SecondNestedInterface nestedInterface, View view, Context context, Object[] convertedObjects) {
-    GEN_JNI.org_jni_1zero_SampleForAnnotationProcessor_testSpecialTypes(clazz, classes, throwable, throwables, string, strings, convertedString, convertedStrings, optionalString, tStruct, structs, obj, convertedObj, objects, nestedInterface, view, context, convertedObjects);
+    GEN_JNI.org_jni_1zero_SampleForAnnotationProcessor_testSpecialTypes(
+        clazz,
+        classes,
+        throwable,
+        throwables,
+        string,
+        strings,
+        convertedString,
+        convertedStrings,
+        optionalString,
+        tStruct,
+        structs,
+        obj,
+        convertedObj,
+        objects,
+        nestedInterface,
+        view,
+        context,
+        convertedObjects);
   }
 
   public static SampleForAnnotationProcessor.Natives get() {
diff --git a/third_party/jni_zero/test/golden/testForTestingKept-SampleProxyEdgeCasesJni.java.golden b/third_party/jni_zero/test/golden/testForTestingKept-SampleProxyEdgeCasesJni.java.golden
index 881a0c38..04f89b3a 100644
--- a/third_party/jni_zero/test/golden/testForTestingKept-SampleProxyEdgeCasesJni.java.golden
+++ b/third_party/jni_zero/test/golden/testForTestingKept-SampleProxyEdgeCasesJni.java.golden
@@ -30,17 +30,23 @@
 
   @Override
   public int addStructB(SampleForTests caller, SampleForTests.InnerStructB b) {
-    return (int) GEN_JNI.org_jni_1zero_SampleProxyEdgeCases_addStructB(caller, b);
+    return (int) GEN_JNI.org_jni_1zero_SampleProxyEdgeCases_addStructB(
+        caller,
+        b);
   }
 
   @Override
   public String[][] arrayTypes1(int[] a, Object[][] b) {
-    return (String[][]) GEN_JNI.org_jni_1zero_SampleProxyEdgeCases_arrayTypes1(a, b);
+    return (String[][]) GEN_JNI.org_jni_1zero_SampleProxyEdgeCases_arrayTypes1(
+        a,
+        b);
   }
 
   @Override
   public int[] arrayTypes2(int[] a, Throwable[][] b) {
-    return (int[]) GEN_JNI.org_jni_1zero_SampleProxyEdgeCases_arrayTypes2(a, b);
+    return (int[]) GEN_JNI.org_jni_1zero_SampleProxyEdgeCases_arrayTypes2(
+        a,
+        b);
   }
 
   @Override
@@ -65,7 +71,8 @@
 
   @Override
   public Map[] genericsWithNestedClassArray(Map[] arg) {
-    return (Map[]) GEN_JNI.org_jni_1zero_SampleProxyEdgeCases_genericsWithNestedClassArray(arg);
+    return (Map[]) GEN_JNI.org_jni_1zero_SampleProxyEdgeCases_genericsWithNestedClassArray(
+        arg);
   }
 
   @Override
@@ -75,12 +82,14 @@
 
   @Override
   public boolean setStringBuilder(StringBuilder sb) {
-    return (boolean) GEN_JNI.org_jni_1zero_SampleProxyEdgeCases_setStringBuilder(sb);
+    return (boolean) GEN_JNI.org_jni_1zero_SampleProxyEdgeCases_setStringBuilder(
+        sb);
   }
 
   @Override
   public int setStringBuilder(int sb) {
-    return (int) GEN_JNI.org_jni_1zero_SampleProxyEdgeCases_setStringBuilder(sb);
+    return (int) GEN_JNI.org_jni_1zero_SampleProxyEdgeCases_setStringBuilder(
+        sb);
   }
 
   public static SampleProxyEdgeCases.Natives get() {
diff --git a/third_party/jni_zero/test/golden/testPackagePrefixGenerator-SampleForTestsJni.java.golden b/third_party/jni_zero/test/golden/testPackagePrefixGenerator-SampleForTestsJni.java.golden
index f6489c4..d81fafeb 100644
--- a/third_party/jni_zero/test/golden/testPackagePrefixGenerator-SampleForTestsJni.java.golden
+++ b/third_party/jni_zero/test/golden/testPackagePrefixGenerator-SampleForTestsJni.java.golden
@@ -30,23 +30,31 @@
   @Override
   public void addStructB(long nativeCPPClass, SampleForTests caller, SampleForTests.InnerStructB b) {
     assert nativeCPPClass != 0;
-    GEN_JNI.this_is_a_package_prefix_org_jni_1zero_SampleForTests_addStructB(nativeCPPClass, caller, b);
+    GEN_JNI.this_is_a_package_prefix_org_jni_1zero_SampleForTests_addStructB(
+        nativeCPPClass,
+        caller,
+        b);
   }
 
   @Override
   public void destroy(long nativeCPPClass, SampleForTests caller, byte[] bytes) {
     assert nativeCPPClass != 0;
-    GEN_JNI.this_is_a_package_prefix_org_jni_1zero_SampleForTests_destroy(nativeCPPClass, caller, bytes);
+    GEN_JNI.this_is_a_package_prefix_org_jni_1zero_SampleForTests_destroy(
+        nativeCPPClass,
+        caller,
+        bytes);
   }
 
   @Override
   public Class getClass(Class env) {
-    return (Class) GEN_JNI.this_is_a_package_prefix_org_jni_1zero_SampleForTests_getClass(env);
+    return (Class) GEN_JNI.this_is_a_package_prefix_org_jni_1zero_SampleForTests_getClass(
+        env);
   }
 
   @Override
   public double getDoubleFunction(SampleForTests ret) {
-    return (double) GEN_JNI.this_is_a_package_prefix_org_jni_1zero_SampleForTests_getDoubleFunction(ret);
+    return (double) GEN_JNI.this_is_a_package_prefix_org_jni_1zero_SampleForTests_getDoubleFunction(
+        ret);
   }
 
   @Override
@@ -56,51 +64,70 @@
 
   @Override
   public Map getMap(Map arg0) {
-    return (Map) GEN_JNI.this_is_a_package_prefix_org_jni_1zero_SampleForTests_getMap(arg0);
+    return (Map) GEN_JNI.this_is_a_package_prefix_org_jni_1zero_SampleForTests_getMap(
+        arg0);
   }
 
   @Override
   public Object getNonPODDatatype(SampleForTests jcaller) {
-    return (Object) GEN_JNI.this_is_a_package_prefix_org_jni_1zero_SampleForTests_getNonPODDatatype(jcaller);
+    return (Object) GEN_JNI.this_is_a_package_prefix_org_jni_1zero_SampleForTests_getNonPODDatatype(
+        jcaller);
   }
 
   @Override
   public Throwable getThrowable(Throwable arg0) {
-    return (Throwable) GEN_JNI.this_is_a_package_prefix_org_jni_1zero_SampleForTests_getThrowable(arg0);
+    return (Throwable) GEN_JNI.this_is_a_package_prefix_org_jni_1zero_SampleForTests_getThrowable(
+        arg0);
   }
 
   @Override
   public long init(SampleForTests caller, String param, byte[] bytes, SampleForTests convertedType, SampleForTests[] nonConvertedArray) {
-    return (long) GEN_JNI.this_is_a_package_prefix_org_jni_1zero_SampleForTests_init(caller, param, bytes, convertedType, nonConvertedArray);
+    return (long) GEN_JNI.this_is_a_package_prefix_org_jni_1zero_SampleForTests_init(
+        caller,
+        param,
+        bytes,
+        convertedType,
+        nonConvertedArray);
   }
 
   @Override
   public void iterateAndDoSomethingWithStructB(long nativeCPPClass, SampleForTests caller) {
     assert nativeCPPClass != 0;
-    GEN_JNI.this_is_a_package_prefix_org_jni_1zero_SampleForTests_iterateAndDoSomethingWithStructB(nativeCPPClass, caller);
+    GEN_JNI.this_is_a_package_prefix_org_jni_1zero_SampleForTests_iterateAndDoSomethingWithStructB(
+        nativeCPPClass,
+        caller);
   }
 
   @Override
   public int method(long nativeCPPClass, SampleForTests caller, String[] strings) {
     assert nativeCPPClass != 0;
-    return (int) GEN_JNI.this_is_a_package_prefix_org_jni_1zero_SampleForTests_method(nativeCPPClass, caller, strings);
+    return (int) GEN_JNI.this_is_a_package_prefix_org_jni_1zero_SampleForTests_method(
+        nativeCPPClass,
+        caller,
+        strings);
   }
 
   @Override
   public double methodOtherP0(long nativePtr, SampleForTests caller) {
     assert nativePtr != 0;
-    return (double) GEN_JNI.this_is_a_package_prefix_org_jni_1zero_SampleForTests_methodOtherP0(nativePtr, caller);
+    return (double) GEN_JNI.this_is_a_package_prefix_org_jni_1zero_SampleForTests_methodOtherP0(
+        nativePtr,
+        caller);
   }
 
   @Override
   public String returnAString(long nativeCPPClass, SampleForTests caller) {
     assert nativeCPPClass != 0;
-    return (String) GEN_JNI.this_is_a_package_prefix_org_jni_1zero_SampleForTests_returnAString(nativeCPPClass, caller);
+    return (String) GEN_JNI.this_is_a_package_prefix_org_jni_1zero_SampleForTests_returnAString(
+        nativeCPPClass,
+        caller);
   }
 
   @Override
   public void setNonPODDatatype(SampleForTests obj, Rect rect) {
-    GEN_JNI.this_is_a_package_prefix_org_jni_1zero_SampleForTests_setNonPODDatatype(obj, rect);
+    GEN_JNI.this_is_a_package_prefix_org_jni_1zero_SampleForTests_setNonPODDatatype(
+        obj,
+        rect);
   }
 
   public static SampleForTests.Natives get() {
diff --git a/third_party/jni_zero/test/golden/testPerFileNatives-SampleForAnnotationProcessorJni.java.golden b/third_party/jni_zero/test/golden/testPerFileNatives-SampleForAnnotationProcessorJni.java.golden
index 0aaa4348..9345910 100644
--- a/third_party/jni_zero/test/golden/testPerFileNatives-SampleForAnnotationProcessorJni.java.golden
+++ b/third_party/jni_zero/test/golden/testPerFileNatives-SampleForAnnotationProcessorJni.java.golden
@@ -54,7 +54,8 @@
 
   @Override
   public SampleForAnnotationProcessor[] returnConvertedAppObjects() {
-    return (SampleForAnnotationProcessor[]) nativeReturnConvertedAppObjects(SampleForAnnotationProcessor.class);
+    return (SampleForAnnotationProcessor[]) nativeReturnConvertedAppObjects(
+        SampleForAnnotationProcessor.class);
   }
   private static native Object[] nativeReturnConvertedAppObjects(Class __arrayClazz);
 
@@ -144,13 +145,47 @@
 
   @Override
   public int[] testAllPrimitives(int zint, int[] ints, long zlong, long[] longs, short zshort, short[] shorts, char zchar, char[] chars, byte zbyte, byte[] bytes, double zdouble, double[] doubles, float zfloat, float[] floats, boolean zbool, boolean[] bools) {
-    return (int[]) nativeTestAllPrimitives(zint, ints, zlong, longs, zshort, shorts, zchar, chars, zbyte, bytes, zdouble, doubles, zfloat, floats, zbool, bools);
+    return (int[]) nativeTestAllPrimitives(
+        zint,
+        ints,
+        zlong,
+        longs,
+        zshort,
+        shorts,
+        zchar,
+        chars,
+        zbyte,
+        bytes,
+        zdouble,
+        doubles,
+        zfloat,
+        floats,
+        zbool,
+        bools);
   }
   private static native int[] nativeTestAllPrimitives(int zint, int[] ints, long zlong, long[] longs, short zshort, short[] shorts, char zchar, char[] chars, byte zbyte, byte[] bytes, double zdouble, double[] doubles, float zfloat, float[] floats, boolean zbool, boolean[] bools);
 
   @Override
   public void testSpecialTypes(Class clazz, Class[] classes, Throwable throwable, Throwable[] throwables, String string, String[] strings, String convertedString, String[] convertedStrings, String optionalString, SampleForAnnotationProcessor.TestStruct tStruct, SampleForAnnotationProcessor.TestStruct[] structs, Object obj, Object convertedObj, Object[] objects, MyClass.SecondNestedInterface nestedInterface, View view, Context context, Object[] convertedObjects) {
-    nativeTestSpecialTypes(clazz, classes, throwable, throwables, string, strings, convertedString, convertedStrings, optionalString, tStruct, structs, obj, convertedObj, objects, nestedInterface, view, context, convertedObjects);
+    nativeTestSpecialTypes(
+        clazz,
+        classes,
+        throwable,
+        throwables,
+        string,
+        strings,
+        convertedString,
+        convertedStrings,
+        optionalString,
+        tStruct,
+        structs,
+        obj,
+        convertedObj,
+        objects,
+        nestedInterface,
+        view,
+        context,
+        convertedObjects);
   }
   private static native void nativeTestSpecialTypes(Class clazz, Class[] classes, Throwable throwable, Throwable[] throwables, String string, String[] strings, String convertedString, String[] convertedStrings, String optionalString, Object tStruct, Object[] structs, Object obj, Object convertedObj, Object[] objects, Object nestedInterface, Object view, Object context, Object[] convertedObjects);
 
diff --git a/third_party/openscreen/src b/third_party/openscreen/src
index d77735d..f90b21d 160000
--- a/third_party/openscreen/src
+++ b/third_party/openscreen/src
@@ -1 +1 @@
-Subproject commit d77735d9e5ef30477139b0fc1ca3db6706ec8651
+Subproject commit f90b21d85c04e51f0477042d0071db520e152593
diff --git a/third_party/pdfium b/third_party/pdfium
index 49db42f..8007d6e 160000
--- a/third_party/pdfium
+++ b/third_party/pdfium
@@ -1 +1 @@
-Subproject commit 49db42fb84e011f3367f30872266ab3a9c621489
+Subproject commit 8007d6ed5cd165d13c5655f9a230b58cf10195f0
diff --git a/third_party/perfetto b/third_party/perfetto
index 9ac50d2..a81c77e 160000
--- a/third_party/perfetto
+++ b/third_party/perfetto
@@ -1 +1 @@
-Subproject commit 9ac50d25a24563cd24268c1a5c6561c8595b4647
+Subproject commit a81c77e87599fd87d238fb15ab4f4fb90a7a7adc
diff --git a/third_party/rust/PRESUBMIT.py b/third_party/rust/PRESUBMIT.py
index 26e8489..b143eab 100644
--- a/third_party/rust/PRESUBMIT.py
+++ b/third_party/rust/PRESUBMIT.py
@@ -35,12 +35,24 @@
     toml_path = input_api.os_path.join(
         chromium_crates_io, 'supply-chain', 'config.toml')
     hbs_path = input_api.os_path.join(chromium_crates_io, 'vet_config.toml.hbs')
+    gnrt_path = input_api.os_path.join('tools', 'crates', 'gnrt')
 
-    affected_files = input_api.LocalPaths()
+    # We don't use `input_api.AffectedFiles()` nor `input_api.LocalPaths()`,
+    # because (unlike `input_api.change.AffectedFiles()`) they only cover files
+    # underneath `//third_party/rust` (and our checks also care about
+    # `//tools/crates/gnrt` aka `gnrt_path`).
+    affected_files = set([f.LocalPath() for f in input_api.change.AffectedFiles()])
+
     is_toml_edited = toml_path in affected_files
     is_hbs_edited = hbs_path in affected_files
-
     if is_toml_edited != is_hbs_edited:
+        # Allow editing `config.toml` without editing `vet_config.toml.hbs` when
+        # `gnrt` sources change - assume in this case that the `gnrt` changes
+        # affect how `config.toml` is generated.
+        is_gnrt_edited = any([gnrt_path in f for f in affected_files])
+        if is_toml_edited and is_gnrt_edited:
+            return []
+
         return [output_api.PresubmitError(
             f"ERROR: `{hbs_path}` and `{toml_path}` are not modified together",
             long_text=\
diff --git a/third_party/rust/chromium_crates_io/supply-chain/config.toml b/third_party/rust/chromium_crates_io/supply-chain/config.toml
index 014bccd..bbd958f 100644
--- a/third_party/rust/chromium_crates_io/supply-chain/config.toml
+++ b/third_party/rust/chromium_crates_io/supply-chain/config.toml
@@ -42,226 +42,226 @@
 ub-risk-4 = "ub-risk-4"
 
 [policy."aho-corasick:1.1.3"]
-criteria = ["does-not-implement-crypto", "safe-to-run"]
+criteria = ["crypto-safe", "safe-to-run"]
 
 [policy."anstyle:1.0.7"]
-criteria = ["does-not-implement-crypto", "safe-to-run"]
+criteria = ["crypto-safe", "safe-to-run"]
 
 [policy."anyhow:1.0.83"]
-criteria = ["does-not-implement-crypto", "safe-to-run"]
+criteria = ["crypto-safe", "safe-to-run"]
 
 [policy."base64:0.13.1"]
-criteria = ["does-not-implement-crypto", "safe-to-run"]
+criteria = ["crypto-safe", "safe-to-run"]
 
 [policy."bitflags:2.5.0"]
-criteria = ["does-not-implement-crypto", "safe-to-deploy", "ub-risk-2"]
+criteria = ["crypto-safe", "safe-to-deploy", "ub-risk-2"]
 
 [policy."bytemuck:1.16.0"]
-criteria = ["does-not-implement-crypto", "safe-to-deploy", "ub-risk-2"]
+criteria = ["crypto-safe", "safe-to-deploy", "ub-risk-2"]
 
 [policy."bytemuck_derive:1.6.0"]
-criteria = ["does-not-implement-crypto", "safe-to-deploy", "ub-risk-2"]
+criteria = ["crypto-safe", "safe-to-deploy", "ub-risk-2"]
 
 [policy."bytes:1.6.0"]
-criteria = ["does-not-implement-crypto", "safe-to-run"]
+criteria = ["crypto-safe", "safe-to-run"]
 
 [policy."cc:1.0.97"]
 criteria = []
 
 [policy."cfg-if:1.0.0"]
-criteria = ["does-not-implement-crypto", "safe-to-deploy", "ub-risk-2"]
+criteria = ["crypto-safe", "safe-to-deploy", "ub-risk-2"]
 
 [policy."clap:4.5.4"]
-criteria = ["does-not-implement-crypto", "safe-to-run"]
+criteria = ["crypto-safe", "safe-to-run"]
 
 [policy."clap_builder:4.5.2"]
-criteria = ["does-not-implement-crypto", "safe-to-run"]
+criteria = ["crypto-safe", "safe-to-run"]
 
 [policy."clap_lex:0.7.0"]
-criteria = ["does-not-implement-crypto", "safe-to-run"]
+criteria = ["crypto-safe", "safe-to-run"]
 
 [policy."codespan-reporting:0.11.1"]
-criteria = ["does-not-implement-crypto", "safe-to-run"]
+criteria = ["crypto-safe", "safe-to-run"]
 
 [policy."cxx:1.0.122"]
-criteria = ["does-not-implement-crypto", "safe-to-deploy", "ub-risk-2"]
+criteria = ["crypto-safe", "safe-to-deploy", "ub-risk-2"]
 
 [policy."cxxbridge-cmd:1.0.122"]
-criteria = ["does-not-implement-crypto", "safe-to-run"]
+criteria = ["crypto-safe", "safe-to-run"]
 
 [policy."cxxbridge-flags:1.0.122"]
-criteria = ["does-not-implement-crypto", "safe-to-deploy", "ub-risk-2"]
+criteria = ["crypto-safe", "safe-to-deploy", "ub-risk-2"]
 
 [policy."cxxbridge-macro:1.0.122"]
-criteria = ["does-not-implement-crypto", "safe-to-deploy", "ub-risk-2"]
+criteria = ["crypto-safe", "safe-to-deploy", "ub-risk-2"]
 
 [policy."either:1.11.0"]
-criteria = ["does-not-implement-crypto", "safe-to-run"]
+criteria = ["crypto-safe", "safe-to-run"]
 
 [policy."fend-core:1.4.8"]
-criteria = ["does-not-implement-crypto", "safe-to-deploy", "ub-risk-2"]
+criteria = ["crypto-safe", "safe-to-deploy", "ub-risk-2"]
 
 [policy."font-types:0.5.3"]
-criteria = ["does-not-implement-crypto", "safe-to-deploy", "ub-risk-2"]
+criteria = ["crypto-safe", "safe-to-deploy", "ub-risk-2"]
 
 [policy."getrandom:0.2.15"]
-criteria = ["does-not-implement-crypto", "safe-to-run"]
+criteria = ["crypto-safe", "safe-to-run"]
 
 [policy."heck:0.4.1"]
-criteria = ["does-not-implement-crypto", "safe-to-deploy", "ub-risk-2"]
+criteria = ["crypto-safe", "safe-to-deploy", "ub-risk-2"]
 
 [policy."hex-literal:0.4.1"]
-criteria = ["does-not-implement-crypto", "safe-to-run"]
+criteria = ["crypto-safe", "safe-to-run"]
 
 [policy."hex:0.4.3"]
-criteria = ["does-not-implement-crypto", "safe-to-run"]
+criteria = ["crypto-safe", "safe-to-run"]
 
 [policy."itertools:0.11.0"]
-criteria = ["does-not-implement-crypto", "safe-to-run"]
+criteria = ["crypto-safe", "safe-to-run"]
 
 [policy."itoa:1.0.11"]
-criteria = ["does-not-implement-crypto", "safe-to-deploy", "ub-risk-2"]
+criteria = ["crypto-safe", "safe-to-deploy", "ub-risk-2"]
 
 [policy."lazy_static:1.4.0"]
-criteria = ["does-not-implement-crypto", "safe-to-deploy", "ub-risk-2"]
+criteria = ["crypto-safe", "safe-to-deploy", "ub-risk-2"]
 
 [policy."libc:0.2.154"]
-criteria = ["does-not-implement-crypto", "safe-to-deploy", "ub-risk-2"]
+criteria = ["crypto-safe", "safe-to-deploy", "ub-risk-2"]
 
 [policy."link-cplusplus:1.0.9"]
 criteria = []
 
 [policy."log:0.4.21"]
-criteria = ["does-not-implement-crypto", "safe-to-run"]
+criteria = ["crypto-safe", "safe-to-run"]
 
 [policy."memchr:2.7.2"]
-criteria = ["does-not-implement-crypto", "safe-to-deploy", "ub-risk-2"]
+criteria = ["crypto-safe", "safe-to-deploy", "ub-risk-2"]
 
 [policy."minimal-lexical:0.2.1"]
 criteria = []
 
 [policy."nom:7.1.3"]
-criteria = ["does-not-implement-crypto", "safe-to-deploy", "ub-risk-2"]
+criteria = ["crypto-safe", "safe-to-deploy", "ub-risk-2"]
 
 [policy."ppv-lite86:0.2.17"]
-criteria = ["does-not-implement-crypto", "safe-to-run"]
+criteria = ["crypto-safe", "safe-to-run"]
 
 [policy."proc-macro2:1.0.82"]
-criteria = ["does-not-implement-crypto", "safe-to-deploy", "ub-risk-2"]
+criteria = ["crypto-safe", "safe-to-deploy", "ub-risk-2"]
 
 [policy."prost-derive:0.12.5"]
-criteria = ["does-not-implement-crypto", "safe-to-run"]
+criteria = ["crypto-safe", "safe-to-run"]
 
 [policy."prost:0.12.4"]
-criteria = ["does-not-implement-crypto", "safe-to-run"]
+criteria = ["crypto-safe", "safe-to-run"]
 
 [policy."qr_code:2.0.0"]
-criteria = ["does-not-implement-crypto", "safe-to-deploy", "ub-risk-2"]
+criteria = ["crypto-safe", "safe-to-deploy", "ub-risk-2"]
 
 [policy."quote:1.0.36"]
-criteria = ["does-not-implement-crypto", "safe-to-deploy", "ub-risk-2"]
+criteria = ["crypto-safe", "safe-to-deploy", "ub-risk-2"]
 
 [policy."rand:0.8.5"]
-criteria = ["does-not-implement-crypto", "safe-to-run"]
+criteria = ["crypto-safe", "safe-to-run"]
 
 [policy."rand_chacha:0.3.1"]
-criteria = ["does-not-implement-crypto", "safe-to-run"]
+criteria = ["crypto-safe", "safe-to-run"]
 
 [policy."rand_core:0.6.4"]
-criteria = ["does-not-implement-crypto", "safe-to-run"]
+criteria = ["crypto-safe", "safe-to-run"]
 
 [policy."rand_pcg:0.3.1"]
-criteria = ["does-not-implement-crypto", "safe-to-run"]
+criteria = ["crypto-safe", "safe-to-run"]
 
 [policy."read-fonts:0.19.1"]
-criteria = ["does-not-implement-crypto", "safe-to-deploy", "ub-risk-2"]
+criteria = ["crypto-safe", "safe-to-deploy", "ub-risk-2"]
 
 [policy."regex-automata:0.4.6"]
-criteria = ["does-not-implement-crypto", "safe-to-run"]
+criteria = ["crypto-safe", "safe-to-run"]
 
 [policy."regex-syntax:0.8.3"]
-criteria = ["does-not-implement-crypto", "safe-to-run"]
+criteria = ["crypto-safe", "safe-to-run"]
 
 [policy."regex:1.10.4"]
-criteria = ["does-not-implement-crypto", "safe-to-run"]
+criteria = ["crypto-safe", "safe-to-run"]
 
 [policy."rstest:0.17.0"]
-criteria = ["does-not-implement-crypto", "safe-to-run"]
+criteria = ["crypto-safe", "safe-to-run"]
 
 [policy."rstest_macros:0.17.0"]
-criteria = ["does-not-implement-crypto", "safe-to-run"]
+criteria = ["crypto-safe", "safe-to-run"]
 
 [policy."rstest_reuse:0.5.0"]
-criteria = ["does-not-implement-crypto", "safe-to-run"]
+criteria = ["crypto-safe", "safe-to-run"]
 
 [policy."rustc-demangle-capi:0.1.0"]
-criteria = ["does-not-implement-crypto", "safe-to-run"]
+criteria = ["crypto-safe", "safe-to-run"]
 
 [policy."rustc-demangle:0.1.24"]
-criteria = ["does-not-implement-crypto", "safe-to-run"]
+criteria = ["crypto-safe", "safe-to-run"]
 
 [policy."rustc_version:0.4.0"]
-criteria = ["does-not-implement-crypto", "safe-to-run"]
+criteria = ["crypto-safe", "safe-to-run"]
 
 [policy."rustversion:1.0.16"]
-criteria = ["does-not-implement-crypto", "safe-to-deploy", "ub-risk-2"]
+criteria = ["crypto-safe", "safe-to-deploy", "ub-risk-2"]
 
 [policy."ryu:1.0.18"]
-criteria = ["does-not-implement-crypto", "safe-to-deploy", "ub-risk-2"]
+criteria = ["crypto-safe", "safe-to-deploy", "ub-risk-2"]
 
 [policy."semver:1.0.23"]
-criteria = ["does-not-implement-crypto", "safe-to-run"]
+criteria = ["crypto-safe", "safe-to-run"]
 
 [policy."serde:1.0.201"]
-criteria = ["does-not-implement-crypto", "safe-to-deploy", "ub-risk-2"]
+criteria = ["crypto-safe", "safe-to-deploy", "ub-risk-2"]
 
 [policy."serde_derive:1.0.201"]
-criteria = ["does-not-implement-crypto", "safe-to-deploy", "ub-risk-2"]
+criteria = ["crypto-safe", "safe-to-deploy", "ub-risk-2"]
 
 [policy."serde_json:1.0.117"]
-criteria = ["does-not-implement-crypto", "safe-to-run"]
+criteria = ["crypto-safe", "safe-to-run"]
 
 [policy."serde_json_lenient:0.2.1"]
-criteria = ["does-not-implement-crypto", "safe-to-deploy", "ub-risk-2"]
+criteria = ["crypto-safe", "safe-to-deploy", "ub-risk-2"]
 
 [policy."skrifa:0.19.0"]
-criteria = ["does-not-implement-crypto", "safe-to-deploy", "ub-risk-2"]
+criteria = ["crypto-safe", "safe-to-deploy", "ub-risk-2"]
 
 [policy."small_ctor:0.1.1"]
-criteria = ["does-not-implement-crypto", "safe-to-run"]
+criteria = ["crypto-safe", "safe-to-run"]
 
 [policy."static_assertions:1.1.0"]
-criteria = ["does-not-implement-crypto", "safe-to-deploy", "ub-risk-2"]
+criteria = ["crypto-safe", "safe-to-deploy", "ub-risk-2"]
 
 [policy."strsim:0.11.1"]
-criteria = ["does-not-implement-crypto", "safe-to-run"]
+criteria = ["crypto-safe", "safe-to-run"]
 
 [policy."strum:0.25.0"]
-criteria = ["does-not-implement-crypto", "safe-to-deploy", "ub-risk-2"]
+criteria = ["crypto-safe", "safe-to-deploy", "ub-risk-2"]
 
 [policy."strum_macros:0.25.3"]
-criteria = ["does-not-implement-crypto", "safe-to-deploy", "ub-risk-2"]
+criteria = ["crypto-safe", "safe-to-deploy", "ub-risk-2"]
 
 [policy."syn:1.0.109"]
-criteria = ["does-not-implement-crypto", "safe-to-run"]
+criteria = ["crypto-safe", "safe-to-run"]
 
 [policy."syn:2.0.63"]
-criteria = ["does-not-implement-crypto", "safe-to-deploy", "ub-risk-2"]
+criteria = ["crypto-safe", "safe-to-deploy", "ub-risk-2"]
 
 [policy."termcolor:1.4.1"]
-criteria = ["does-not-implement-crypto", "safe-to-run"]
+criteria = ["crypto-safe", "safe-to-run"]
 
 [policy."tinyvec:1.6.0"]
-criteria = ["does-not-implement-crypto", "safe-to-deploy", "ub-risk-2"]
+criteria = ["crypto-safe", "safe-to-deploy", "ub-risk-2"]
 
 [policy."unicode-ident:1.0.12"]
-criteria = ["does-not-implement-crypto", "safe-to-deploy", "ub-risk-2"]
+criteria = ["crypto-safe", "safe-to-deploy", "ub-risk-2"]
 
 [policy."unicode-linebreak:0.1.5"]
-criteria = ["does-not-implement-crypto", "safe-to-deploy", "ub-risk-2"]
+criteria = ["crypto-safe", "safe-to-deploy", "ub-risk-2"]
 
 [policy."unicode-width:0.1.12"]
-criteria = ["does-not-implement-crypto", "safe-to-run"]
+criteria = ["crypto-safe", "safe-to-run"]
 
 [policy."wasi:0.11.0+wasi-snapshot-preview1"]
 criteria = []
@@ -270,16 +270,16 @@
 criteria = []
 
 [policy."winapi-util:0.1.6"]
-criteria = ["does-not-implement-crypto", "safe-to-run"]
+criteria = ["crypto-safe", "safe-to-run"]
 
 [policy."winapi-x86_64-pc-windows-gnu:0.4.0"]
 criteria = []
 
 [policy."winapi:0.3.9"]
-criteria = ["does-not-implement-crypto", "safe-to-run"]
+criteria = ["crypto-safe", "safe-to-run"]
 
 [policy."wycheproof:0.4.0"]
-criteria = ["does-not-implement-crypto", "safe-to-run"]
+criteria = ["crypto-safe", "safe-to-run"]
 
 [[exemptions.cxx]]
 version = "1.0.122"
diff --git a/third_party/skia b/third_party/skia
index e0881f0..fb644bf 160000
--- a/third_party/skia
+++ b/third_party/skia
@@ -1 +1 @@
-Subproject commit e0881f06f94a1bc31e0a5ecd4618090002f61c28
+Subproject commit fb644bfe964ddd51db6d6caf493ffc6a125b4db0
diff --git a/third_party/webgpu-cts/src b/third_party/webgpu-cts/src
index 007e101..125990e 160000
--- a/third_party/webgpu-cts/src
+++ b/third_party/webgpu-cts/src
@@ -1 +1 @@
-Subproject commit 007e1015875a0a91794edbc14feb3ad5fe60f90b
+Subproject commit 125990efef045d9bbea45beb70291f1e0afda5fd
diff --git a/third_party/webrtc b/third_party/webrtc
index af0c18c..316fe0b 160000
--- a/third_party/webrtc
+++ b/third_party/webrtc
@@ -1 +1 @@
-Subproject commit af0c18c594e71d067cda6e439df176135bea112f
+Subproject commit 316fe0b133419c15fb08846fc54f2d38651b98cc
diff --git a/tools/crates/gnrt/lib/vet.rs b/tools/crates/gnrt/lib/vet.rs
index 0c6a6d1..e7d8511 100644
--- a/tools/crates/gnrt/lib/vet.rs
+++ b/tools/crates/gnrt/lib/vet.rs
@@ -24,7 +24,7 @@
         // We currently consider ub-risk-2 as satisfying the Rule of Two, though there seems to be
         // some spot in between risk 1 and 2 that fits better and this could be improved.
         (Some(true), Group::Safe) | (None, Group::Safe) => {
-            vec![DoesNotImplementCrypto, SafeToDeploy, UbRisk2]
+            vec![CryptoSafe, SafeToDeploy, UbRisk2]
         }
         // Sandbox crates are used in a sandbox, so we have a weaker tolerance. There may be a bunch
         // of ASM code in there for example. Adversarial inputs may have a way to break things,
@@ -33,16 +33,16 @@
         // This type of crate is not well described in the UB risk guidelines for now, so we use
         // "ub-risk-3" for this category.
         (Some(true), Group::Sandbox) | (None, Group::Sandbox) => {
-            vec![DoesNotImplementCrypto, SafeToDeploy, UbRisk3]
+            vec![CryptoSafe, SafeToDeploy, UbRisk3]
         }
         // Code in tests is not run on user machines and does not interact with adversarial inputs.
         // Thus it does not need to be safe-to-deploy, but it needs to not be malicious against
         // developers and CI bots which is covered by "safe-to-run".
-        (_, Group::Test) => vec![DoesNotImplementCrypto, SafeToRun],
+        (_, Group::Test) => vec![CryptoSafe, SafeToRun],
         // Crates that contribute to the shipped binary but are not themselves shipped (code
         // generators for example) do not get deployed themselves and do not interact with
         // adversarial inputs. Thus they need to be "safe-to-run" by developers and CI only.
-        (Some(false), _) => vec![DoesNotImplementCrypto, SafeToRun],
+        (Some(false), _) => vec![CryptoSafe, SafeToRun],
     }
 }
 
@@ -66,7 +66,7 @@
 #[derive(serde::Serialize)]
 #[serde(rename_all = "kebab-case")]
 pub enum AuditCriteria {
-    DoesNotImplementCrypto,
+    CryptoSafe,
     SafeToDeploy,
     SafeToRun,
     #[serde(rename = "ub-risk-2")]
diff --git a/tools/disable_tests/PRESUBMIT.py b/tools/disable_tests/PRESUBMIT.py
index 8ce8b6ed..f32ab92 100644
--- a/tools/disable_tests/PRESUBMIT.py
+++ b/tools/disable_tests/PRESUBMIT.py
@@ -13,6 +13,13 @@
   results.extend(
       input_api.canned_checks.RunPylint(input_api, output_api, version='2.6'))
 
+  files_to_skip = ['integration_test.py']
+  if input_api.change.scm != 'git':
+    # The clang-format hook is being migrated for cog and until that is
+    # complete, tests that rely on clang-format are unsupported on cog.
+    # TODO(b/333744051): Remove this when clang-format is fully migrated.
+    files_to_skip.append('.*gtest_test\.py')
+
   commands = []
   commands.extend(
       input_api.canned_checks.GetUnitTestsRecursively(
@@ -20,7 +27,7 @@
           output_api,
           input_api.os_path.join(input_api.PresubmitLocalPath()),
           files_to_check=[r'.+_test\.py$'],
-          files_to_skip=['integration_test.py']))
+          files_to_skip=files_to_skip))
 
   # integration_test.py uses subcommands, so we can't use the standard unit test
   # presubmit API to run it.
diff --git a/tools/json_schema_compiler/PRESUBMIT.py b/tools/json_schema_compiler/PRESUBMIT.py
index ad70639..cfa88ce 100644
--- a/tools/json_schema_compiler/PRESUBMIT.py
+++ b/tools/json_schema_compiler/PRESUBMIT.py
@@ -25,15 +25,34 @@
   return Generate(input_api, output_api, dryrun=True)
 
 
+def _GetFilesToSkip(input_api):
+  """Returns the list of test files to skip.
+
+  The clang-format hook is being migrated for cog and until that is complete,
+  tests that rely on clang-format are unsupported on cog.
+
+  TODO(b/333744051): Remove this method when clang-format is fully migrated.
+  """
+  files_to_skip = []
+  if input_api.change.scm != 'git':
+      files_to_skip.append('.*ts_definition_generator_test\.py')
+  return files_to_skip
+
+
 def CheckChangeOnUpload(input_api, output_api):
   ret = input_api.canned_checks.RunUnitTestsInDirectory(
-      input_api, output_api, '.', files_to_check=FILE_PATTERN)
+      input_api,
+      output_api,
+      '.',
+      files_to_check=FILE_PATTERN,
+      files_to_skip=_GetFilesToSkip(input_api))
   ret += _CheckExterns(input_api, output_api)
   return ret
 
 
 def CheckChangeOnCommit(input_api, output_api):
   ret = input_api.canned_checks.RunUnitTestsInDirectory(
-      input_api, output_api, '.', files_to_check=FILE_PATTERN)
+      input_api, output_api, '.', files_to_check=FILE_PATTERN,
+      files_to_skip=_GetFilesToSkip(input_api))
   ret += _CheckExterns(input_api, output_api)
   return ret
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml
index c1f39c37..473f7d2f 100644
--- a/tools/metrics/actions/actions.xml
+++ b/tools/metrics/actions/actions.xml
@@ -14359,6 +14359,14 @@
   <description>Recorded when the Safety Check result has changed.</description>
 </action>
 
+<action name="IOSMagicStackSettingsOpened">
+  <owner>thegreenfrog@google.com</owner>
+  <owner>bling-team@google.com</owner>
+  <description>
+    Recorded when the user opens the Magic Stack Settings page.
+  </description>
+</action>
+
 <action name="IOSMagicStackShortcutsFreshSignal" not_user_triggered="true">
   <owner>thegreenfrog@google.com</owner>
   <owner>bling-team@google.com</owner>
@@ -43120,6 +43128,11 @@
   <affected-action
       name="InProductHelp.ShouldTriggerHelpUIResult.WouldHaveTriggered.IPH"/>
   <affected-action name="UserEducation.MessageAction.Abort.IPH"/>
+  <affected-action
+      name="UserEducation.MessageAction.AbortedByAnchorHidden.IPH"/>
+  <affected-action
+      name="UserEducation.MessageAction.AbortedByBubbleDestroyed.IPH"/>
+  <affected-action name="UserEducation.MessageAction.AbortedByFeature.IPH"/>
   <affected-action name="UserEducation.MessageAction.Action.IPH"/>
   <affected-action name="UserEducation.MessageAction.Cancel.IPH"/>
   <affected-action name="UserEducation.MessageAction.Dismiss.IPH"/>
diff --git a/tools/metrics/histograms/metadata/android/histograms.xml b/tools/metrics/histograms/metadata/android/histograms.xml
index fc92039..17a25a3 100644
--- a/tools/metrics/histograms/metadata/android/histograms.xml
+++ b/tools/metrics/histograms/metadata/android/histograms.xml
@@ -6433,6 +6433,17 @@
   </summary>
 </histogram>
 
+<histogram name="Android.WebView.ScheduleOnVizAndBlockTime"
+    units="microseconds" expires_after="2024-11-20">
+  <owner>cduvall@chromium.org</owner>
+  <owner>src/android_webview/OWNERS</owner>
+  <summary>
+    Records the total time spent running a blocking task in
+    TaskQueueWebView::ScheduleOnVizAndBlock(). This is recorded only on devices
+    that support high resolution timers.
+  </summary>
+</histogram>
+
 <histogram name="Android.WebView.SecureCookieAction" enum="SecureCookieAction"
     expires_after="2024-11-03">
   <owner>ntfschr@chromium.org</owner>
@@ -6865,6 +6876,17 @@
   </token>
 </histogram>
 
+<histogram name="Android.WebView.VizCompositorRunnerPostTaskBlockTime"
+    units="microseconds" expires_after="2024-11-20">
+  <owner>cduvall@chromium.org</owner>
+  <owner>src/android_webview/OWNERS</owner>
+  <summary>
+    Records the total time spent running a blocking task in
+    VizCompositorThreadRunnerWebView::PostTaskAndBlock(). This is recorded only
+    on devices that support high resolution timers.
+  </summary>
+</histogram>
+
 <histogram name="AndroidSearchEngineLogo.Events"
     enum="AndroidSearchEngineLogoEvents" expires_after="2024-09-22">
   <owner>wylieb@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/ash/histograms.xml b/tools/metrics/histograms/metadata/ash/histograms.xml
index 967909e..680909a 100644
--- a/tools/metrics/histograms/metadata/ash/histograms.xml
+++ b/tools/metrics/histograms/metadata/ash/histograms.xml
@@ -3737,6 +3737,26 @@
   </summary>
 </histogram>
 
+<histogram name="Ash.FocusMode.PercentOfSessionCompleted.{SessionDurationType}"
+    units="%" expires_after="2025-05-01">
+  <owner>nupurjain@google.com</owner>
+  <owner>richui@chromium.org</owner>
+  <owner>skau@chromium.org</owner>
+  <owner>hongyulong@chromium.org</owner>
+  <owner>chromeos-wms@google.com</owner>
+  <summary>
+    Records the percent of the session which was completed (bucketed by the
+    final session duration) when a session is over. This is for the
+    {SessionDurationType} type of `Short` (&lt; 11 mins), `Medium` (11 - 29
+    mins), and `Long` (&gt; 29 mins).
+  </summary>
+  <token key="SessionDurationType">
+    <variant name="Long"/>
+    <variant name="Medium"/>
+    <variant name="Short"/>
+  </token>
+</histogram>
+
 <histogram name="Ash.FocusMode.StartSession.HasSelectedTask" enum="Boolean"
     expires_after="2025-02-01">
   <owner>hongyulong@chromium.org</owner>
@@ -3772,6 +3792,19 @@
   </summary>
 </histogram>
 
+<histogram name="Ash.FocusMode.TasksCompleted" units="count"
+    expires_after="2025-05-01">
+  <owner>nupurjain@google.com</owner>
+  <owner>richui@chromium.org</owner>
+  <owner>skau@chromium.org</owner>
+  <owner>hongyulong@chromium.org</owner>
+  <owner>chromeos-wms@google.com</owner>
+  <summary>
+    When a session ends, record how many tasks were marked as completed by the
+    user during the session.
+  </summary>
+</histogram>
+
 <histogram name="Ash.FocusMode.TasksSelected" units="count"
     expires_after="2025-05-01">
   <owner>hongyulong@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/compositing/histograms.xml b/tools/metrics/histograms/metadata/compositing/histograms.xml
index 028c5ad..ac62e6c 100644
--- a/tools/metrics/histograms/metadata/compositing/histograms.xml
+++ b/tools/metrics/histograms/metadata/compositing/histograms.xml
@@ -256,17 +256,6 @@
   </summary>
 </histogram>
 
-<histogram name="Compositing.DirectRenderer.SkippedNonRootRenderPassOutputSize"
-    units="pixels" expires_after="2024-06-30">
-  <owner>magchen@chromium.org</owner>
-  <owner>ccameron@chromium.org</owner>
-  <owner>graphics-dev@chromium.org</owner>
-  <summary>
-    Pixel size for a skipped nonroot render pass output surface. Recorded in Viz
-    DirectRenderer draw frame.
-  </summary>
-</histogram>
-
 <histogram name="Compositing.Display.AdpfHintUs" units="microseconds"
     expires_after="2024-09-01">
   <owner>boliu@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/extensions/histograms.xml b/tools/metrics/histograms/metadata/extensions/histograms.xml
index 7cd817c..84847fc 100644
--- a/tools/metrics/histograms/metadata/extensions/histograms.xml
+++ b/tools/metrics/histograms/metadata/extensions/histograms.xml
@@ -1140,9 +1140,10 @@
 </histogram>
 
 <histogram name="Extensions.DeclarativeNetRequest.UpdateDynamicRulesStatus"
-    enum="UpdateDynamicRulesStatus" expires_after="2024-06-26">
+    enum="UpdateDynamicRulesStatus" expires_after="2025-06-26">
   <owner>rdevlin.cronin@chromium.org</owner>
-  <owner>src/extensions/OWNERS</owner>
+  <owner>kelvinjiang@chromium.org</owner>
+  <owner>extensions-core@chromium.org</owner>
   <summary>
     Indicates the result of an extension function call to add or remove dynamic
     rules. Logged every time an extension calls the addDynamicRules or
diff --git a/tools/metrics/histograms/metadata/ios/histograms.xml b/tools/metrics/histograms/metadata/ios/histograms.xml
index 8e08fa5..1b1c13c 100644
--- a/tools/metrics/histograms/metadata/ios/histograms.xml
+++ b/tools/metrics/histograms/metadata/ios/histograms.xml
@@ -1585,6 +1585,13 @@
   </summary>
 </histogram>
 
+<histogram name="IOS.MagicStack.Module.Disabled" enum="IOSMagicStackModuleType"
+    expires_after="2025-05-03">
+  <owner>thegreenfrog@chromium.org</owner>
+  <owner>bling-get-set-up@google.com</owner>
+  <summary>Records when a module was disabled.</summary>
+</histogram>
+
 <histogram name="IOS.MagicStack.Module.TopImpression"
     enum="IOSMagicStackModuleType" expires_after="2024-11-03">
   <owner>thegreenfrog@chromium.org</owner>
@@ -1603,6 +1610,16 @@
   </summary>
 </histogram>
 
+<histogram name="IOS.MagicStack.ScrollActionToIndex" units="index"
+    expires_after="2025-05-03">
+  <owner>thegreenfrog@chromium.org</owner>
+  <owner>bling-get-set-up@google.com</owner>
+  <summary>
+    Records the Magic Stack card index to which the user scrolled to each time a
+    scroll to a new module happens.
+  </summary>
+</histogram>
+
 <histogram name="IOS.MagicStack.Start.SegmentationRankingFetchTime" units="ms"
     expires_after="2024-11-03">
   <owner>thegreenfrog@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/media/enums.xml b/tools/metrics/histograms/metadata/media/enums.xml
index d0ad2db..d48a094a 100644
--- a/tools/metrics/histograms/metadata/media/enums.xml
+++ b/tools/metrics/histograms/metadata/media/enums.xml
@@ -1010,6 +1010,12 @@
   <int value="3" label="Failed using photo stream"/>
 </enum>
 
+<enum name="LacrosBundledWidevine">
+  <int value="0" label="None found"/>
+  <int value="1" label="Lacros bundled"/>
+  <int value="2" label="Ash bundled"/>
+</enum>
+
 <enum name="MakeAudioInputStreamStatus">
   <int value="0" label="Success"/>
   <int value="1" label="Error: Had switch fail-audio-stream-creation"/>
diff --git a/tools/metrics/histograms/metadata/media/histograms.xml b/tools/metrics/histograms/metadata/media/histograms.xml
index f2890d87..69a2e63 100644
--- a/tools/metrics/histograms/metadata/media/histograms.xml
+++ b/tools/metrics/histograms/metadata/media/histograms.xml
@@ -3350,6 +3350,16 @@
   <token key="VideoCodec" variants="VideoCodec"/>
 </histogram>
 
+<histogram name="Media.EME.Widevine.LacrosBundledCdm"
+    enum="LacrosBundledWidevine" expires_after="2025-02-27">
+  <owner>jrummell@chromium.org</owner>
+  <owner>media-dev-uma@chromium.org</owner>
+  <summary>
+    Reports which Widevine CDM was used by Lacros. Only reported by the browser
+    process when registering available CDMs during startup.
+  </summary>
+</histogram>
+
 <histogram name="Media.EME.Widevine.SoftwareSecure.SystemCode"
     enum="CdmSystemCode" expires_after="2025-02-27">
   <owner>xhwang@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/nearby/histograms.xml b/tools/metrics/histograms/metadata/nearby/histograms.xml
index 7685e977..f2710d9 100644
--- a/tools/metrics/histograms/metadata/nearby/histograms.xml
+++ b/tools/metrics/histograms/metadata/nearby/histograms.xml
@@ -315,6 +315,17 @@
   </summary>
 </histogram>
 
+<histogram name="Nearby.Connections.V3.Connection.Result"
+    enum="NearbyConnectionsStatus" expires_after="2025-05-01">
+  <owner>crisrael@google.com</owner>
+  <owner>chromeos-cross-device-eng@google.com</owner>
+  <summary>
+    Records the result upon completing an attempt to connect to a remote device
+    with the V3 connection flow. Emitted when receiving the result from the
+    Nearby utility process.
+  </summary>
+</histogram>
+
 <histogram name="Nearby.Connections.V3.Medium.ChangedToMedium"
     enum="NearbyConnectionsMedium" expires_after="2025-05-01">
   <owner>crisrael@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/sharing/histograms.xml b/tools/metrics/histograms/metadata/sharing/histograms.xml
index 587331e..29bbf29e 100644
--- a/tools/metrics/histograms/metadata/sharing/histograms.xml
+++ b/tools/metrics/histograms/metadata/sharing/histograms.xml
@@ -278,6 +278,17 @@
   </summary>
 </histogram>
 
+<histogram name="Sharing.LocalSharingTargetInfoSupportsSync" enum="Boolean"
+    expires_after="2024-09-27">
+  <owner>rushans@google.com</owner>
+  <owner>src/chrome/browser/share/OWNERS</owner>
+  <summary>
+    Records whether the local device contains sharing information to use sync
+    (as opposed to FCM) from other devices. Recorded during device registration
+    on every browser startup or after sync reset.
+  </summary>
+</histogram>
+
 <histogram name="Sharing.MessageReceivedType" enum="SharingMessageType"
     expires_after="2024-11-03">
   <owner>mvanouwerkerk@chromium.org</owner>
@@ -429,6 +440,16 @@
   </summary>
 </histogram>
 
+<histogram name="Sharing.SendMessageWithSyncAckFcmConfiguration" enum="Boolean"
+    expires_after="2024-09-27">
+  <owner>rushans@google.com</owner>
+  <owner>src/chrome/browser/share/OWNERS</owner>
+  <summary>
+    Records whether a SharingMessage contains data required to send Ack message
+    using sync. Recorded for non-ack messages before sending them.
+  </summary>
+</histogram>
+
 <histogram name="Sharing.SendTabToSelf.NotificationStatus"
     enum="SendTabToSelfNotificationStatus" expires_after="2024-09-22">
   <owner>jeffreycohen@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/signin/enums.xml b/tools/metrics/histograms/metadata/signin/enums.xml
index a77ad9a0..8c05b2a4 100644
--- a/tools/metrics/histograms/metadata/signin/enums.xml
+++ b/tools/metrics/histograms/metadata/signin/enums.xml
@@ -833,7 +833,7 @@
   <int value="21" label="AccountReconcilor::RevokeTokensNotInCookies"/>
   <int value="22"
       label="DiceResponseHandler::Signin from sign in promo after password
-             save"/>
+             save (obsolete)"/>
 </enum>
 
 <enum name="SyncButtonClicked">
diff --git a/tools/metrics/histograms/metadata/sync/enums.xml b/tools/metrics/histograms/metadata/sync/enums.xml
index e688e1f..e56b7ad 100644
--- a/tools/metrics/histograms/metadata/sync/enums.xml
+++ b/tools/metrics/histograms/metadata/sync/enums.xml
@@ -606,6 +606,7 @@
   <int value="100279" label="KeyboardDefaultSplitModifierSettings"/>
   <int value="100280" label="DisplayAmbientLightSensorLastEnabled"/>
   <int value="100281" label="AccessibilityMainNodeAnnotationsEnabled"/>
+  <int value="100282" label="SyncableVersionedWallpaperInfo"/>
 <!-- LINT.ThenChange(/chrome/browser/sync/prefs/chrome_syncable_prefs_database.cc:ChromeSyncablePref)-->
 
 <!-- LINT.IfChange(IosSyncablePref) -->
diff --git a/tools/metrics/histograms/metadata/user_education/histograms.xml b/tools/metrics/histograms/metadata/user_education/histograms.xml
index 8baa31d..c6850105 100644
--- a/tools/metrics/histograms/metadata/user_education/histograms.xml
+++ b/tools/metrics/histograms/metadata/user_education/histograms.xml
@@ -251,6 +251,18 @@
   </summary>
 </histogram>
 
+<histogram name="UserEducation.Session.MonthlyActiveDays" units="count"
+    expires_after="2024-09-21">
+  <owner>dfried@chromium.org</owner>
+  <owner>frizzle-team@google.com</owner>
+  <summary>
+    When a new User Education session starts, records the number of days with at
+    least one session in the past month. This helps us understand how frequently
+    our product is being used in alignment with standard Google user
+    segmentation metrics.
+  </summary>
+</histogram>
+
 <histogram name="UserEducation.Session.RecentActiveDays" units="count"
     expires_after="2024-09-21">
   <owner>dfried@chromium.org</owner>
@@ -281,7 +293,7 @@
   <owner>frizzle-team@google.com</owner>
   <summary>
     When a new User Education session starts, records the number of weeks with
-    at least four sessions in the past month. This helps us understand how
+    at least four active days in the past month. This helps us understand how
     frequently our product is being used in alignment with standard Google user
     segmentation metrics.
   </summary>
diff --git a/tools/metrics/histograms/metadata/web_core/histograms.xml b/tools/metrics/histograms/metadata/web_core/histograms.xml
index ad7d16b..469d5504 100644
--- a/tools/metrics/histograms/metadata/web_core/histograms.xml
+++ b/tools/metrics/histograms/metadata/web_core/histograms.xml
@@ -249,7 +249,7 @@
 </histogram>
 
 <histogram name="WebCore.IndexedDB.BackingStore.OverlyLargeOriginLength"
-    units="characters" expires_after="2024-05-24">
+    units="characters" expires_after="2024-09-29">
   <owner>estade@chromium.org</owner>
   <owner>chrome-owp-storage@google.com</owner>
   <summary>
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index 1086dc1..ac54146 100644
--- a/tools/perf/core/perfetto_binary_roller/binary_deps.json
+++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -5,8 +5,8 @@
             "full_remote_path": "perfetto-luci-artifacts/v45.0/linux-arm64/trace_processor_shell"
         },
         "win": {
-            "hash": "6a529d354f6e412405237384aac26356844ecea5",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/3ce5202ceef95d02a826c13c516a8485ef26237a/trace_processor_shell.exe"
+            "hash": "02554a91eff3dff305bc9aedc5b156fdb631963e",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/9ac50d25a24563cd24268c1a5c6561c8595b4647/trace_processor_shell.exe"
         },
         "linux_arm": {
             "hash": "f7cc2e856e9ee1260e9691c078f3771193eb4dea",
@@ -21,8 +21,8 @@
             "full_remote_path": "perfetto-luci-artifacts/v45.0/mac-arm64/trace_processor_shell"
         },
         "linux": {
-            "hash": "e7f6c638a4861a82681e0840d1593e7d0179ffb7",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/3ce5202ceef95d02a826c13c516a8485ef26237a/trace_processor_shell"
+            "hash": "1ed3b1b28dd748dc22d50950cd25cd4f94dc5918",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/c4883dde9a01ca50d8430dfff7f2427738c14497/trace_processor_shell"
         }
     },
     "power_profile.sql": {
diff --git a/ui/accelerated_widget_mac/ca_renderer_layer_tree.h b/ui/accelerated_widget_mac/ca_renderer_layer_tree.h
index 275d50b..6218649 100644
--- a/ui/accelerated_widget_mac/ca_renderer_layer_tree.h
+++ b/ui/accelerated_widget_mac/ca_renderer_layer_tree.h
@@ -35,7 +35,6 @@
 
 ACCELERATED_WIDGET_MAC_EXPORT BASE_DECLARE_FEATURE(
     kFullscreenLowPowerBackdropMac);
-ACCELERATED_WIDGET_MAC_EXPORT BASE_DECLARE_FEATURE(kCALayerTreeOptimization);
 
 struct CARendererLayerParams;
 
@@ -101,7 +100,6 @@
   using CALayerMap =
       std::unordered_map<IOSurfaceRef, base::WeakPtr<ContentLayer>>;
 
-  void MatchLayersToOldTreeDefault(CARendererLayerTree* old_tree);
   void MatchLayersToOldTree(CARendererLayerTree* old_tree);
 
   class RootLayer {
@@ -321,10 +319,6 @@
   const bool allow_solid_color_layers_ = true;
   id<MTLDevice> __strong metal_device_ = nil;
 
-  // Enable CALayerTree optimization that will try to reuse the CALayer with a
-  // matched CALayer from the old CALayerTree in the previous frame.
-  const bool ca_layer_tree_optimization_;
-
   // Map of content IOSurface.
   CALayerMap ca_layer_map_;
 };
diff --git a/ui/accelerated_widget_mac/ca_renderer_layer_tree.mm b/ui/accelerated_widget_mac/ca_renderer_layer_tree.mm
index 75b82e3..6511c75 100644
--- a/ui/accelerated_widget_mac/ca_renderer_layer_tree.mm
+++ b/ui/accelerated_widget_mac/ca_renderer_layer_tree.mm
@@ -38,10 +38,6 @@
              "FullscreenLowPowerBackdropMac",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
-BASE_FEATURE(kCALayerTreeOptimization,
-             "CALayerTreeOptimization",
-             base::FEATURE_ENABLED_BY_DEFAULT);
-
 #if BUILDFLAG(IS_MAC)
 // Show borders around RenderPassDrawQuad CALayers. which is the output of a
 // non-root render pass.
@@ -315,9 +311,7 @@
     bool allow_solid_color_layers)
     : allow_av_sample_buffer_display_layer_(
           allow_av_sample_buffer_display_layer),
-      allow_solid_color_layers_(allow_solid_color_layers),
-      ca_layer_tree_optimization_(
-          base::FeatureList::IsEnabled(kCALayerTreeOptimization)) {}
+      allow_solid_color_layers_(allow_solid_color_layers) {}
 CARendererLayerTree::~CARendererLayerTree() = default;
 
 bool CARendererLayerTree::ScheduleCALayer(const CARendererLayerParams& params) {
@@ -336,10 +330,8 @@
   TRACE_EVENT0("gpu", "CARendererLayerTree::CommitScheduledCALayers");
   scale_factor_ = scale_factor;
 
-  if (ca_layer_tree_optimization_)
-    MatchLayersToOldTree(old_tree.get());
-  else
-    MatchLayersToOldTreeDefault(old_tree.get());
+  // The CALayerTree optimization reuses the matched CALayer from the previous.
+  MatchLayersToOldTree(old_tree.get());
 
   root_layer_.CommitToCA(superlayer, pixel_size);
   // If there are any extra CALayers in |old_tree| that were not stolen by this
@@ -348,22 +340,6 @@
   has_committed_ = true;
 }
 
-void CARendererLayerTree::MatchLayersToOldTreeDefault(
-    CARendererLayerTree* old_tree) {
-  if (!old_tree)
-    return;
-  DCHECK(old_tree->has_committed_);
-
-  // Match the root layer.
-  if (old_tree->scale_factor_ != scale_factor_)
-    return;
-
-  root_layer_.old_layer_ =
-      old_tree->root_layer_.weak_factory_for_new_layer_.GetWeakPtr();
-
-  root_layer_.CALayerFallBack();
-}
-
 void CARendererLayerTree::MatchLayersToOldTree(CARendererLayerTree* old_tree) {
   if (!old_tree)
     return;
diff --git a/ui/android/BUILD.gn b/ui/android/BUILD.gn
index a8a9e214..4fb5f3d 100644
--- a/ui/android/BUILD.gn
+++ b/ui/android/BUILD.gn
@@ -511,17 +511,14 @@
     "javatests/src/org/chromium/ui/test/util/BlankUiTestActivity.java",
     "javatests/src/org/chromium/ui/test/util/BlankUiTestActivityTestCase.java",
     "javatests/src/org/chromium/ui/test/util/DeviceRestriction.java",
-    "javatests/src/org/chromium/ui/test/util/DeviceRestrictionSkipCheck.java",
     "javatests/src/org/chromium/ui/test/util/DisableAnimationsTestRule.java",
     "javatests/src/org/chromium/ui/test/util/GmsCoreVersionRestriction.java",
-    "javatests/src/org/chromium/ui/test/util/GmsCoreVersionRestrictionSkipCheck.java",
     "javatests/src/org/chromium/ui/test/util/MockitoHelper.java",
     "javatests/src/org/chromium/ui/test/util/NightModeTestUtils.java",
     "javatests/src/org/chromium/ui/test/util/RenderTestRule.java",
     "javatests/src/org/chromium/ui/test/util/UiDisableIf.java",
     "javatests/src/org/chromium/ui/test/util/UiDisableIfSkipCheck.java",
     "javatests/src/org/chromium/ui/test/util/UiRestriction.java",
-    "javatests/src/org/chromium/ui/test/util/UiRestrictionSkipCheck.java",
     "javatests/src/org/chromium/ui/test/util/ViewUtils.java",
     "javatests/src/org/chromium/ui/test/util/modaldialog/FakeModalDialogManager.java",
     "javatests/src/org/chromium/ui/test/util/modelutil/FakeViewProvider.java",
diff --git a/ui/android/javatests/src/org/chromium/ui/test/util/DeviceRestriction.java b/ui/android/javatests/src/org/chromium/ui/test/util/DeviceRestriction.java
index ff3c68c..7f789b7 100644
--- a/ui/android/javatests/src/org/chromium/ui/test/util/DeviceRestriction.java
+++ b/ui/android/javatests/src/org/chromium/ui/test/util/DeviceRestriction.java
@@ -4,12 +4,14 @@
 
 package org.chromium.ui.test.util;
 
+import org.chromium.base.BuildInfo;
+import org.chromium.base.ThreadUtils;
 import org.chromium.base.test.util.Restriction;
+import org.chromium.base.test.util.RestrictionSkipCheck;
 
 /**
  * DeviceRestrictions list device form factor restrictions, that are usable with the {@link
- * Restriction} annotation in layers depending on device type.
- * E.g. <code>
+ * Restriction} annotation in layers depending on device type. E.g. <code>
  *   \@Restriction({DeviceRestriction.RESTRICTION_TYPE_AUTO})
  * </code>
  */
@@ -19,4 +21,20 @@
 
     /** Specifies the test is only valid on non-automotive form factors. */
     public static final String RESTRICTION_TYPE_NON_AUTO = "Non Auto";
+
+    private static Boolean sIsAuto;
+
+    private static boolean isAuto() {
+        if (sIsAuto == null) {
+            sIsAuto =
+                    ThreadUtils.runOnUiThreadBlockingNoException(
+                            () -> BuildInfo.getInstance().isAutomotive);
+        }
+        return sIsAuto;
+    }
+
+    public static void registerChecks(RestrictionSkipCheck check) {
+        check.addHandler(RESTRICTION_TYPE_AUTO, () -> !isAuto());
+        check.addHandler(RESTRICTION_TYPE_NON_AUTO, () -> isAuto());
+    }
 }
diff --git a/ui/android/javatests/src/org/chromium/ui/test/util/DeviceRestrictionSkipCheck.java b/ui/android/javatests/src/org/chromium/ui/test/util/DeviceRestrictionSkipCheck.java
deleted file mode 100644
index 7d3fde37..0000000
--- a/ui/android/javatests/src/org/chromium/ui/test/util/DeviceRestrictionSkipCheck.java
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright 2023 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.ui.test.util;
-
-import android.content.Context;
-import android.text.TextUtils;
-
-import org.chromium.base.BuildInfo;
-import org.chromium.base.ThreadUtils;
-import org.chromium.base.test.util.RestrictionSkipCheck;
-
-/** Checks if any restrictions exist and skip the test if it meets those restrictions. */
-public class DeviceRestrictionSkipCheck extends RestrictionSkipCheck {
-    public DeviceRestrictionSkipCheck(Context targetContext) {
-        super(targetContext);
-    }
-
-    @Override
-    protected boolean restrictionApplies(String restriction) {
-        boolean restrictedToAuto =
-                TextUtils.equals(restriction, DeviceRestriction.RESTRICTION_TYPE_AUTO);
-        boolean restrictedToNonAuto =
-                TextUtils.equals(restriction, DeviceRestriction.RESTRICTION_TYPE_NON_AUTO);
-
-        boolean noRestrictionsApply = !restrictedToAuto && !restrictedToNonAuto;
-        if (noRestrictionsApply) {
-            return false;
-        }
-
-        boolean isAuto =
-                ThreadUtils.runOnUiThreadBlockingNoException(
-                        () -> BuildInfo.getInstance().isAutomotive);
-        boolean autoRestrictionApplies = restrictedToAuto && !isAuto;
-        boolean nonAutoRestrictionApplies = restrictedToNonAuto && isAuto;
-        return autoRestrictionApplies || nonAutoRestrictionApplies;
-    }
-}
diff --git a/ui/android/javatests/src/org/chromium/ui/test/util/GmsCoreVersionRestriction.java b/ui/android/javatests/src/org/chromium/ui/test/util/GmsCoreVersionRestriction.java
index aeedc279..6a3ac2e 100644
--- a/ui/android/javatests/src/org/chromium/ui/test/util/GmsCoreVersionRestriction.java
+++ b/ui/android/javatests/src/org/chromium/ui/test/util/GmsCoreVersionRestriction.java
@@ -4,7 +4,10 @@
 
 package org.chromium.ui.test.util;
 
+import org.chromium.base.BuildInfo;
+import org.chromium.base.ThreadUtils;
 import org.chromium.base.test.util.Restriction;
+import org.chromium.base.test.util.RestrictionSkipCheck;
 
 /**
  * GmsCoreVersionRestriction list form factor restrictions, that are usable with the {@link
@@ -18,4 +21,31 @@
 
     /** Specifies the test to run only with the GMS Core version greater or equal 2020w02. */
     public static final String RESTRICTION_TYPE_VERSION_GE_2020W02 = "GMSCoreVersion2020w02";
+
+    private static final int VERSION_2020W02 = 20415000;
+    private static final int VERSION_22W30 = 223012000;
+    private static Integer sGmsVersion;
+
+    public static void registerChecks(RestrictionSkipCheck check) {
+        check.addHandler(RESTRICTION_TYPE_VERSION_GE_22W30, () -> getVersion() < VERSION_22W30);
+        check.addHandler(RESTRICTION_TYPE_VERSION_GE_2020W02, () -> getVersion() < VERSION_2020W02);
+    }
+
+    private static int tryParseInt(String value, int defaultVal) {
+        try {
+            return Integer.parseInt(value);
+        } catch (NumberFormatException e) {
+            return defaultVal;
+        }
+    }
+
+    private static int getVersion() {
+        if (sGmsVersion == null) {
+            String gmsVersionStr =
+                    ThreadUtils.runOnUiThreadBlockingNoException(
+                            () -> BuildInfo.getInstance().gmsVersionCode);
+            sGmsVersion = tryParseInt(gmsVersionStr, 0);
+        }
+        return sGmsVersion;
+    }
 }
diff --git a/ui/android/javatests/src/org/chromium/ui/test/util/GmsCoreVersionRestrictionSkipCheck.java b/ui/android/javatests/src/org/chromium/ui/test/util/GmsCoreVersionRestrictionSkipCheck.java
deleted file mode 100644
index daea3eb..0000000
--- a/ui/android/javatests/src/org/chromium/ui/test/util/GmsCoreVersionRestrictionSkipCheck.java
+++ /dev/null
@@ -1,50 +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.
-
-package org.chromium.ui.test.util;
-
-import android.content.Context;
-
-import org.chromium.base.BuildInfo;
-import org.chromium.base.ThreadUtils;
-import org.chromium.base.test.util.RestrictionSkipCheck;
-
-/** Checks if any restrictions exist and skip the test if it meets those restrictions. */
-public class GmsCoreVersionRestrictionSkipCheck extends RestrictionSkipCheck {
-    private static final int VERSION_2020W02 = 20415000;
-    private static final int VERSION_22W30 = 223012000;
-
-    public GmsCoreVersionRestrictionSkipCheck(Context targetContext) {
-        super(targetContext);
-    }
-
-    @Override
-    protected boolean restrictionApplies(String restriction) {
-        boolean v2022w30 =
-                GmsCoreVersionRestriction.RESTRICTION_TYPE_VERSION_GE_22W30.equals(restriction);
-        boolean v2020w02 =
-                GmsCoreVersionRestriction.RESTRICTION_TYPE_VERSION_GE_2020W02.equals(restriction);
-
-        if (!v2020w02 && !v2022w30) {
-            return false;
-        }
-
-        String gmsVersionStr =
-                ThreadUtils.runOnUiThreadBlockingNoException(
-                        () -> BuildInfo.getInstance().gmsVersionCode);
-        int version = tryParseInt(gmsVersionStr, 0);
-        if (v2020w02) {
-            return version < VERSION_2020W02;
-        }
-        return version < VERSION_22W30;
-    }
-
-    private static int tryParseInt(String value, int defaultVal) {
-        try {
-            return Integer.parseInt(value);
-        } catch (NumberFormatException e) {
-            return defaultVal;
-        }
-    }
-}
diff --git a/ui/android/javatests/src/org/chromium/ui/test/util/UiRestriction.java b/ui/android/javatests/src/org/chromium/ui/test/util/UiRestriction.java
index fdbaf44e..42d3f9c45 100644
--- a/ui/android/javatests/src/org/chromium/ui/test/util/UiRestriction.java
+++ b/ui/android/javatests/src/org/chromium/ui/test/util/UiRestriction.java
@@ -4,12 +4,14 @@
 
 package org.chromium.ui.test.util;
 
+import org.chromium.base.ThreadUtils;
 import org.chromium.base.test.util.Restriction;
+import org.chromium.base.test.util.RestrictionSkipCheck;
+import org.chromium.ui.base.DeviceFormFactor;
 
 /**
- * UiRestrictions list form factor restrictions, that are usable with
- * the {@link Restriction} annotation in layers depending on //ui.
- * E.g. <code>
+ * UiRestrictions list form factor restrictions, that are usable with the {@link Restriction}
+ * annotation in layers depending on //ui. E.g. <code>
  *   \@Restriction({UiRestriction.RESTRICTION_TYPE_PHONE})
  * </code>
  */
@@ -19,4 +21,19 @@
 
     /** Specifies the test is only valid on tablet sized screens. */
     public static final String RESTRICTION_TYPE_TABLET = "Tablet";
+
+    private static Boolean sIsTablet;
+
+    private static boolean isTablet() {
+        if (sIsTablet == null) {
+            sIsTablet =
+                    ThreadUtils.runOnUiThreadBlockingNoException(() -> DeviceFormFactor.isTablet());
+        }
+        return sIsTablet;
+    }
+
+    public static void registerChecks(RestrictionSkipCheck check) {
+        check.addHandler(UiRestriction.RESTRICTION_TYPE_PHONE, () -> isTablet());
+        check.addHandler(UiRestriction.RESTRICTION_TYPE_TABLET, () -> !isTablet());
+    }
 }
diff --git a/ui/android/javatests/src/org/chromium/ui/test/util/UiRestrictionSkipCheck.java b/ui/android/javatests/src/org/chromium/ui/test/util/UiRestrictionSkipCheck.java
deleted file mode 100644
index a0dad46c..0000000
--- a/ui/android/javatests/src/org/chromium/ui/test/util/UiRestrictionSkipCheck.java
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2017 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.ui.test.util;
-
-import android.content.Context;
-import android.text.TextUtils;
-
-import org.chromium.base.ThreadUtils;
-import org.chromium.base.test.util.RestrictionSkipCheck;
-import org.chromium.ui.base.DeviceFormFactor;
-
-/** Checks if any restrictions exist and skip the test if it meets those restrictions. */
-public class UiRestrictionSkipCheck extends RestrictionSkipCheck {
-    public UiRestrictionSkipCheck(Context targetContext) {
-        super(targetContext);
-    }
-
-    @Override
-    protected boolean restrictionApplies(String restriction) {
-        boolean phoneOnly = TextUtils.equals(restriction, UiRestriction.RESTRICTION_TYPE_PHONE);
-        boolean tabletOnly = TextUtils.equals(restriction, UiRestriction.RESTRICTION_TYPE_TABLET);
-        if (!phoneOnly && !tabletOnly) {
-            return false;
-        }
-        boolean isTablet =
-                ThreadUtils.runOnUiThreadBlockingNoException(() -> DeviceFormFactor.isTablet());
-        return phoneOnly && isTablet || tabletOnly && !isTablet;
-    }
-}
diff --git a/ui/base/ui_base_features.cc b/ui/base/ui_base_features.cc
index f029496..f00b6911 100644
--- a/ui/base/ui_base_features.cc
+++ b/ui/base/ui_base_features.cc
@@ -368,17 +368,6 @@
   return keyboard_accessible_tooltip_enabled;
 }
 
-// Enables trackpad gestures to dismiss notifications. Also, updates gestures to
-// only dismiss notifications when swiping towards the notification center.
-// TODO(https://b/288337080): Remove this flag once the feature is ready.
-BASE_FEATURE(kNotificationGesturesUpdate,
-             "NotificationGesturesUpdate",
-             base::FEATURE_ENABLED_BY_DEFAULT);
-
-bool IsNotificationGesturesUpdateEnabled() {
-  return base::FeatureList::IsEnabled(kNotificationGesturesUpdate);
-}
-
 BASE_FEATURE(kSynchronousPageFlipTesting,
              "SynchronousPageFlipTesting",
              base::FEATURE_ENABLED_BY_DEFAULT);
diff --git a/ui/base/ui_base_features.h b/ui/base/ui_base_features.h
index bb579e5..3b3b8767 100644
--- a/ui/base/ui_base_features.h
+++ b/ui/base/ui_base_features.h
@@ -100,11 +100,6 @@
 BASE_DECLARE_FEATURE(kKeyboardAccessibleTooltip);
 COMPONENT_EXPORT(UI_BASE_FEATURES) bool IsKeyboardAccessibleTooltipEnabled();
 
-// Used to enable gesture changes for notifications.
-COMPONENT_EXPORT(UI_BASE_FEATURES)
-BASE_DECLARE_FEATURE(kNotificationGesturesUpdate);
-COMPONENT_EXPORT(UI_BASE_FEATURES) bool IsNotificationGesturesUpdateEnabled();
-
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 COMPONENT_EXPORT(UI_BASE_FEATURES) BASE_DECLARE_FEATURE(kDeprecateAltClick);
 
diff --git a/ui/display/manager/display_change_observer_unittest.cc b/ui/display/manager/display_change_observer_unittest.cc
index 9eeb596..5594efa 100644
--- a/ui/display/manager/display_change_observer_unittest.cc
+++ b/ui/display/manager/display_change_observer_unittest.cc
@@ -533,7 +533,7 @@
 
   const auto color_space = display_color_spaces.GetRasterColorSpace();
   EXPECT_TRUE(color_space.IsValid());
-  EXPECT_EQ(color_space.GetPrimaryID(), gfx::ColorSpace::PrimaryID::P3);
+  EXPECT_EQ(color_space.GetPrimaryID(), gfx::ColorSpace::PrimaryID::BT709);
   EXPECT_EQ(color_space.GetTransferID(), gfx::ColorSpace::TransferID::SRGB);
 }
 
diff --git a/ui/display/util/display_util.cc b/ui/display/util/display_util.cc
index 9cbf3a8..0342ba5 100644
--- a/ui/display/util/display_util.cc
+++ b/ui/display/util/display_util.cc
@@ -302,8 +302,9 @@
                                    DisplaySnapshot::PrimaryFormat());
   }
 
-  skcms_Matrix3x3 primary_matrix{};
-  snapshot_color_space.GetPrimaryMatrix(&primary_matrix);
+  // Make all displays report that they have sRGB primaries. Hardware color
+  // management will convert to the device's color primaries.
+  skcms_Matrix3x3 primary_matrix = SkNamedGamut::kSRGB;
 
   // Reconstruct the native colorspace with an IEC61966 2.1 transfer function
   // for SDR content (matching that of sRGB).
diff --git a/ui/display/win/display_info.cc b/ui/display/win/display_info.cc
index c112e65..78166bf 100644
--- a/ui/display/win/display_info.cc
+++ b/ui/display/win/display_info.cc
@@ -6,7 +6,6 @@
 
 #include "base/hash/hash.h"
 #include "base/metrics/histogram_functions.h"
-#include "base/numerics/safe_conversions.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
 #include "ui/display/win/display_config_helper.h"
@@ -52,22 +51,20 @@
 
 // static
 int64_t DisplayInfo::DisplayIdFromMonitorInfo(const MONITORINFOEX& monitor) {
-  // Use the monitor's config path target ID as the display ID.
-  // This seems to be broadly available, unique for each monitor of the device,
-  // and stable across display configuration changes and device restarts.
+  // Derive a display ID from the monitor adapter ID per-adapter monitor ID.
+  // This provides better ID stability when adding/removing displays vs
+  // MONITORINFOEX::szDevice which can cause subtle unexpected behavior.
   std::optional<DISPLAYCONFIG_PATH_INFO> config_path =
       GetDisplayConfigPathInfo(monitor);
   // Record if DISPLAYCONFIG_PATH_INFO is available or not.
   base::UmaHistogramBoolean("Windows.LegacyDisplayIdAlgorithm",
                             !config_path.has_value());
-
   if (config_path.has_value()) {
-    return base::checked_cast<int64_t>(config_path->targetInfo.id);
+    return static_cast<int64_t>(base::PersistentHash(base::StringPrintf(
+        "%lu/%li/%u", config_path->targetInfo.adapterId.LowPart,
+        config_path->targetInfo.adapterId.HighPart,
+        config_path->targetInfo.id)));
   }
-  // MONITORINFOEX::szDevice is a plausible backup with some notable drawbacks.
-  // This value (e.g. "\\.\DISPLAY1") may change when adding/removing displays,
-  // and even be reassigned between physical monitors during those changes,
-  // which can cause subtle unexpected behavior.
   return static_cast<int64_t>(
       base::PersistentHash(base::WideToUTF8(monitor.szDevice)));
 }
diff --git a/ui/message_center/views/message_view.cc b/ui/message_center/views/message_view.cc
index 735210b..1cb2824 100644
--- a/ui/message_center/views/message_view.cc
+++ b/ui/message_center/views/message_view.cc
@@ -12,7 +12,6 @@
 #include "ui/accessibility/ax_node_data.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
-#include "ui/base/ui_base_features.h"
 #include "ui/color/color_id.h"
 #include "ui/color/color_provider.h"
 #include "ui/compositor/layer.h"
@@ -77,10 +76,7 @@
       timestamp_(notification.timestamp()),
       slide_out_controller_(this, this) {
   SetNotifyEnterExitOnChild(true);
-
-  if (features::IsNotificationGesturesUpdateEnabled()) {
-    slide_out_controller_.set_trackpad_gestures_enabled(true);
-  }
+  slide_out_controller_.set_trackpad_gestures_enabled(true);
   SetFocusBehavior(FocusBehavior::ALWAYS);
   views::FocusRing::Install(this);
   views::FocusRing::Get(this)->SetOutsetFocusRingDisabled(true);
@@ -412,8 +408,7 @@
   }
 
   auto* message_center = MessageCenter::Get();
-  if (features::IsNotificationGesturesUpdateEnabled() &&
-      message_center->FindPopupNotificationById(notification_id_copy)) {
+  if (message_center->FindPopupNotificationById(notification_id_copy)) {
     message_center->MarkSinglePopupAsShown(notification_id_copy,
                                            /*mark_notification_as_read=*/true);
     return;
diff --git a/ui/message_center/views/notification_view_base_unittest.cc b/ui/message_center/views/notification_view_base_unittest.cc
index 871ca196..5b32b5c 100644
--- a/ui/message_center/views/notification_view_base_unittest.cc
+++ b/ui/message_center/views/notification_view_base_unittest.cc
@@ -1,6 +1,9 @@
 // Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
+
+#include "ui/message_center/views/notification_view_base.h"
+
 #include <memory>
 
 #include "base/memory/raw_ptr.h"
@@ -12,7 +15,6 @@
 #include "build/chromeos_buildflags.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/skia/include/core/SkColor.h"
-#include "ui/base/ui_base_features.h"
 #include "ui/color/color_id.h"
 #include "ui/color/color_provider.h"
 #include "ui/compositor/layer.h"
@@ -29,7 +31,6 @@
 #include "ui/message_center/views/notification_control_buttons_view.h"
 #include "ui/message_center/views/notification_header_view.h"
 #include "ui/message_center/views/notification_view.h"
-#include "ui/message_center/views/notification_view_base.h"
 #include "ui/message_center/views/padded_button.h"
 #include "ui/message_center/views/proportional_image_view.h"
 #include "ui/native_theme/native_theme.h"
@@ -1177,26 +1178,6 @@
                    ->GetVisible());
 }
 
-class NotificationViewBaseWithNotificationGestureUpdateTest
-    : public NotificationViewBaseTest {
- public:
-  NotificationViewBaseWithNotificationGestureUpdateTest() = default;
-  NotificationViewBaseWithNotificationGestureUpdateTest(
-      const NotificationViewBaseTest&) = delete;
-  NotificationViewBaseTest& operator=(const NotificationViewBaseTest&) = delete;
-
-  // Overridden from ViewsTestBase:
-  void SetUp() override {
-    scoped_feature_list_.InitWithFeatures(
-        {features::kNotificationGesturesUpdate}, {});
-
-    NotificationViewBaseTest::SetUp();
-  }
-
- private:
-  base::test::ScopedFeatureList scoped_feature_list_;
-};
-
 #if BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_FUCHSIA)
 #define MAYBE_SlideOutWithMessageCenterView \
   DISABLED_SlideOutWithMessageCenterView
@@ -1206,8 +1187,7 @@
 // Tests slide out behavior when the `MessageCenterView` exists. The
 // notification's popup should be dismissed but the notification should not be
 // removed.
-TEST_F(NotificationViewBaseWithNotificationGestureUpdateTest,
-       MAYBE_SlideOutWithMessageCenterView) {
+TEST_F(NotificationViewBaseTest, MAYBE_SlideOutWithMessageCenterView) {
   SetHasMessageCenterView(/*has_message_center_view=*/true);
 
   ui::ScopedAnimationDurationScaleMode zero_duration_scope(
@@ -1237,15 +1217,16 @@
 #else
 #define MAYBE_SlideOutByTrackpad SlideOutByTrackpad
 #endif
-TEST_F(NotificationViewBaseWithNotificationGestureUpdateTest,
-       MAYBE_SlideOutByTrackpad) {
+TEST_F(NotificationViewBaseTest, MAYBE_SlideOutByTrackpad) {
   ui::ScopedAnimationDurationScaleMode zero_duration_scope(
       ui::ScopedAnimationDurationScaleMode::ZERO_DURATION);
 
   ui::test::EventGenerator generator(
       GetRootWindow(notification_view()->GetWidget()));
-  generator.ScrollSequence(gfx::Point(), base::TimeDelta(), /*x_offset=*/20,
-                           /*y_offset=*/0, /*steps=*/1, /*num_fingers=*/2);
+  generator.ScrollSequence(
+      gfx::Point(), base::TimeDelta(),
+      /*x_offset=*/notification_view()->bounds().width() + 1,
+      /*y_offset=*/0, /*steps=*/1, /*num_fingers=*/2);
   EXPECT_TRUE(IsPopupRemovedAfterIdle(kDefaultNotificationId));
 }
 
diff --git a/ui/ozone/platform/drm/gpu/drm_display.cc b/ui/ozone/platform/drm/gpu/drm_display.cc
index 39bc7dfe..6436fe3 100644
--- a/ui/ozone/platform/drm/gpu/drm_display.cc
+++ b/ui/ozone/platform/drm/gpu/drm_display.cc
@@ -4,6 +4,7 @@
 
 #include "ui/ozone/platform/drm/gpu/drm_display.h"
 
+#include <xf86drm.h>
 #include <xf86drmMode.h>
 
 #include <algorithm>
@@ -152,11 +153,15 @@
       base::FeatureList::IsEnabled(display::features::kUseHDRTransferFunction);
 
   if (is_hdr_capable_ &&
+      display_snapshot.color_space() == gfx::ColorSpace::CreateHDR10() &&
       base::FeatureList::IsEnabled(
           display::features::kEnableExternalDisplayHDR10Mode)) {
     output_primaries = SkNamedPrimariesExt::kRec2020;
     SetColorspaceProperty(display_snapshot.color_space());
     SetHdrOutputMetadata(display_snapshot.color_space());
+  } else {
+    SetColorspaceProperty(gfx::ColorSpace::CreateSRGB());
+    ClearHdrOutputMetadata();
   }
 #endif
   drm_->plane_manager()->SetOutputColorSpace(crtc_, output_primaries);
@@ -358,14 +363,34 @@
   }
 }
 
+bool DrmDisplay::ClearHdrOutputMetadata() {
+  DCHECK(connector_);
+
+  ScopedDrmPropertyPtr hdr_output_metadata_property(
+      drm_->GetProperty(connector_.get(), kHdrOutputMetadata));
+  if (!hdr_output_metadata_property) {
+    PLOG(INFO) << "'" << kHdrOutputMetadata << "' property doesn't exist.";
+    return false;
+  }
+
+  if (!drm_->SetProperty(connector_->connector_id,
+                         hdr_output_metadata_property->prop_id, 0)) {
+    PLOG(INFO) << "Cannot set '" << kHdrOutputMetadata
+               << "' property on connector " << connector_->connector_id;
+    return false;
+  }
+
+  return true;
+}
+
 bool DrmDisplay::SetHdrOutputMetadata(const gfx::ColorSpace color_space) {
   DCHECK(connector_);
   DCHECK(hdr_static_metadata_.has_value());
   DCHECK(color_space.IsValid());
 
-  drm_hdr_output_metadata* hdr_output_metadata =
+  ScopedDrmHdrOutputMetadataPtr hdr_output_metadata(
       static_cast<drm_hdr_output_metadata*>(
-          malloc(sizeof(drm_hdr_output_metadata)));
+          drmMalloc(sizeof(drm_hdr_output_metadata))));
   hdr_output_metadata->metadata_type = 0;
   hdr_output_metadata->hdmi_metadata_type1.metadata_type = 0;
 
@@ -404,11 +429,14 @@
   hdr_output_metadata->hdmi_metadata_type1.white_point.y =
       primaries.fWY * kPrimariesFixedPoint;
 
-  ScopedDrmHdrOutputMetadataPtr hdr_output_metadata_blob(hdr_output_metadata);
-
   ScopedDrmPropertyBlob hdr_output_metadata_property_blob =
-      drm_->CreatePropertyBlob(hdr_output_metadata_blob.get(),
+      drm_->CreatePropertyBlob(hdr_output_metadata.get(),
                                sizeof(drm_hdr_output_metadata));
+  if (!hdr_output_metadata_property_blob) {
+    PLOG(INFO) << "Cannot create '" << kHdrOutputMetadata << "' property blob.";
+    return false;
+  }
+
   ScopedDrmPropertyPtr hdr_output_metadata_property(
       drm_->GetProperty(connector_.get(), kHdrOutputMetadata));
   if (!hdr_output_metadata_property) {
@@ -416,7 +444,8 @@
     return false;
   }
 
-  if (!drm_->SetProperty(connector_->connector_id,
+  if (!hdr_output_metadata_property->prop_id ||
+      !drm_->SetProperty(connector_->connector_id,
                          hdr_output_metadata_property->prop_id,
                          hdr_output_metadata_property_blob->id())) {
     PLOG(INFO) << "Cannot set '" << kHdrOutputMetadata << "' property.";
@@ -427,19 +456,19 @@
 
 bool DrmDisplay::SetColorspaceProperty(const gfx::ColorSpace color_space) {
   DCHECK(connector_);
-  DCHECK(hdr_static_metadata_.has_value());
   ScopedDrmPropertyPtr color_space_property(
       drm_->GetProperty(connector_.get(), kColorSpace));
   if (!color_space_property) {
     PLOG(INFO) << "'" << kColorSpace << "' property doesn't exist.";
     return false;
   }
-  if (!drm_->SetProperty(
+  if (!color_space_property->prop_id ||
+      !drm_->SetProperty(
           connector_->connector_id, color_space_property->prop_id,
           GetEnumValueForName(*drm_, color_space_property->prop_id,
                               GetNameForColorspace(color_space)))) {
     PLOG(INFO) << "Cannot set '" << GetNameForColorspace(color_space)
-               << "' to 'Colorspace' property.";
+               << "' to '" << kColorSpace << "' property.";
     return false;
   }
   return true;
diff --git a/ui/ozone/platform/drm/gpu/drm_display.h b/ui/ozone/platform/drm/gpu/drm_display.h
index 8c031ba..9098e07 100644
--- a/ui/ozone/platform/drm/gpu/drm_display.h
+++ b/ui/ozone/platform/drm/gpu/drm_display.h
@@ -106,6 +106,7 @@
  private:
   gfx::HDRStaticMetadata::Eotf GetEotf(
       const gfx::ColorSpace::TransferID transfer_id);
+  bool ClearHdrOutputMetadata();
 
   const int64_t display_id_;
   const int64_t base_connector_id_;
diff --git a/ui/ozone/platform/drm/gpu/drm_gpu_util.cc b/ui/ozone/platform/drm/gpu/drm_gpu_util.cc
index ff22879..e97c576 100644
--- a/ui/ozone/platform/drm/gpu/drm_gpu_util.cc
+++ b/ui/ozone/platform/drm/gpu/drm_gpu_util.cc
@@ -229,7 +229,7 @@
 ScopedDrmColorCtmPtr CreateCTMBlob(const skcms_Matrix3x3& color_matrix,
                                    bool negative_values_broken) {
   ScopedDrmColorCtmPtr ctm(
-      static_cast<drm_color_ctm*>(malloc(sizeof(drm_color_ctm))));
+      static_cast<drm_color_ctm*>(drmMalloc(sizeof(drm_color_ctm))));
   for (size_t i = 0; i < 9; ++i) {
     float value = color_matrix.vals[i / 3][i % 3];
     if (value < 0) {
@@ -245,6 +245,7 @@
           static_cast<uint64_t>(value * kCtmValueScale) & kCtmValueMask;
     }
   }
+
   return ctm;
 }
 
diff --git a/ui/views/accessibility/view_accessibility.cc b/ui/views/accessibility/view_accessibility.cc
index 68bfd70..5ba80ca 100644
--- a/ui/views/accessibility/view_accessibility.cc
+++ b/ui/views/accessibility/view_accessibility.cc
@@ -35,7 +35,6 @@
     // These roles all have special meaning and shouldn't ever be
     // set on a View.
     case ax::mojom::Role::kDesktop:
-    case ax::mojom::Role::kDocument:  // Used for ARIA role="document".
     case ax::mojom::Role::kIframe:
     case ax::mojom::Role::kIframePresentational:
     case ax::mojom::Role::kPdfRoot:
@@ -45,6 +44,18 @@
     case ax::mojom::Role::kUnknown:
       return false;
 
+    // The role kDocument should not be allowed on Views, but it needs to be
+    // allowed temporarily for the CaptionBubbleLabel view. This is because the
+    // CaptionBubbleLabel is designed to be interacted with by a braille display
+    // in virtual buffer mode. In order to activate the virtual buffer in NVDA,
+    // we set the role to kDocument and the readonly restriction.
+    //
+    // TODO(crbug.com/339479333): Investigate this further to either add a
+    // views-specific role that maps to the document role on the various
+    // platform APIs, or remove this comment and update the allowed usage of the
+    // kDocument role.
+    case ax::mojom::Role::kDocument:  // Used for ARIA role="document".
+      return true;
     default:
       return true;
   }
@@ -170,6 +181,18 @@
     return;
   }
 
+  // The role and the name needs to be set on the data member passed to
+  // View::GetAccessibleNodeData in case the view changes either or both.
+  //
+  // TODO(accessibility): This won't be necessary once all Views initialize
+  // their role and name in Views::GetAccessibleNodeData, and that function
+  // is only called once to initialize the cached data.
+  data->role = data_.role;
+  data->SetNameFrom(GetCachedNameFrom());
+  if (!GetCachedName().empty()) {
+    data->SetName(GetCachedName());
+  }
+
   view_->GetAccessibleNodeData(data);
 
   // TODO(accessibility): This next check should be added to SetRole.
@@ -328,7 +351,7 @@
     if (name_from.has_value()) {
       SetName(name.value(), name_from.value());
     } else {
-      SetName(name.value(), ax::mojom::NameFrom::kAttribute);
+      SetName(name.value());
     }
   }
 
@@ -424,16 +447,14 @@
   SetRole(role);
 }
 
-void ViewAccessibility::SetName(const std::string& name,
+void ViewAccessibility::SetName(std::u16string name,
                                 ax::mojom::NameFrom name_from) {
-  DCHECK_NE(name_from, ax::mojom::NameFrom::kNone);
+  // Allow subclasses to adjust the name.
+  view_->AdjustAccessibleName(name, name_from);
+
   // Ensure we have a current `name_from` value. For instance, the name might
   // still be an empty string, but a view is now indicating that this is by
   // design by setting `NameFrom::kAttributeExplicitlyEmpty`.
-  DCHECK_EQ(name.empty(),
-            name_from == ax::mojom::NameFrom::kAttributeExplicitlyEmpty)
-      << "If the name is being removed to improve the user experience, "
-         "|name_from| should be set to |kAttributeExplicitlyEmpty|.";
   data_.SetNameFrom(name_from);
 
   if (name == GetCachedName()) {
@@ -458,23 +479,22 @@
     data_.SetName(name);
   }
 
+  view_->OnAccessibleNameChanged(name);
   NotifyEvent(ax::mojom::Event::kTextChanged, true);
 }
 
-void ViewAccessibility::SetName(const std::u16string& name,
+void ViewAccessibility::SetName(const std::string& name,
                                 ax::mojom::NameFrom name_from) {
-  std::string string_name = base::UTF16ToUTF8(name);
+  std::u16string string_name = base::UTF8ToUTF16(name);
   SetName(string_name, name_from);
 }
 
 void ViewAccessibility::SetName(const std::string& name) {
-  SetName(name, static_cast<ax::mojom::NameFrom>(data_.GetIntAttribute(
-                    ax::mojom::IntAttribute::kNameFrom)));
+  SetName(name, GetCachedNameFrom());
 }
 
 void ViewAccessibility::SetName(const std::u16string& name) {
-  SetName(name, static_cast<ax::mojom::NameFrom>(data_.GetIntAttribute(
-                    ax::mojom::IntAttribute::kNameFrom)));
+  SetName(name, GetCachedNameFrom());
 }
 
 void ViewAccessibility::SetName(View& naming_view) {
@@ -495,8 +515,7 @@
     DCHECK(!name.empty());
     SetName(name, ax::mojom::NameFrom::kRelatedElement);
   } else {
-    const std::string& name =
-        naming_view.GetViewAccessibility().GetCachedName();
+    std::u16string name = naming_view.GetViewAccessibility().GetCachedName();
     DCHECK(!name.empty());
     SetName(name, ax::mojom::NameFrom::kRelatedElement);
   }
@@ -506,8 +525,8 @@
       {naming_view.GetViewAccessibility().GetUniqueId().Get()});
 }
 
-const std::string& ViewAccessibility::GetCachedName() const {
-  return data_.GetStringAttribute(ax::mojom::StringAttribute::kName);
+std::u16string ViewAccessibility::GetCachedName() const {
+  return data_.GetString16Attribute(ax::mojom::StringAttribute::kName);
 }
 
 ax::mojom::NameFrom ViewAccessibility::GetCachedNameFrom() const {
@@ -600,8 +619,7 @@
 void ViewAccessibility::SetDescription(View& describing_view) {
   DCHECK_NE(view_, &describing_view);
 
-  const std::string& name =
-      describing_view.GetViewAccessibility().GetCachedName();
+  std::u16string name = describing_view.GetViewAccessibility().GetCachedName();
   if (name.empty()) {
     // TODO(javiercon): This is a temporary workaround for the scenarios where
     // the name is set via View::SetAccessibleName, which means that
diff --git a/ui/views/accessibility/view_accessibility.h b/ui/views/accessibility/view_accessibility.h
index 499113f..6cf2d86 100644
--- a/ui/views/accessibility/view_accessibility.h
+++ b/ui/views/accessibility/view_accessibility.h
@@ -179,7 +179,7 @@
   // follow the established pattern and be named GetName()
   // TODO(accessibility): Rename to GetName once the ViewsAX project is
   // completed and we don't have ViewAXPlatformNodeDelegate anymore.
-  const std::string& GetCachedName() const;
+  std::u16string GetCachedName() const;
 
   // Returns the source type of the accessible name.
   //
@@ -211,10 +211,10 @@
   // * kTitle: Name from a title attribute or element (HTML or SVG).
   // * kValue: Name from a value attribute (e.g. button).
   // * kPopoverAttribute: Name from a tooltip-style popover.
+  void SetName(std::u16string name, ax::mojom::NameFrom name_from);
   void SetName(const std::string& name, ax::mojom::NameFrom name_from);
-  void SetName(const std::u16string& name, ax::mojom::NameFrom name_from);
-  void SetName(const std::string& name);
   void SetName(const std::u16string& name);
+  void SetName(const std::string& name);
 
   // Sets the accessible name of this view to that of `naming_view`. Often
   // `naming_view` is a `views::Label`, but any view with an accessible name
@@ -409,6 +409,8 @@
   AccessibilityEventsCallback accessibility_events_callback_;
 
  private:
+  FRIEND_TEST_ALL_PREFIXES(ViewTest, PauseAccessibilityEvents);
+
   // Prune/Unprune all descendant views from the accessibility tree. We prune
   // for two reasons: 1) The view has been explicitly marked as a leaf node, 2)
   // The view is focusable and lacks focusable descendants (e.g. a button with a
diff --git a/ui/views/accessibility/view_ax_platform_node_delegate_unittest.cc b/ui/views/accessibility/view_ax_platform_node_delegate_unittest.cc
index 1cd13d8..f169707 100644
--- a/ui/views/accessibility/view_ax_platform_node_delegate_unittest.cc
+++ b/ui/views/accessibility/view_ax_platform_node_delegate_unittest.cc
@@ -471,21 +471,6 @@
   EXPECT_EQ(button_accessibility()->GetDescriptionFrom(),
             ax::mojom::DescriptionFrom::kNone);
 
-  // Setting the name to the empty string without explicitly setting the
-  // source to reflect that should trigger a DCHECK in SetName.
-  EXPECT_DCHECK_DEATH_WITH(
-      button_accessibility()->SetName("", ax::mojom::NameFrom::kAttribute),
-      "Check failed: name.empty\\(\\) == name_from == "
-      "ax::mojom::NameFrom::kAttributeExplicitlyEmpty");
-
-  // Setting the name to a non-empty string with a NameFrom of
-  // kAttributeExplicitlyEmpty should trigger a DCHECK in SetName.
-  EXPECT_DCHECK_DEATH_WITH(
-      button_accessibility()->SetName(
-          "foo", ax::mojom::NameFrom::kAttributeExplicitlyEmpty),
-      "Check failed: name.empty\\(\\) == name_from == "
-      "ax::mojom::NameFrom::kAttributeExplicitlyEmpty");
-
   button_accessibility()->SetName(
       "", ax::mojom::NameFrom::kAttributeExplicitlyEmpty);
   EXPECT_EQ(button_accessibility()->GetName(), "");
diff --git a/ui/views/animation/slide_out_controller.cc b/ui/views/animation/slide_out_controller.cc
index d41a778..8bbd2a40 100644
--- a/ui/views/animation/slide_out_controller.cc
+++ b/ui/views/animation/slide_out_controller.cc
@@ -13,6 +13,7 @@
 #include "ui/compositor/layer.h"
 #include "ui/compositor/scoped_layer_animation_settings.h"
 #include "ui/events/event.h"
+#include "ui/events/types/event_type.h"
 #include "ui/gfx/geometry/transform.h"
 #include "ui/views/animation/animation_builder.h"
 #include "ui/views/animation/slide_out_controller_delegate.h"
@@ -139,33 +140,43 @@
 }
 
 void SlideOutController::OnScrollEvent(ui::ScrollEvent* event) {
-  if (!trackpad_gestures_enabled_) {
+  // Ignore events if slide out by trackpad is not available.
+  if (!trackpad_gestures_enabled_ || mode_ != SlideMode::kFull) {
     return;
   }
 
-  // This threshold has been set to 20.f to stay consistent with the trackpad
-  // scroll threshold used to determine if the app list should be opened when
-  // scrolling on the shelf in ChromeOS.
-  constexpr float kScrollOffsetThreshold = 20.f;
-  if (abs(event->x_offset()) < kScrollOffsetThreshold ||
-      event->finger_count() != 2) {
+  // Ignore events where vertical offset is greater than horizontal (likely not
+  // a slide-out gesture).
+  if (abs(event->x_offset()) < abs(event->y_offset())) {
     return;
   }
 
-  if (mode_ == SlideMode::kFull) {
+  if (event->type() == ui::EventType::ET_SCROLL_FLING_CANCEL) {
+    gesture_amount_ = 0;
+  } else if (event->type() == ui::EventType::ET_SCROLL) {
+    if (event->finger_count() == 2) {
+      gesture_amount_ += event->x_offset();
+    }
+  } else if (event->type() == ui::EventType::ET_SCROLL_FLING_START) {
     auto* layer = delegate_->GetSlideOutLayer();
-    gfx::Transform transform;
-    transform.Translate(layer->bounds().width(), 0);
-    AnimationBuilder()
-        .OnEnded(base::BindOnce(&SlideOutController::OnSlideOut,
-                                weak_ptr_factory_.GetWeakPtr()))
-        .Once()
-        .SetDuration(base::Milliseconds(kSwipeOutTotalDurationMs))
-        .SetTransform(layer, transform, kSwipeTweenType)
-        .SetOpacity(layer, 0.f);
-    event->SetHandled();
-    return;
+    int width = layer->bounds().width();
+    if (abs(gesture_amount_) > width) {
+      int direction = gesture_amount_ > 0 ? -1 : 1;
+      gfx::Transform transform;
+      transform.Translate(direction * width, 0);
+
+      AnimationBuilder()
+          .OnEnded(base::BindOnce(&SlideOutController::OnSlideOut,
+                                  weak_ptr_factory_.GetWeakPtr()))
+          .Once()
+          .SetDuration(base::Milliseconds(kSwipeOutTotalDurationMs))
+          .SetTransform(layer, transform, kSwipeTweenType)
+          .SetOpacity(layer, 0.f);
+    }
+    gesture_amount_ = 0;
   }
+
+  event->SetHandled();
 }
 
 void SlideOutController::RestoreVisualState() {
diff --git a/ui/views/animation/slide_out_controller_unittest.cc b/ui/views/animation/slide_out_controller_unittest.cc
index 96407fd..3d6c55a8 100644
--- a/ui/views/animation/slide_out_controller_unittest.cc
+++ b/ui/views/animation/slide_out_controller_unittest.cc
@@ -15,6 +15,7 @@
 #include "ui/compositor/layer.h"
 #include "ui/events/event.h"
 #include "ui/events/test/event_generator.h"
+#include "ui/events/types/event_type.h"
 #include "ui/views/animation/slide_out_controller_delegate.h"
 #include "ui/views/test/views_test_base.h"
 #include "ui/views/view.h"
@@ -24,7 +25,6 @@
 namespace {
 constexpr int kSwipeControlWidth = 30;  // px
 constexpr int kTargetWidth = 200;       // px
-constexpr int kScrollOffsetThreshold = 20;
 }  // namespace
 
 class TestSlideOutControllerDelegate : public SlideOutControllerDelegate {
@@ -123,10 +123,12 @@
         ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_END));
   }
 
-  void PostTrackPadSwipeEvent(int swipe_amount, int finger_count) {
-    auto scroll_event = ui::ScrollEvent(ui::ET_SCROLL, gfx::PointF(),
-                                        gfx::PointF(), base::TimeTicks(), 0,
-                                        swipe_amount, 0, 0, 0, finger_count);
+  void PostTrackPadSwipeEvent(ui::EventType type,
+                              int swipe_amount,
+                              int finger_count) {
+    auto scroll_event =
+        ui::ScrollEvent(type, gfx::PointF(), gfx::PointF(), base::TimeTicks(),
+                        0, swipe_amount, 0, 0, 0, finger_count);
     slide_out_controller()->OnScrollEvent(&scroll_event);
   }
 
@@ -575,20 +577,28 @@
 };
 
 TEST_F(TrackPadGestureTest, SlideOut) {
-  // A slide out should not be triggered if the scroll offset is below
-  // `kScrollOffsetThreshold`.
-  PostTrackPadSwipeEvent(kScrollOffsetThreshold - 1, /*finger_count=*/2);
+  int width = delegate()->GetSlideOutLayer()->bounds().width();
+  // A slide out should not be triggered if the scroll offset isn't less greater
+  // than the view's width.
+  PostTrackPadSwipeEvent(ui::EventType::ET_SCROLL, width,
+                         /*finger_count=*/2);
+  PostTrackPadSwipeEvent(ui::EventType::ET_SCROLL_FLING_START, 0, 2);
   EXPECT_EQ(0, delegate()->slide_out_count_);
 
   // A slide out should not be triggered if the finger count is not equal to 2.
-  PostTrackPadSwipeEvent(kScrollOffsetThreshold,
+  PostTrackPadSwipeEvent(ui::EventType::ET_SCROLL, width + 1,
+                         /*finger_count=*/3);
+  PostTrackPadSwipeEvent(ui::EventType::ET_SCROLL_FLING_START, 0,
                          /*finger_count=*/3);
   EXPECT_EQ(0, delegate()->slide_out_count_);
 
-  // A slide out should be triggered with both of the conditions above being
-  // met.
-  PostTrackPadSwipeEvent(kScrollOffsetThreshold,
+  PostTrackPadSwipeEvent(ui::EventType::ET_SCROLL, width + 1,
                          /*finger_count=*/2);
+  // A slide out should not be triggered until the `ET_SCROLL_FLING_START` is
+  // posted.
+  EXPECT_EQ(0, delegate()->slide_out_count_);
+
+  PostTrackPadSwipeEvent(ui::EventType::ET_SCROLL_FLING_START, 0, 2);
   EXPECT_EQ(1, delegate()->slide_out_count_);
 }
 
diff --git a/ui/views/controls/label.cc b/ui/views/controls/label.cc
index 5fc895a..2c773355 100644
--- a/ui/views/controls/label.cc
+++ b/ui/views/controls/label.cc
@@ -115,7 +115,12 @@
   ClearDisplayText();
 
   if (GetAccessibleName().empty() || GetAccessibleName() == current_text) {
-    SetAccessibleName(new_text);
+    if (new_text.empty()) {
+      SetAccessibleName(new_text,
+                        ax::mojom::NameFrom::kAttributeExplicitlyEmpty);
+    } else {
+      SetAccessibleName(new_text);
+    }
   }
 
   OnPropertyChanged(&full_text_ + kLabelText,
diff --git a/ui/views/view.cc b/ui/views/view.cc
index 423a2bd..1164b9d 100644
--- a/ui/views/view.cc
+++ b/ui/views/view.cc
@@ -237,8 +237,6 @@
     SetProperty(kViewStackTraceKey,
                 std::make_unique<base::debug::StackTrace>());
   }
-
-  ax_node_data_ = std::make_unique<ui::AXNodeData>();
 }
 
 View::~View() {
@@ -2080,19 +2078,6 @@
   return *view_accessibility_;
 }
 
-void View::GetAccessibleNodeData(ui::AXNodeData* node_data) {
-  // `ViewAccessibility::GetAccessibleNodeData` populates the id and classname
-  // values prior to asking the View for its data. We don't want to stomp on
-  // those values.
-  ax_node_data_->id = node_data->id;
-  ax_node_data_->AddStringAttribute(
-      ax::mojom::StringAttribute::kClassName,
-      node_data->GetStringAttribute(ax::mojom::StringAttribute::kClassName));
-
-  // Copy everything set by the property setters.
-  *node_data = *ax_node_data_;
-}
-
 void View::SetAccessibilityProperties(
     std::optional<ax::mojom::Role> role,
     std::optional<std::u16string> name,
@@ -2100,138 +2085,39 @@
     std::optional<std::u16string> role_description,
     std::optional<ax::mojom::NameFrom> name_from,
     std::optional<ax::mojom::DescriptionFrom> description_from) {
-  // TODO(accessibility): Remove this attribute once we migrate the
-  // SetAccessibilityProperties function to ViewAccessibility.
-  base::AutoReset<bool> initializing(&pause_accessibility_events_, true);
-  if (role.has_value()) {
-    if (role_description.has_value()) {
-      SetAccessibleRole(role.value(), role_description.value());
-    } else {
-      SetAccessibleRole(role.value());
-    }
-  }
-
-  // Defining the NameFrom value without specifying the name doesn't make much
-  // sense. The only exception might be if the NameFrom is setting the name to
-  // explicitly empty. In order to prevent surprising/confusing behavior, we
-  // only use the NameFrom value if we have an explicit name. As a result, any
-  // caller setting the name to explicitly empty must set the name to an empty
-  // string.
-  if (name.has_value()) {
-    if (name_from.has_value()) {
-      SetAccessibleName(name.value(), name_from.value());
-    } else {
-      SetAccessibleName(name.value());
-    }
-  }
-
-  // See the comment above regarding the NameFrom value.
-  if (description.has_value()) {
-    if (description_from.has_value()) {
-      GetViewAccessibility().SetDescription(description.value(),
-                                            description_from.value());
-    } else {
-      GetViewAccessibility().SetDescription(description.value());
-    }
-  }
+  GetViewAccessibility().SetProperties(
+      role, name, description, role_description, name_from, description_from);
 }
 
 void View::SetAccessibleName(const std::u16string& name) {
-  SetAccessibleName(
-      name, static_cast<ax::mojom::NameFrom>(ax_node_data_->GetIntAttribute(
-                ax::mojom::IntAttribute::kNameFrom)));
+  SetAccessibleName(name, GetViewAccessibility().GetCachedNameFrom());
 }
 
 void View::SetAccessibleName(std::u16string name,
                              ax::mojom::NameFrom name_from) {
-  // Allow subclasses to adjust the name.
-  AdjustAccessibleName(name, name_from);
-
-  // Ensure we have a current `name_from` value. For instance, the name might
-  // still be an empty string, but a view is now indicating that this is by
-  // design by setting `NameFrom::kAttributeExplicitlyEmpty`.
-  ax_node_data_->SetNameFrom(name_from);
-
-  if (name == accessible_name_) {
-    return;
-  }
-
-  if (name.empty()) {
-    ax_node_data_->RemoveStringAttribute(ax::mojom::StringAttribute::kName);
-  } else if (ax_node_data_->role != ax::mojom::Role::kUnknown &&
-             ax_node_data_->role != ax::mojom::Role::kNone) {
-    // TODO(accessibility): This is to temporarily work around the DCHECK
-    // in `AXNodeData` that wants to have a role to calculate a name-from.
-    // If we don't have a role yet, don't add it to the data until we do.
-    // See `SetAccessibleRole` where we check for and handle this condition.
-    // Also note that the `SetAccessibilityProperties` function allows view
-    // authors to set the role and name at once, if all views use it, we can
-    // remove this workaround.
-    ax_node_data_->SetName(name);
-  }
-
-  accessible_name_ = name;
-  OnPropertyChanged(&accessible_name_, kPropertyEffectsNone);
-  OnAccessibleNameChanged(name);
-  NotifyAccessibilityEvent(ax::mojom::Event::kTextChanged, true);
+  GetViewAccessibility().SetName(name, name_from);
 }
 
 void View::SetAccessibleName(View* naming_view) {
   DCHECK(naming_view);
-  DCHECK_NE(this, naming_view);
-
-  const std::u16string& name = naming_view->GetAccessibleName();
-  DCHECK(!name.empty());
-
-  SetAccessibleName(name, ax::mojom::NameFrom::kRelatedElement);
-  ax_node_data_->AddIntListAttribute(
-      ax::mojom::IntListAttribute::kLabelledbyIds,
-      {naming_view->GetViewAccessibility().GetUniqueId().Get()});
+  GetViewAccessibility().SetName(*naming_view);
 }
 
-const std::u16string& View::GetAccessibleName() const {
-  return accessible_name_;
+std::u16string View::GetAccessibleName() const {
+  return GetViewAccessibility().GetCachedName();
 }
 
 void View::SetAccessibleRole(const ax::mojom::Role role) {
-  if (role == accessible_role_) {
-    return;
-  }
-
-  ax_node_data_->role = role;
-  if (role != ax::mojom::Role::kUnknown && role != ax::mojom::Role::kNone) {
-    if (ax_node_data_->GetStringAttribute(ax::mojom::StringAttribute::kName)
-            .empty() &&
-        !accessible_name_.empty()) {
-      // TODO(accessibility): This is to temporarily work around the DCHECK
-      // that wants to have a role to calculate a name-from. If we have a
-      // name in our properties but not in our `AXNodeData`, the name was
-      // set prior to the role. Now that we have a valid role, we can set
-      // the name. See `SetAccessibleName` for where we delayed setting it.
-      ax_node_data_->SetName(accessible_name_);
-    }
-  }
-
-  accessible_role_ = role;
-  OnPropertyChanged(&accessible_role_, kPropertyEffectsNone);
+  GetViewAccessibility().SetRole(role);
 }
 
 void View::SetAccessibleRole(const ax::mojom::Role role,
                              const std::u16string& role_description) {
-  if (!role_description.empty()) {
-    ax_node_data_->AddStringAttribute(
-        ax::mojom::StringAttribute::kRoleDescription,
-        base::UTF16ToUTF8(role_description));
-  } else {
-    ax_node_data_->RemoveStringAttribute(
-        ax::mojom::StringAttribute::kRoleDescription);
-  }
-
-  SetAccessibleRole(role);
+  GetViewAccessibility().SetRole(role, role_description);
 }
 
 ax::mojom::Role View::GetAccessibleRole() const {
-  return accessible_role_;
+  return GetViewAccessibility().GetCachedRole();
 }
 
 void View::SetAccessibleDescription(const std::u16string& description) {
@@ -2300,16 +2186,6 @@
 
 void View::NotifyAccessibilityEvent(ax::mojom::Event event_type,
                                     bool send_native_event) {
-  // TODO(accessibility): Remove this condition once we migrate the
-  // SetAccessibilityProperties function to ViewAccessibility.
-  //
-  // If `pause_accessibility_events_` is true, it means we are initializing
-  // property values. In this specific case, we do not want to notify platform
-  // assistive technologies that a property has changed.
-  if (pause_accessibility_events_) {
-    return;
-  }
-
   GetViewAccessibility().NotifyEvent(event_type, send_native_event);
 }
 
diff --git a/ui/views/view.h b/ui/views/view.h
index 4e0d42b..3a43bb16 100644
--- a/ui/views/view.h
+++ b/ui/views/view.h
@@ -1505,7 +1505,7 @@
   // Get the object managing the accessibility interface for this View.
   ViewAccessibility& GetViewAccessibility() const;
 
-  // Modifies |node_data| to reflect the current accessible state of this view.
+  // Modifies `node_data` to reflect the current accessible state of this view.
   // It accomplishes this by keeping the data up-to-date in response to the use
   // of the accessible-property setters.
   // NOTE: View authors should use the available property setters rather than
@@ -1515,8 +1515,10 @@
   // parent class. This ensures that if an owning view customizes an accessible
   // property, such as the name, role, or description, that customization is
   // included in your view's `AXNodeData`.
-  virtual void GetAccessibleNodeData(ui::AXNodeData* node_data);
+  virtual void GetAccessibleNodeData(ui::AXNodeData* node_data) {}
 
+  // DEPRECATED: Use `ViewAccessibility::SetName` instead.
+  //
   // Sets/gets the accessible name.
   // The value of the accessible name is a localized, end-user-consumable string
   // which may be derived from visible information (e.g. the text on a button)
@@ -1525,8 +1527,13 @@
   // reader when that object gains focus and is critical to understanding the
   // purpose of that object non-visually.
   void SetAccessibleName(const std::u16string& name);
-  const std::u16string& GetAccessibleName() const;
 
+  // This function is deprecated. Use `ViewAccessibility::GetCachedName`
+  // instead.
+  std::u16string GetAccessibleName() const;
+
+  // DEPRECATED: Use `ViewAccessibility::SetName` instead.
+  //
   // Sets the accessible name to the specified string and source type.
   // To indicate that this view should never have an accessible name, e.g. to
   // prevent screen readers from speaking redundant information, set the type to
@@ -1536,6 +1543,8 @@
   // especially for views which are focusable or otherwise interactive.
   void SetAccessibleName(std::u16string name, ax::mojom::NameFrom name_from);
 
+  // DEPRECATED: Use `ViewAccessibility::SetName` instead.
+  //
   // Sets the accessible name of this view to that of `naming_view`. Often
   // `naming_view` is a `views::Label`, but any view with an accessible name
   // will work.
@@ -1627,6 +1636,16 @@
   void RemoveObserver(ViewObserver* observer);
   bool HasObserver(const ViewObserver* observer) const;
 
+  // Called when the accessible name of the View changed.
+  virtual void OnAccessibleNameChanged(const std::u16string& new_name) {}
+
+  // Called by `SetAccessibleName` to allow subclasses to adjust the new name.
+  // Potential use cases include setting the accessible name to the tooltip
+  // text when the new name is empty and prepending/appending additional text
+  // to the new name.
+  virtual void AdjustAccessibleName(std::u16string& new_name,
+                                    ax::mojom::NameFrom& name_from) {}
+
   // View Controller Interfaces -----------------------------------------------
   // These functions provide a common interface for view controllers to interact
   // with views.
@@ -1688,16 +1707,6 @@
       std::optional<ax::mojom::DescriptionFrom> description_from =
           std::nullopt);
 
-  // Called when the accessible name of the View changed.
-  virtual void OnAccessibleNameChanged(const std::u16string& new_name) {}
-
-  // Called by `SetAccessibleName` to allow subclasses to adjust the new name.
-  // Potential use cases include setting the accessible name to the tooltip
-  // text when the new name is empty and prepending/appending additional text
-  // to the new name.
-  virtual void AdjustAccessibleName(std::u16string& new_name,
-                                    ax::mojom::NameFrom& name_from) {}
-
   // Size and disposition ------------------------------------------------------
 
   // Calculates the preferred size for the View given `available_size`.
@@ -1949,7 +1958,6 @@
   FRIEND_TEST_ALL_PREFIXES(ViewTest, PaintWithMovedViewUsesCache);
   FRIEND_TEST_ALL_PREFIXES(ViewTest, PaintWithMovedViewUsesCacheInRTL);
   FRIEND_TEST_ALL_PREFIXES(ViewTest, PaintWithUnknownInvalidation);
-  FRIEND_TEST_ALL_PREFIXES(ViewTest, PauseAccessibilityEvents);
 
   // Painting  -----------------------------------------------------------------
 
@@ -2493,28 +2501,11 @@
   // Manages the accessibility interface for this View.
   mutable std::unique_ptr<ViewAccessibility> view_accessibility_;
 
-  // Updated by the accessibility property setters and returned by
-  // `GetAccessibleNodeData`.
-  std::unique_ptr<ui::AXNodeData> ax_node_data_;
-
-  // TODO(accessibility): Remove this attribute once we migrate the
-  // SetAccessibilityProperties function to ViewAccessibility.
-  //
-  // Used by `SetAccessibilityProperties` and to prevent accessibility
-  // property-change events from being fired during initialization of this view.
-  bool pause_accessibility_events_ = false;
-
   // Keeps track of whether accessibility checks for this View have run yet.
   // They run once inside ::OnPaint() to keep overhead low. The idea is that if
   // a View is ready to paint it should also be set up to be accessible.
   bool has_run_accessibility_paint_checks_ = false;
 
-  // Accessible properties whose values are set by views using the accessible
-  // property setters, and used to populate the `AXNodeData` associated with
-  // this view and provided by `View::GetAccessibleNodeData`.
-  std::u16string accessible_name_;
-  ax::mojom::Role accessible_role_ = ax::mojom::Role::kUnknown;
-
   // Observers -----------------------------------------------------------------
 
   base::ObserverList<ViewObserver>::Unchecked observers_;
diff --git a/ui/views/view_unittest.cc b/ui/views/view_unittest.cc
index ac177a5b..ffa8b55 100644
--- a/ui/views/view_unittest.cc
+++ b/ui/views/view_unittest.cc
@@ -518,29 +518,29 @@
 TEST_F(ViewTest, PauseAccessibilityEvents) {
   TestView v;
   v.SetAccessibleRole(ax::mojom::Role::kStaticText);
-  EXPECT_EQ(v.pause_accessibility_events_, false);
+  EXPECT_EQ(v.GetViewAccessibility().pause_accessibility_events_, false);
 
   // Setting the accessible name when `pause_accessibility_events_` is false
   // should result in an event being fired.
   v.last_a11y_event_ = ax::mojom::Event::kNone;
   v.SetAccessibleName(u"Name");
   EXPECT_EQ(v.last_a11y_event_, ax::mojom::Event::kTextChanged);
-  EXPECT_EQ(v.pause_accessibility_events_, false);
+  EXPECT_EQ(v.GetViewAccessibility().pause_accessibility_events_, false);
 
   // Setting the accessible name when `pause_accessibility_events_` is true
   // should result in no event being fired.
   v.last_a11y_event_ = ax::mojom::Event::kNone;
-  v.pause_accessibility_events_ = true;
+  v.GetViewAccessibility().pause_accessibility_events_ = true;
   v.SetAccessibleName(u"New Name");
   EXPECT_EQ(v.last_a11y_event_, ax::mojom::Event::kNone);
-  EXPECT_EQ(v.pause_accessibility_events_, true);
+  EXPECT_EQ(v.GetViewAccessibility().pause_accessibility_events_, true);
 
   // A11yTestView views are constructed using `SetAccessibilityProperties`. By
   // default, `pause_accessibility_events_` is false. It is temporarily set to
   // true and then reset at the end of initialization.
   A11yTestView ax_v(ax::mojom::Role::kButton, u"Name", u"Description");
   EXPECT_EQ(ax_v.last_a11y_event_, ax::mojom::Event::kNone);
-  EXPECT_EQ(ax_v.pause_accessibility_events_, false);
+  EXPECT_EQ(ax_v.GetViewAccessibility().pause_accessibility_events_, false);
 }
 
 TEST_F(ViewTest, SetAccessibilityPropertiesRoleNameDescription) {
@@ -851,78 +851,6 @@
             ax::mojom::NameFrom::kAttributeExplicitlyEmpty);
 }
 
-TEST_F(ViewTest, SetAccessibleNameToStringRoleNotInitiallySet) {
-  TestView v;
-  ui::AXNodeData data = ui::AXNodeData();
-  v.GetViewAccessibility().GetAccessibleNodeData(&data);
-  EXPECT_EQ(v.GetAccessibleName(), u"");
-  EXPECT_EQ(data.GetString16Attribute(ax::mojom::StringAttribute::kName), u"");
-
-  v.last_a11y_event_ = ax::mojom::Event::kNone;
-  data = ui::AXNodeData();
-
-  // Setting the name prior to setting the role violates an expectation of
-  // `AXNodeData::SetName`. `View::SetAccessibleName` handles that case by
-  // setting the property but not adding it to `ax_node_data_` until a role
-  // has been set.
-  v.SetAccessibleName(u"Name");
-  v.GetViewAccessibility().GetAccessibleNodeData(&data);
-  EXPECT_EQ(v.GetAccessibleName(), u"Name");
-  EXPECT_EQ(data.GetString16Attribute(ax::mojom::StringAttribute::kName), u"");
-  EXPECT_EQ(v.last_a11y_event_, ax::mojom::Event::kTextChanged);
-
-  v.last_a11y_event_ = ax::mojom::Event::kNone;
-  data = ui::AXNodeData();
-
-  // Setting the role to a valid role should add the previously-set name to
-  // ax_node_data_. Note there is currently no role-changed accessibility event.
-  v.SetAccessibleRole(ax::mojom::Role::kButton);
-  v.GetViewAccessibility().GetAccessibleNodeData(&data);
-  EXPECT_EQ(v.GetAccessibleName(), u"Name");
-  EXPECT_EQ(data.GetString16Attribute(ax::mojom::StringAttribute::kName),
-            u"Name");
-  EXPECT_EQ(v.last_a11y_event_, ax::mojom::Event::kNone);
-
-  v.last_a11y_event_ = ax::mojom::Event::kNone;
-  data = ui::AXNodeData();
-}
-
-TEST_F(ViewTest, SetAccessibleNameToLabelRoleNotInitiallySet) {
-  TestView label;
-  label.SetAccessibleName(u"Label's Name");
-
-  TestView v;
-  ui::AXNodeData data = ui::AXNodeData();
-  v.GetViewAccessibility().GetAccessibleNodeData(&data);
-  EXPECT_EQ(v.GetAccessibleName(), u"");
-  EXPECT_EQ(data.GetString16Attribute(ax::mojom::StringAttribute::kName), u"");
-
-  v.last_a11y_event_ = ax::mojom::Event::kNone;
-  data = ui::AXNodeData();
-
-  // Setting the name prior to setting the role violates an expectation of
-  // `AXNodeData::SetName`. `View::SetAccessibleName` handles that case by
-  // setting the property but not adding it to `ax_node_data_` until a role
-  // has been set.
-  v.SetAccessibleName(&label);
-  v.GetViewAccessibility().GetAccessibleNodeData(&data);
-  EXPECT_EQ(v.GetAccessibleName(), u"Label's Name");
-  EXPECT_EQ(data.GetString16Attribute(ax::mojom::StringAttribute::kName), u"");
-  EXPECT_EQ(v.last_a11y_event_, ax::mojom::Event::kTextChanged);
-
-  v.last_a11y_event_ = ax::mojom::Event::kNone;
-  data = ui::AXNodeData();
-
-  // Setting the role to a valid role should add the previously-set name to
-  // ax_node_data_. Note there is currently no role-changed accessibility event.
-  v.SetAccessibleRole(ax::mojom::Role::kButton);
-  v.GetViewAccessibility().GetAccessibleNodeData(&data);
-  EXPECT_EQ(v.GetAccessibleName(), u"Label's Name");
-  EXPECT_EQ(data.GetString16Attribute(ax::mojom::StringAttribute::kName),
-            u"Label's Name");
-  EXPECT_EQ(v.last_a11y_event_, ax::mojom::Event::kNone);
-}
-
 TEST_F(ViewTest, SetAccessibleDescriptionToString) {
   TestView v;
   ui::AXNodeData data = ui::AXNodeData();
diff --git a/ui/webui/resources/tools/codemods/lit_migration_templates.mjs b/ui/webui/resources/tools/codemods/lit_migration_templates.mjs
index 8a03d5f..712ac84 100644
--- a/ui/webui/resources/tools/codemods/lit_migration_templates.mjs
+++ b/ui/webui/resources/tools/codemods/lit_migration_templates.mjs
@@ -25,18 +25,18 @@
 `;
 
 const LISTENER_BINDING_REGEX =
-    /on-(?<eventName>[a-zA-Z-]+)="(?<listenerName>[a-zA-Z_]+)"/g;
+    /on-(?<eventName>[a-zA-Z-]+)="(?<listenerName>[a-zA-Z0-9_]+)"/g;
 
 // Regular expression to parse 2-way bindings like value="{{myValue_}}",
 // and extract 'value' and 'myValue_' into captured groups for further
 // processing.
 const LISTENER_BIDNING_TWO_WAY_REGEX =
-    /(?<childProp>[a-z-]+)="\{\{(?<parentProp>[a-zA-Z_]+)\}\}"/g;
+    /(?<childProp>[a-z-]+)="\{\{(?<parentProp>[a-zA-Z0-9_]+)\}\}"/g;
 
 // Regular expression to extract any "${this.foo}" ocurrences in the HTML
 // template, referring to TS methods or member variables.
 const TS_REFERENCE_REGEX =
-    /"\$\{this\.(?<reference>[a-zA-Z_]+)\}"/g;
+    /"\$\{this\.(?<reference>[a-zA-Z0-9_]+)\}"/g;
 
 // Replaces part of a string with a the provided replacement string.
 function replaceRange(string, start, end, replacement) {
diff --git a/v8 b/v8
index 54fbb5f..ea0402b 160000
--- a/v8
+++ b/v8
@@ -1 +1 @@
-Subproject commit 54fbb5fc84ec83a32b25e6746db942e0aaca02af
+Subproject commit ea0402bfac5428fbdb96f0ac19ac1dbbb4589b8f